From cf49d6b080bb685304c63379a3e101dc7df4ab14 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 15:50:58 -0600 Subject: [PATCH 0001/1096] Add the interpreter from my rustc branch and hook it up to CompileController. --- .gitignore | 1 + Cargo.lock | 4 ++ Cargo.toml | 7 +++ src/interpreter.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 31 +++++++++++++ test/add.rs | 12 +++++ 6 files changed, 164 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/interpreter.rs create mode 100644 src/main.rs create mode 100644 test/add.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..ea8c4bf7f35f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000..b86c754ccdf18 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "miri" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000..8963f7a6ab8d8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "miri" +version = "0.1.0" +authors = ["Scott Olson "] +description = "An experimental interpreter for Rust MIR." +repository = "https://github.com/tsion/miri" +license = "ISC" diff --git a/src/interpreter.rs b/src/interpreter.rs new file mode 100644 index 0000000000000..2382a5d30774a --- /dev/null +++ b/src/interpreter.rs @@ -0,0 +1,109 @@ +use rustc::front; +use rustc::middle::ty; +use rustc_mir::repr::{self as mir, Mir}; +use rustc_mir::mir_map::MirMap; +use syntax::attr::AttrMetaMethods; + +#[derive(Clone, Debug)] +enum Value { + Uninit, + Int(i64), +} + +struct Interpreter<'tcx> { + mir: &'tcx Mir<'tcx>, + var_vals: Vec, + temp_vals: Vec, + result: Value, +} + +impl<'tcx> Interpreter<'tcx> { + fn new(mir: &'tcx Mir<'tcx>) -> Self { + Interpreter { + mir: mir, + var_vals: vec![Value::Uninit; mir.var_decls.len()], + temp_vals: vec![Value::Uninit; mir.temp_decls.len()], + result: Value::Uninit, + } + } + + fn run(&mut self) { + let start_block = self.mir.basic_block_data(mir::START_BLOCK); + + for stmt in &start_block.statements { + use rustc_mir::repr::Lvalue::*; + use rustc_mir::repr::StatementKind::*; + + println!(" {:?}", stmt); + match stmt.kind { + Assign(ref lv, ref rv) => { + let val = self.eval_rvalue(rv); + + let spot = match *lv { + Var(i) => &mut self.var_vals[i as usize], + Temp(i) => &mut self.temp_vals[i as usize], + ReturnPointer => &mut self.result, + _ => unimplemented!(), + }; + + *spot = val; + } + Drop(_kind, ref _lv) => { /* TODO */ }, + } + } + + println!(" {:?}", start_block.terminator); + println!("=> {:?}", self.result); + } + + fn eval_rvalue(&mut self, rv: &mir::Rvalue) -> Value { + use rustc_mir::repr::Rvalue::*; + + match *rv { + Use(ref op) => self.eval_operand(op), + BinaryOp(mir::BinOp::Add, ref left, ref right) => { + let left_val = self.eval_operand(left); + let right_val = self.eval_operand(right); + match (left_val, right_val) { + (Value::Int(l), Value::Int(r)) => Value::Int(l + r), + _ => unimplemented!(), + } + } + _ => unimplemented!(), + } + } + + fn eval_operand(&mut self, op: &mir::Operand) -> Value { + use rustc::middle::const_eval::ConstVal::*; + use rustc_mir::repr::Lvalue::*; + use rustc_mir::repr::Operand::*; + + match *op { + Consume(Var(i)) => self.var_vals[i as usize].clone(), + Consume(Temp(i)) => self.temp_vals[i as usize].clone(), + Constant(ref constant) => { + match constant.literal { + mir::Literal::Value { value: Int(n) } => Value::Int(n), + _ => unimplemented!(), + } + } + _ => unimplemented!(), + } + } +} + +pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) { + for (&id, mir) in mir_map { + for attr in tcx.map.attrs(id) { + if attr.check_name("miri_run") { + let item = match tcx.map.get(id) { + front::map::NodeItem(item) => item, + _ => panic!(), + }; + println!("Interpreting: {}", item.name); + let mut interpreter = Interpreter::new(mir); + interpreter.run(); + } + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000000..808b43e9643fa --- /dev/null +++ b/src/main.rs @@ -0,0 +1,31 @@ +#![feature(rustc_private)] + +extern crate rustc; +extern crate rustc_driver; +extern crate rustc_mir; +extern crate syntax; + +mod interpreter; + +use rustc::session::Session; +use rustc_driver::{driver, CompilerCalls, Compilation}; + +struct MiriCompilerCalls; + +impl<'a> CompilerCalls<'a> for MiriCompilerCalls { + fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { + let mut control = driver::CompileController::basic(); + control.after_analysis.stop = Compilation::Stop; + + control.after_analysis.callback = Box::new(|state| { + interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + }); + + control + } +} + +fn main() { + let args: Vec = std::env::args().collect(); + rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); +} diff --git a/test/add.rs b/test/add.rs new file mode 100644 index 0000000000000..33b3b533f55c7 --- /dev/null +++ b/test/add.rs @@ -0,0 +1,12 @@ +#![feature(custom_attribute, rustc_attrs)] +#![allow(dead_code, unused_attributes)] + +#[rustc_mir] +#[miri_run] +fn foo() -> i32 { + let x = 1; + let y = 2; + x + y +} + +fn main() {} From fbec376eedd5f6ac39ae629d9ffcd216ff37e0a6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 16:13:05 -0600 Subject: [PATCH 0002/1096] Add more tests. Remove unecessary attributes. --- test/add.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/test/add.rs b/test/add.rs index 33b3b533f55c7..77d0582daddc6 100644 --- a/test/add.rs +++ b/test/add.rs @@ -1,9 +1,18 @@ -#![feature(custom_attribute, rustc_attrs)] +#![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -#[rustc_mir] -#[miri_run] -fn foo() -> i32 { +#[miri_run(expected = "Int(1)")] +fn ret() -> i32 { + 1 +} + +#[miri_run(expected = "Int(3)")] +fn add() -> i32 { + 1 + 2 +} + +#[miri_run(expected = "Int(3)")] +fn indirect_add() -> i32 { let x = 1; let y = 2; x + y From 4e80530bdb78614b12d8a5fd3c2ec316ed82e5cd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 16:13:22 -0600 Subject: [PATCH 0003/1096] Sort uses. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2382a5d30774a..c28a5ff4bef80 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,7 +1,7 @@ use rustc::front; use rustc::middle::ty; -use rustc_mir::repr::{self as mir, Mir}; use rustc_mir::mir_map::MirMap; +use rustc_mir::repr::{self as mir, Mir}; use syntax::attr::AttrMetaMethods; #[derive(Clone, Debug)] From 74537614056f276e74903baa03d1d06985ac3b61 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 16:13:35 -0600 Subject: [PATCH 0004/1096] Implement all binary operations on ints. --- src/interpreter.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c28a5ff4bef80..63a5ae965383a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -7,6 +7,7 @@ use syntax::attr::AttrMetaMethods; #[derive(Clone, Debug)] enum Value { Uninit, + Bool(bool), Int(i64), } @@ -56,16 +57,32 @@ impl<'tcx> Interpreter<'tcx> { println!("=> {:?}", self.result); } - fn eval_rvalue(&mut self, rv: &mir::Rvalue) -> Value { + fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { use rustc_mir::repr::Rvalue::*; + use rustc_mir::repr::BinOp::*; - match *rv { - Use(ref op) => self.eval_operand(op), - BinaryOp(mir::BinOp::Add, ref left, ref right) => { - let left_val = self.eval_operand(left); - let right_val = self.eval_operand(right); - match (left_val, right_val) { - (Value::Int(l), Value::Int(r)) => Value::Int(l + r), + match *rvalue { + Use(ref operand) => self.eval_operand(operand), + BinaryOp(bin_op, ref left, ref right) => { + match (self.eval_operand(left), self.eval_operand(right)) { + (Value::Int(l), Value::Int(r)) => match bin_op { + Add => Value::Int(l + r), + Sub => Value::Int(l - r), + Mul => Value::Int(l * r), + Div => Value::Int(l / r), + Rem => Value::Int(l % r), + BitXor => Value::Int(l ^ r), + BitAnd => Value::Int(l & r), + BitOr => Value::Int(l | r), + Shl => Value::Int(l << r), + Shr => Value::Int(l >> r), + Eq => Value::Bool(l == r), + Lt => Value::Bool(l < r), + Le => Value::Bool(l <= r), + Ne => Value::Bool(l != r), + Ge => Value::Bool(l >= r), + Gt => Value::Bool(l > r), + }, _ => unimplemented!(), } } From b099391aaf65b83385a92cced4eed8d7f02d94c2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 17:11:41 -0600 Subject: [PATCH 0005/1096] Check actual vs. expected values. --- src/interpreter.rs | 41 +++++++++++++++++++++++++++++---------- test/{add.rs => basic.rs} | 0 2 files changed, 31 insertions(+), 10 deletions(-) rename test/{add.rs => basic.rs} (100%) diff --git a/src/interpreter.rs b/src/interpreter.rs index 63a5ae965383a..52f9560dd6d2c 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,7 +1,7 @@ -use rustc::front; use rustc::middle::ty; use rustc_mir::mir_map::MirMap; use rustc_mir::repr::{self as mir, Mir}; +use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; #[derive(Clone, Debug)] @@ -28,14 +28,13 @@ impl<'tcx> Interpreter<'tcx> { } } - fn run(&mut self) { + fn run(&mut self) -> Value { let start_block = self.mir.basic_block_data(mir::START_BLOCK); for stmt in &start_block.statements { use rustc_mir::repr::Lvalue::*; use rustc_mir::repr::StatementKind::*; - println!(" {:?}", stmt); match stmt.kind { Assign(ref lv, ref rv) => { let val = self.eval_rvalue(rv); @@ -53,8 +52,7 @@ impl<'tcx> Interpreter<'tcx> { } } - println!(" {:?}", start_block.terminator); - println!("=> {:?}", self.result); + self.result.clone() } fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { @@ -113,14 +111,37 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> for (&id, mir) in mir_map { for attr in tcx.map.attrs(id) { if attr.check_name("miri_run") { - let item = match tcx.map.get(id) { - front::map::NodeItem(item) => item, - _ => panic!(), - }; + let item = tcx.map.expect_item(id); + println!("Interpreting: {}", item.name); let mut interpreter = Interpreter::new(mir); - interpreter.run(); + let val = interpreter.run(); + let val_str = format!("{:?}", val); + + if !check_expected(&val_str, attr) { + println!("=> {}\n", val_str); + } + } + } + } +} + +fn check_expected(actual: &str, attr: &Attribute) -> bool { + if let Some(meta_items) = attr.meta_item_list() { + for meta_item in meta_items { + if meta_item.check_name("expected") { + let expected = meta_item.value_str().unwrap(); + + if actual == &expected[..] { + println!("Test passed!\n"); + } else { + println!("Actual value:\t{}\nExpected value:\t{}\n", actual, expected); + } + + return true; } } } + + false } diff --git a/test/add.rs b/test/basic.rs similarity index 100% rename from test/add.rs rename to test/basic.rs From 71f70e95ed1c4b38b3e5994a6672eb7b42f36f2f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 17:24:43 -0600 Subject: [PATCH 0006/1096] Implement unary operators for integers. --- src/interpreter.rs | 51 +++++++++++++++++++++++++++++----------------- test/basic.rs | 5 +++++ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 52f9560dd6d2c..b42e4235cb8ce 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -8,7 +8,7 @@ use syntax::attr::AttrMetaMethods; enum Value { Uninit, Bool(bool), - Int(i64), + Int(i64), // FIXME: Should be bit-width aware. } struct Interpreter<'tcx> { @@ -58,32 +58,45 @@ impl<'tcx> Interpreter<'tcx> { fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { use rustc_mir::repr::Rvalue::*; use rustc_mir::repr::BinOp::*; + use rustc_mir::repr::UnOp::*; match *rvalue { Use(ref operand) => self.eval_operand(operand), + BinaryOp(bin_op, ref left, ref right) => { match (self.eval_operand(left), self.eval_operand(right)) { - (Value::Int(l), Value::Int(r)) => match bin_op { - Add => Value::Int(l + r), - Sub => Value::Int(l - r), - Mul => Value::Int(l * r), - Div => Value::Int(l / r), - Rem => Value::Int(l % r), - BitXor => Value::Int(l ^ r), - BitAnd => Value::Int(l & r), - BitOr => Value::Int(l | r), - Shl => Value::Int(l << r), - Shr => Value::Int(l >> r), - Eq => Value::Bool(l == r), - Lt => Value::Bool(l < r), - Le => Value::Bool(l <= r), - Ne => Value::Bool(l != r), - Ge => Value::Bool(l >= r), - Gt => Value::Bool(l > r), - }, + (Value::Int(l), Value::Int(r)) => { + match bin_op { + Add => Value::Int(l + r), + Sub => Value::Int(l - r), + Mul => Value::Int(l * r), + Div => Value::Int(l / r), + Rem => Value::Int(l % r), + BitXor => Value::Int(l ^ r), + BitAnd => Value::Int(l & r), + BitOr => Value::Int(l | r), + Shl => Value::Int(l << r), + Shr => Value::Int(l >> r), + Eq => Value::Bool(l == r), + Lt => Value::Bool(l < r), + Le => Value::Bool(l <= r), + Ne => Value::Bool(l != r), + Ge => Value::Bool(l >= r), + Gt => Value::Bool(l > r), + } + } + _ => unimplemented!(), + } + } + + UnaryOp(un_op, ref operand) => { + match (un_op, self.eval_operand(operand)) { + (Not, Value::Int(n)) => Value::Int(!n), + (Neg, Value::Int(n)) => Value::Int(-n), _ => unimplemented!(), } } + _ => unimplemented!(), } } diff --git a/test/basic.rs b/test/basic.rs index 77d0582daddc6..9eb5851a14e52 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -6,6 +6,11 @@ fn ret() -> i32 { 1 } +#[miri_run(expected = "Int(-1)")] +fn neg() -> i32 { + -1 +} + #[miri_run(expected = "Int(3)")] fn add() -> i32 { 1 + 2 From 694facf3958881e2845ac453f75c6fc387dad091 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 17:44:29 -0600 Subject: [PATCH 0007/1096] Factor out constant evaluation. --- src/interpreter.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b42e4235cb8ce..bf0fe3c17185f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,4 @@ -use rustc::middle::ty; +use rustc::middle::{const_eval, ty}; use rustc_mir::mir_map::MirMap; use rustc_mir::repr::{self as mir, Mir}; use syntax::ast::Attribute; @@ -101,8 +101,7 @@ impl<'tcx> Interpreter<'tcx> { } } - fn eval_operand(&mut self, op: &mir::Operand) -> Value { - use rustc::middle::const_eval::ConstVal::*; + fn eval_operand(&self, op: &mir::Operand) -> Value { use rustc_mir::repr::Lvalue::*; use rustc_mir::repr::Operand::*; @@ -111,13 +110,29 @@ impl<'tcx> Interpreter<'tcx> { Consume(Temp(i)) => self.temp_vals[i as usize].clone(), Constant(ref constant) => { match constant.literal { - mir::Literal::Value { value: Int(n) } => Value::Int(n), - _ => unimplemented!(), + mir::Literal::Value { value: ref const_val } => self.eval_constant(const_val), + mir::Literal::Item { .. } => unimplemented!(), } } _ => unimplemented!(), } } + + fn eval_constant(&self, const_val: &const_eval::ConstVal) -> Value { + use rustc::middle::const_eval::ConstVal::*; + + match *const_val { + Float(_f) => unimplemented!(), + Int(i) => Value::Int(i), + Uint(_u) => unimplemented!(), + Str(ref _s) => unimplemented!(), + ByteStr(ref _bs) => unimplemented!(), + Bool(_b) => unimplemented!(), + Struct(_node_id) => unimplemented!(), + Tuple(_node_id) => unimplemented!(), + Function(_def_id) => unimplemented!(), + } + } } pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) { From 3f0eac2c7848a95e84f965e1225e2110da7ae0fa Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 12 Nov 2015 18:00:22 -0600 Subject: [PATCH 0008/1096] Add a more complicated arithmetic test. --- test/basic.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/basic.rs b/test/basic.rs index 9eb5851a14e52..24ef19b3fdb0f 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -23,4 +23,9 @@ fn indirect_add() -> i32 { x + y } +#[miri_run(expected = "Int(25)")] +fn arith() -> i32 { + 3*3 + 4*4 +} + fn main() {} From fa1c04f19419a4239d7f427c47e829625c18ef7b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 14 Nov 2015 01:19:07 -0600 Subject: [PATCH 0009/1096] Factor out lvalue evaluation and use a single value stack. --- src/interpreter.rs | 76 ++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bf0fe3c17185f..987a74b9b6348 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -4,6 +4,8 @@ use rustc_mir::repr::{self as mir, Mir}; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; +use std::iter; + #[derive(Clone, Debug)] enum Value { Uninit, @@ -11,48 +13,58 @@ enum Value { Int(i64), // FIXME: Should be bit-width aware. } -struct Interpreter<'tcx> { - mir: &'tcx Mir<'tcx>, - var_vals: Vec, - temp_vals: Vec, - result: Value, +struct Interpreter { + stack: Vec, + num_vars: usize, + num_temps: usize, } -impl<'tcx> Interpreter<'tcx> { - fn new(mir: &'tcx Mir<'tcx>) -> Self { +impl Interpreter { + fn new() -> Self { Interpreter { - mir: mir, - var_vals: vec![Value::Uninit; mir.var_decls.len()], - temp_vals: vec![Value::Uninit; mir.temp_decls.len()], - result: Value::Uninit, + stack: Vec::new(), + num_vars: 0, + num_temps: 0, } } - fn run(&mut self) -> Value { - let start_block = self.mir.basic_block_data(mir::START_BLOCK); + fn run(&mut self, mir: &Mir) -> Value { + let start_block = mir.basic_block_data(mir::START_BLOCK); + + self.num_vars = mir.var_decls.len(); + self.num_temps = mir.temp_decls.len(); + + self.stack.extend( + iter::repeat(Value::Uninit).take(1 + self.num_vars + self.num_temps)); for stmt in &start_block.statements { - use rustc_mir::repr::Lvalue::*; use rustc_mir::repr::StatementKind::*; match stmt.kind { - Assign(ref lv, ref rv) => { - let val = self.eval_rvalue(rv); - - let spot = match *lv { - Var(i) => &mut self.var_vals[i as usize], - Temp(i) => &mut self.temp_vals[i as usize], - ReturnPointer => &mut self.result, - _ => unimplemented!(), - }; - - *spot = val; + Assign(ref lvalue, ref rvalue) => { + let index = self.eval_lvalue(lvalue); + let value = self.eval_rvalue(rvalue); + self.stack[index] = value; } - Drop(_kind, ref _lv) => { /* TODO */ }, + + Drop(_kind, ref _lv) => { + // TODO + }, } } - self.result.clone() + self.stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone() + } + + fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> usize { + use rustc_mir::repr::Lvalue::*; + + match *lvalue { + Var(i) => 1 + i as usize, + Temp(i) => 1 + self.num_vars + i as usize, + ReturnPointer => 0, + _ => unimplemented!(), + } } fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { @@ -102,19 +114,17 @@ impl<'tcx> Interpreter<'tcx> { } fn eval_operand(&self, op: &mir::Operand) -> Value { - use rustc_mir::repr::Lvalue::*; use rustc_mir::repr::Operand::*; match *op { - Consume(Var(i)) => self.var_vals[i as usize].clone(), - Consume(Temp(i)) => self.temp_vals[i as usize].clone(), + Consume(ref lvalue) => self.stack[self.eval_lvalue(lvalue)].clone(), + Constant(ref constant) => { match constant.literal { mir::Literal::Value { value: ref const_val } => self.eval_constant(const_val), mir::Literal::Item { .. } => unimplemented!(), } } - _ => unimplemented!(), } } @@ -142,8 +152,8 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> let item = tcx.map.expect_item(id); println!("Interpreting: {}", item.name); - let mut interpreter = Interpreter::new(mir); - let val = interpreter.run(); + let mut interpreter = Interpreter::new(); + let val = interpreter.run(mir); let val_str = format!("{:?}", val); if !check_expected(&val_str, attr) { From c37b2bba055cabca0591f4e8ebbe7168c273ea14 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 16 Nov 2015 15:22:27 -0600 Subject: [PATCH 0010/1096] Add call frames to track offsets of values in the value stack. --- src/interpreter.rs | 54 ++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 987a74b9b6348..3da416de8148f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -13,29 +13,42 @@ enum Value { Int(i64), // FIXME: Should be bit-width aware. } -struct Interpreter { - stack: Vec, +#[derive(Debug)] +struct Frame { + offset: usize, + num_args: usize, num_vars: usize, num_temps: usize, } +struct Interpreter { + value_stack: Vec, + call_stack: Vec, +} + impl Interpreter { fn new() -> Self { Interpreter { - stack: Vec::new(), - num_vars: 0, - num_temps: 0, + value_stack: Vec::new(), + call_stack: Vec::new(), } } - fn run(&mut self, mir: &Mir) -> Value { - let start_block = mir.basic_block_data(mir::START_BLOCK); - - self.num_vars = mir.var_decls.len(); - self.num_temps = mir.temp_decls.len(); + fn call(&mut self, mir: &Mir, _args: &[Value]) -> Value { + self.call_stack.push(Frame { + offset: self.value_stack.len(), + num_args: mir.arg_decls.len(), + num_vars: mir.var_decls.len(), + num_temps: mir.temp_decls.len(), + }); + + { + let frame = self.call_stack.last().unwrap(); + let frame_size = 1 + frame.num_args + frame.num_vars + frame.num_temps; + self.value_stack.extend(iter::repeat(Value::Uninit).take(frame_size)); + } - self.stack.extend( - iter::repeat(Value::Uninit).take(1 + self.num_vars + self.num_temps)); + let start_block = mir.basic_block_data(mir::START_BLOCK); for stmt in &start_block.statements { use rustc_mir::repr::StatementKind::*; @@ -44,7 +57,7 @@ impl Interpreter { Assign(ref lvalue, ref rvalue) => { let index = self.eval_lvalue(lvalue); let value = self.eval_rvalue(rvalue); - self.stack[index] = value; + self.value_stack[index] = value; } Drop(_kind, ref _lv) => { @@ -53,16 +66,19 @@ impl Interpreter { } } - self.stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone() + self.value_stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone() } fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> usize { use rustc_mir::repr::Lvalue::*; + let frame = self.call_stack.last().expect("missing call frame"); + match *lvalue { - Var(i) => 1 + i as usize, - Temp(i) => 1 + self.num_vars + i as usize, - ReturnPointer => 0, + ReturnPointer => frame.offset, + Arg(i) => frame.offset + 1 + i as usize, + Var(i) => frame.offset + 1 + frame.num_args + i as usize, + Temp(i) => frame.offset + 1 + frame.num_args + frame.num_vars + i as usize, _ => unimplemented!(), } } @@ -117,7 +133,7 @@ impl Interpreter { use rustc_mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.stack[self.eval_lvalue(lvalue)].clone(), + Consume(ref lvalue) => self.value_stack[self.eval_lvalue(lvalue)].clone(), Constant(ref constant) => { match constant.literal { @@ -153,7 +169,7 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> println!("Interpreting: {}", item.name); let mut interpreter = Interpreter::new(); - let val = interpreter.run(mir); + let val = interpreter.call(mir, &[]); let val_str = format!("{:?}", val); if !check_expected(&val_str, attr) { From 7112fc8cd1d42bc660f60cbae7beb86cfa81936c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 19 Nov 2015 03:23:50 -0600 Subject: [PATCH 0011/1096] Handle Goto, Panic, and If terminators properly. --- src/interpreter.rs | 62 +++++++++++++++++++++++++++++++++------------- test/basic.rs | 10 ++++++++ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3da416de8148f..47b1ab252a422 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -34,7 +34,7 @@ impl Interpreter { } } - fn call(&mut self, mir: &Mir, _args: &[Value]) -> Value { + fn push_stack_frame(&mut self, mir: &Mir, _args: &[Value]) { self.call_stack.push(Frame { offset: self.value_stack.len(), num_args: mir.arg_decls.len(), @@ -42,27 +42,55 @@ impl Interpreter { num_temps: mir.temp_decls.len(), }); - { - let frame = self.call_stack.last().unwrap(); - let frame_size = 1 + frame.num_args + frame.num_vars + frame.num_temps; - self.value_stack.extend(iter::repeat(Value::Uninit).take(frame_size)); - } + let frame = self.call_stack.last().unwrap(); + let frame_size = 1 + frame.num_args + frame.num_vars + frame.num_temps; + self.value_stack.extend(iter::repeat(Value::Uninit).take(frame_size)); + + // TODO(tsion): Write args into value_stack. + } + + fn call(&mut self, mir: &Mir, args: &[Value]) -> Value { + self.push_stack_frame(mir, args); + let mut block = mir::START_BLOCK; + + loop { + use rustc_mir::repr::Terminator::*; + + let block_data = mir.basic_block_data(block); + + for stmt in &block_data.statements { + use rustc_mir::repr::StatementKind::*; - let start_block = mir.basic_block_data(mir::START_BLOCK); + match stmt.kind { + Assign(ref lvalue, ref rvalue) => { + let index = self.eval_lvalue(lvalue); + let value = self.eval_rvalue(rvalue); + self.value_stack[index] = value; + } + + Drop(_kind, ref _lv) => { + // TODO + }, + } + } - for stmt in &start_block.statements { - use rustc_mir::repr::StatementKind::*; + println!("{:?}", block_data.terminator); + match block_data.terminator { + Goto { target } => block = target, - match stmt.kind { - Assign(ref lvalue, ref rvalue) => { - let index = self.eval_lvalue(lvalue); - let value = self.eval_rvalue(rvalue); - self.value_stack[index] = value; + Panic { target: _target } => unimplemented!(), + + If { ref cond, targets } => { + match self.eval_operand(&cond) { + Value::Bool(true) => block = targets[0], + Value::Bool(false) => block = targets[1], + cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), + } } - Drop(_kind, ref _lv) => { - // TODO - }, + Return => break, + + _ => unimplemented!(), } } diff --git a/test/basic.rs b/test/basic.rs index 24ef19b3fdb0f..ceb82466ea69c 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -28,4 +28,14 @@ fn arith() -> i32 { 3*3 + 4*4 } +#[miri_run(expected = "Int(0)")] +fn if_false() -> i32 { + if false { 1 } else { 0 } +} + +#[miri_run(expected = "Int(1)")] +fn if_true() -> i32 { + if true { 1 } else { 0 } +} + fn main() {} From 7ce6a250d4d5d7ceb8835935ad922b2146bcdaac Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 19 Nov 2015 07:07:47 -0600 Subject: [PATCH 0012/1096] Implement function calls. --- src/interpreter.rs | 75 +++++++++++++++++++++++++++++++++++----------- test/basic.rs | 9 ++++++ 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 47b1ab252a422..596f539060fc8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,4 @@ -use rustc::middle::{const_eval, ty}; +use rustc::middle::{const_eval, def_id, ty}; use rustc_mir::mir_map::MirMap; use rustc_mir::repr::{self as mir, Mir}; use syntax::ast::Attribute; @@ -11,6 +11,7 @@ enum Value { Uninit, Bool(bool), Int(i64), // FIXME: Should be bit-width aware. + Func(def_id::DefId), } #[derive(Debug)] @@ -21,20 +22,30 @@ struct Frame { num_temps: usize, } -struct Interpreter { +impl Frame { + fn size(&self) -> usize { + 1 + self.num_args + self.num_vars + self.num_temps + } +} + +struct Interpreter<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + mir_map: &'a MirMap<'tcx>, value_stack: Vec, call_stack: Vec, } -impl Interpreter { - fn new() -> Self { +impl<'a, 'tcx> Interpreter<'a, 'tcx> { + fn new(tcx: &'a ty::ctxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { + tcx: tcx, + mir_map: mir_map, value_stack: Vec::new(), call_stack: Vec::new(), } } - fn push_stack_frame(&mut self, mir: &Mir, _args: &[Value]) { + fn push_stack_frame(&mut self, mir: &Mir, args: &[Value]) { self.call_stack.push(Frame { offset: self.value_stack.len(), num_args: mir.arg_decls.len(), @@ -43,10 +54,16 @@ impl Interpreter { }); let frame = self.call_stack.last().unwrap(); - let frame_size = 1 + frame.num_args + frame.num_vars + frame.num_temps; - self.value_stack.extend(iter::repeat(Value::Uninit).take(frame_size)); + self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); + + for (i, arg) in args.iter().enumerate() { + self.value_stack[frame.offset + 1 + i] = arg.clone(); + } + } - // TODO(tsion): Write args into value_stack. + fn pop_stack_frame(&mut self) { + let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none"); + self.value_stack.truncate(frame.offset); } fn call(&mut self, mir: &Mir, args: &[Value]) -> Value { @@ -74,27 +91,46 @@ impl Interpreter { } } - println!("{:?}", block_data.terminator); match block_data.terminator { + Return => break, Goto { target } => block = target, - Panic { target: _target } => unimplemented!(), + Call { data: mir::CallData { ref destination, ref func, ref args }, targets } => { + let index = self.eval_lvalue(destination); + let func_val = self.eval_operand(func); + + if let Value::Func(def_id) = func_val { + let node_id = self.tcx.map.as_local_node_id(def_id).unwrap(); + let mir = &self.mir_map[&node_id]; + let arg_vals: Vec = + args.iter().map(|arg| self.eval_operand(arg)).collect(); + + self.value_stack[index] = self.call(mir, &arg_vals); + block = targets[0]; + } else { + panic!("tried to call a non-function value: {:?}", func_val); + } + } If { ref cond, targets } => { - match self.eval_operand(&cond) { + match self.eval_operand(cond) { Value::Bool(true) => block = targets[0], Value::Bool(false) => block = targets[1], cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), } } - Return => break, - _ => unimplemented!(), + // Diverge => unimplemented!(), + // Panic { target } => unimplemented!(), + // Switch { ref discr, adt_def, ref targets } => unimplemented!(), + // SwitchInt { ref discr, switch_ty, ref values, ref targets } => unimplemented!(), } } - self.value_stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone() + let ret_val = self.value_stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone(); + self.pop_stack_frame(); + ret_val } fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> usize { @@ -157,7 +193,7 @@ impl Interpreter { } } - fn eval_operand(&self, op: &mir::Operand) -> Value { + fn eval_operand(&mut self, op: &mir::Operand) -> Value { use rustc_mir::repr::Operand::*; match *op { @@ -165,8 +201,11 @@ impl Interpreter { Constant(ref constant) => { match constant.literal { - mir::Literal::Value { value: ref const_val } => self.eval_constant(const_val), - mir::Literal::Item { .. } => unimplemented!(), + mir::Literal::Value { ref value } => self.eval_constant(value), + + mir::Literal::Item { def_id, substs: _ } => { + Value::Func(def_id) + } } } } @@ -196,7 +235,7 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> let item = tcx.map.expect_item(id); println!("Interpreting: {}", item.name); - let mut interpreter = Interpreter::new(); + let mut interpreter = Interpreter::new(tcx, mir_map); let val = interpreter.call(mir, &[]); let val_str = format!("{:?}", val); diff --git a/test/basic.rs b/test/basic.rs index ceb82466ea69c..2da62bfa6226d 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -38,4 +38,13 @@ fn if_true() -> i32 { if true { 1 } else { 0 } } +#[miri_run(expected = "Int(2)")] +fn call() -> i32 { + fn increment(x: i32) -> i32 { + x + 1 + } + + increment(1) +} + fn main() {} From 4c8a6b64de024dc4ab6eaf2a97aad4978967f59f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 19 Nov 2015 07:10:17 -0600 Subject: [PATCH 0013/1096] Test looping and recursive factorial. --- test/basic.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/basic.rs b/test/basic.rs index 2da62bfa6226d..99a3fb39ad2ca 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -47,4 +47,30 @@ fn call() -> i32 { increment(1) } +#[miri_run(expected = "Int(3628800)")] +fn factorial_loop() -> i32 { + let mut product = 1; + let mut i = 1; + + while i <= 10 { + product *= i; + i += 1; + } + + product +} + +#[miri_run(expected = "Int(3628800)")] +fn factorial_recursive() -> i32 { + fn fact(n: i32) -> i32 { + if n == 0 { + 1 + } else { + n * fact(n - 1) + } + } + + fact(10) +} + fn main() {} From c7244afea1bd6254f9417b47a2afaaeee15eee1a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 19 Nov 2015 16:49:13 -0600 Subject: [PATCH 0014/1096] Implement SwitchInt (for some match expressions). --- src/interpreter.rs | 16 ++++++++++++---- test/basic.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 596f539060fc8..6f2f604d84ec0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,7 +6,7 @@ use syntax::attr::AttrMetaMethods; use std::iter; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] enum Value { Uninit, Bool(bool), @@ -120,11 +120,19 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - _ => unimplemented!(), + SwitchInt { ref discr, switch_ty: _, ref values, ref targets } => { + let discr_val = &self.value_stack[self.eval_lvalue(discr)]; + + let index = values.iter().position(|v| *discr_val == self.eval_constant(v)) + .expect("discriminant matched no values"); + + block = targets[index]; + } + // Diverge => unimplemented!(), // Panic { target } => unimplemented!(), // Switch { ref discr, adt_def, ref targets } => unimplemented!(), - // SwitchInt { ref discr, switch_ty, ref values, ref targets } => unimplemented!(), + _ => unimplemented!(), } } @@ -220,7 +228,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Uint(_u) => unimplemented!(), Str(ref _s) => unimplemented!(), ByteStr(ref _bs) => unimplemented!(), - Bool(_b) => unimplemented!(), + Bool(b) => Value::Bool(b), Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), diff --git a/test/basic.rs b/test/basic.rs index 99a3fb39ad2ca..bd60733b3d07d 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -73,4 +73,38 @@ fn factorial_recursive() -> i32 { fact(10) } +#[miri_run(expected = "Int(1)")] +fn match_bool() -> i32 { + let b = true; + match b { + true => 1, + false => 0, + } +} + +#[miri_run(expected = "Int(20)")] +fn match_int() -> i32 { + let n = 2; + match n { + 0 => 0, + 1 => 10, + 2 => 20, + 3 => 30, + _ => 100, + } +} + +// #[miri_run(expected = "Int(4)")] +// fn match_int_range() -> i32 { +// let n = 42; +// match n { +// 0...9 => 0, +// 10...19 => 1, +// 20...29 => 2, +// 30...39 => 3, +// 40...49 => 4, +// _ => 5, +// } +// } + fn main() {} From f674aeba97748a7c55ef088c5b683a3981bee330 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 15:32:39 -0600 Subject: [PATCH 0015/1096] Add a constant flag to enable and disable execution trac printouts. --- src/interpreter.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6f2f604d84ec0..6bae0115d97bf 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,6 +6,8 @@ use syntax::attr::AttrMetaMethods; use std::iter; +const TRACE_EXECUTION: bool = false; + #[derive(Clone, Debug, PartialEq)] enum Value { Uninit, @@ -78,6 +80,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { for stmt in &block_data.statements { use rustc_mir::repr::StatementKind::*; + if TRACE_EXECUTION { println!("{:?}", stmt); } + match stmt.kind { Assign(ref lvalue, ref rvalue) => { let index = self.eval_lvalue(lvalue); @@ -91,6 +95,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } + if TRACE_EXECUTION { println!("{:?}", block_data.terminator); } + match block_data.terminator { Return => break, Goto { target } => block = target, From fae7a5685f378d846f53d2d5083f4c2feec39960 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 15:34:28 -0600 Subject: [PATCH 0016/1096] Refactor stack frames and pointers in preparation for aggregates like ADTs. --- src/interpreter.rs | 100 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6bae0115d97bf..dc17516d5c534 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -16,18 +16,65 @@ enum Value { Func(def_id::DefId), } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum Pointer { + Stack(usize), + // TODO(tsion): Heap +} + +/// A stack frame: +/// +/// ```text +/// +-----------------------+ +/// | ReturnPointer | return value +/// + - - - - - - - - - - - + +/// | Arg(0) | +/// | Arg(1) | arguments +/// | ... | +/// | Arg(num_args - 1) | +/// + - - - - - - - - - - - + +/// | Var(0) | +/// | Var(1) | variables +/// | ... | +/// | Var(num_vars - 1) | +/// + - - - - - - - - - - - + +/// | Temp(0) | +/// | Temp(1) | temporaries +/// | ... | +/// | Temp(num_temps - 1) | +/// + - - - - - - - - - - - + +/// | Aggregates | aggregates +/// +-----------------------+ +/// ``` #[derive(Debug)] struct Frame { offset: usize, num_args: usize, num_vars: usize, num_temps: usize, + // aggregates } impl Frame { fn size(&self) -> usize { 1 + self.num_args + self.num_vars + self.num_temps } + + fn return_val_offset(&self) -> usize { + self.offset + } + + fn arg_offset(&self, i: u32) -> usize { + self.offset + 1 + i as usize + } + + fn var_offset(&self, i: u32) -> usize { + self.offset + 1 + self.num_args + i as usize + } + + fn temp_offset(&self, i: u32) -> usize { + self.offset + 1 + self.num_args + self.num_vars + i as usize + } } struct Interpreter<'a, 'tcx: 'a> { @@ -84,9 +131,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match stmt.kind { Assign(ref lvalue, ref rvalue) => { - let index = self.eval_lvalue(lvalue); + let ptr = self.eval_lvalue(lvalue); let value = self.eval_rvalue(rvalue); - self.value_stack[index] = value; + self.write_pointer(ptr, value); } Drop(_kind, ref _lv) => { @@ -102,7 +149,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Goto { target } => block = target, Call { data: mir::CallData { ref destination, ref func, ref args }, targets } => { - let index = self.eval_lvalue(destination); + let ptr = self.eval_lvalue(destination); let func_val = self.eval_operand(func); if let Value::Func(def_id) = func_val { @@ -111,7 +158,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let arg_vals: Vec = args.iter().map(|arg| self.eval_operand(arg)).collect(); - self.value_stack[index] = self.call(mir, &arg_vals); + // FIXME: Pass the destination lvalue such that the ReturnPointer inside + // the function call will point to the destination. + let return_val = self.call(mir, &arg_vals); + self.write_pointer(ptr, return_val); block = targets[0]; } else { panic!("tried to call a non-function value: {:?}", func_val); @@ -127,9 +177,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } SwitchInt { ref discr, switch_ty: _, ref values, ref targets } => { - let discr_val = &self.value_stack[self.eval_lvalue(discr)]; + let discr_val = self.read_lvalue(discr); - let index = values.iter().position(|v| *discr_val == self.eval_constant(v)) + let index = values.iter().position(|v| discr_val == self.eval_constant(v)) .expect("discriminant matched no values"); block = targets[index]; @@ -142,21 +192,21 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - let ret_val = self.value_stack[self.eval_lvalue(&mir::Lvalue::ReturnPointer)].clone(); + let ret_val = self.read_lvalue(&mir::Lvalue::ReturnPointer); self.pop_stack_frame(); ret_val } - fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> usize { + fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer { use rustc_mir::repr::Lvalue::*; let frame = self.call_stack.last().expect("missing call frame"); match *lvalue { - ReturnPointer => frame.offset, - Arg(i) => frame.offset + 1 + i as usize, - Var(i) => frame.offset + 1 + frame.num_args + i as usize, - Temp(i) => frame.offset + 1 + frame.num_args + frame.num_vars + i as usize, + ReturnPointer => Pointer::Stack(frame.return_val_offset()), + Arg(i) => Pointer::Stack(frame.arg_offset(i)), + Var(i) => Pointer::Stack(frame.var_offset(i)), + Temp(i) => Pointer::Stack(frame.temp_offset(i)), _ => unimplemented!(), } } @@ -203,6 +253,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } + // Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), ref operands) => { + // let num_fields = adt_def.variants[variant].fields.len(); + // debug_assert_eq!(num_fields, operands.len()); + + // let data = operands.iter().map(|op| self.eval_operand(op)).collect(); + // Value::Adt(variant, data) + // } + _ => unimplemented!(), } } @@ -211,7 +269,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { use rustc_mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.value_stack[self.eval_lvalue(lvalue)].clone(), + Consume(ref lvalue) => self.read_lvalue(lvalue), Constant(ref constant) => { match constant.literal { @@ -240,6 +298,22 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Function(_def_id) => unimplemented!(), } } + + fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value { + self.read_pointer(self.eval_lvalue(lvalue)) + } + + fn read_pointer(&self, p: Pointer) -> Value { + match p { + Pointer::Stack(offset) => self.value_stack[offset].clone(), + } + } + + fn write_pointer(&mut self, p: Pointer, val: Value) { + match p { + Pointer::Stack(offset) => self.value_stack[offset] = val, + } + } } pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) { From 39d9d40e402abacae14f73c3476541c729a0f5a2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 15:54:02 -0600 Subject: [PATCH 0017/1096] Remove glob uses and slightly refactor. --- src/interpreter.rs | 132 ++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index dc17516d5c534..8885833eaf9ac 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -120,23 +120,19 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let mut block = mir::START_BLOCK; loop { - use rustc_mir::repr::Terminator::*; - let block_data = mir.basic_block_data(block); for stmt in &block_data.statements { - use rustc_mir::repr::StatementKind::*; - if TRACE_EXECUTION { println!("{:?}", stmt); } match stmt.kind { - Assign(ref lvalue, ref rvalue) => { + mir::StatementKind::Assign(ref lvalue, ref rvalue) => { let ptr = self.eval_lvalue(lvalue); let value = self.eval_rvalue(rvalue); self.write_pointer(ptr, value); } - Drop(_kind, ref _lv) => { + mir::StatementKind::Drop(_kind, ref _lv) => { // TODO }, } @@ -145,10 +141,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { if TRACE_EXECUTION { println!("{:?}", block_data.terminator); } match block_data.terminator { - Return => break, - Goto { target } => block = target, + mir::Terminator::Return => break, + mir::Terminator::Goto { target } => block = target, - Call { data: mir::CallData { ref destination, ref func, ref args }, targets } => { + mir::Terminator::Call { data: mir::CallData { ref destination, ref func, ref args }, targets } => { let ptr = self.eval_lvalue(destination); let func_val = self.eval_operand(func); @@ -168,7 +164,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - If { ref cond, targets } => { + mir::Terminator::If { ref cond, targets } => { match self.eval_operand(cond) { Value::Bool(true) => block = targets[0], Value::Bool(false) => block = targets[1], @@ -176,7 +172,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - SwitchInt { ref discr, switch_ty: _, ref values, ref targets } => { + mir::Terminator::SwitchInt { ref discr, switch_ty: _, ref values, ref targets } => { let discr_val = self.read_lvalue(discr); let index = values.iter().position(|v| discr_val == self.eval_constant(v)) @@ -185,9 +181,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { block = targets[index]; } - // Diverge => unimplemented!(), - // Panic { target } => unimplemented!(), - // Switch { ref discr, adt_def, ref targets } => unimplemented!(), + // mir::Terminator::Diverge => unimplemented!(), + // mir::Terminator::Panic { target } => unimplemented!(), + // mir::Terminator::Switch { ref discr, adt_def, ref targets } => unimplemented!(), _ => unimplemented!(), } } @@ -198,62 +194,64 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer { - use rustc_mir::repr::Lvalue::*; - let frame = self.call_stack.last().expect("missing call frame"); match *lvalue { - ReturnPointer => Pointer::Stack(frame.return_val_offset()), - Arg(i) => Pointer::Stack(frame.arg_offset(i)), - Var(i) => Pointer::Stack(frame.var_offset(i)), - Temp(i) => Pointer::Stack(frame.temp_offset(i)), + mir::Lvalue::ReturnPointer => Pointer::Stack(frame.return_val_offset()), + mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i)), + mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i)), + mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i)), _ => unimplemented!(), } } - fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { - use rustc_mir::repr::Rvalue::*; - use rustc_mir::repr::BinOp::*; - use rustc_mir::repr::UnOp::*; + fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Value, right: Value) -> Value { + match (left, right) { + (Value::Int(l), Value::Int(r)) => { + match bin_op { + mir::BinOp::Add => Value::Int(l + r), + mir::BinOp::Sub => Value::Int(l - r), + mir::BinOp::Mul => Value::Int(l * r), + mir::BinOp::Div => Value::Int(l / r), + mir::BinOp::Rem => Value::Int(l % r), + mir::BinOp::BitXor => Value::Int(l ^ r), + mir::BinOp::BitAnd => Value::Int(l & r), + mir::BinOp::BitOr => Value::Int(l | r), + mir::BinOp::Shl => Value::Int(l << r), + mir::BinOp::Shr => Value::Int(l >> r), + mir::BinOp::Eq => Value::Bool(l == r), + mir::BinOp::Lt => Value::Bool(l < r), + mir::BinOp::Le => Value::Bool(l <= r), + mir::BinOp::Ne => Value::Bool(l != r), + mir::BinOp::Ge => Value::Bool(l >= r), + mir::BinOp::Gt => Value::Bool(l > r), + } + } + _ => unimplemented!(), + } + } + + fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { match *rvalue { - Use(ref operand) => self.eval_operand(operand), - - BinaryOp(bin_op, ref left, ref right) => { - match (self.eval_operand(left), self.eval_operand(right)) { - (Value::Int(l), Value::Int(r)) => { - match bin_op { - Add => Value::Int(l + r), - Sub => Value::Int(l - r), - Mul => Value::Int(l * r), - Div => Value::Int(l / r), - Rem => Value::Int(l % r), - BitXor => Value::Int(l ^ r), - BitAnd => Value::Int(l & r), - BitOr => Value::Int(l | r), - Shl => Value::Int(l << r), - Shr => Value::Int(l >> r), - Eq => Value::Bool(l == r), - Lt => Value::Bool(l < r), - Le => Value::Bool(l <= r), - Ne => Value::Bool(l != r), - Ge => Value::Bool(l >= r), - Gt => Value::Bool(l > r), - } - } - _ => unimplemented!(), - } + mir::Rvalue::Use(ref operand) => self.eval_operand(operand), + + mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { + let left_val = self.eval_operand(left); + let right_val = self.eval_operand(right); + self.eval_binary_op(bin_op, left_val, right_val) } - UnaryOp(un_op, ref operand) => { + mir::Rvalue::UnaryOp(un_op, ref operand) => { match (un_op, self.eval_operand(operand)) { - (Not, Value::Int(n)) => Value::Int(!n), - (Neg, Value::Int(n)) => Value::Int(-n), + (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n), + (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n), _ => unimplemented!(), } } - // Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), ref operands) => { + // mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), + // ref operands) => { // let num_fields = adt_def.variants[variant].fields.len(); // debug_assert_eq!(num_fields, operands.len()); @@ -266,12 +264,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn eval_operand(&mut self, op: &mir::Operand) -> Value { - use rustc_mir::repr::Operand::*; - match *op { - Consume(ref lvalue) => self.read_lvalue(lvalue), + mir::Operand::Consume(ref lvalue) => self.read_lvalue(lvalue), - Constant(ref constant) => { + mir::Operand::Constant(ref constant) => { match constant.literal { mir::Literal::Value { ref value } => self.eval_constant(value), @@ -284,18 +280,16 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn eval_constant(&self, const_val: &const_eval::ConstVal) -> Value { - use rustc::middle::const_eval::ConstVal::*; - match *const_val { - Float(_f) => unimplemented!(), - Int(i) => Value::Int(i), - Uint(_u) => unimplemented!(), - Str(ref _s) => unimplemented!(), - ByteStr(ref _bs) => unimplemented!(), - Bool(b) => Value::Bool(b), - Struct(_node_id) => unimplemented!(), - Tuple(_node_id) => unimplemented!(), - Function(_def_id) => unimplemented!(), + const_eval::ConstVal::Float(_f) => unimplemented!(), + const_eval::ConstVal::Int(i) => Value::Int(i), + const_eval::ConstVal::Uint(_u) => unimplemented!(), + const_eval::ConstVal::Str(ref _s) => unimplemented!(), + const_eval::ConstVal::ByteStr(ref _bs) => unimplemented!(), + const_eval::ConstVal::Bool(b) => Value::Bool(b), + const_eval::ConstVal::Struct(_node_id) => unimplemented!(), + const_eval::ConstVal::Tuple(_node_id) => unimplemented!(), + const_eval::ConstVal::Function(_def_id) => unimplemented!(), } } From 651896a0ae9514a6111285c4d9e46ec8c2941095 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 16:16:34 -0600 Subject: [PATCH 0018/1096] Fix an overlong line formatting issue. --- src/interpreter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8885833eaf9ac..1a19dab221bca 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -144,7 +144,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Terminator::Return => break, mir::Terminator::Goto { target } => block = target, - mir::Terminator::Call { data: mir::CallData { ref destination, ref func, ref args }, targets } => { + mir::Terminator::Call { ref data, targets } => { + let mir::CallData { ref destination, ref func, ref args } = *data; + let ptr = self.eval_lvalue(destination); let func_val = self.eval_operand(func); From aa4b82209ea13ca04c92583222782e3b9e17a096 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 16:18:46 -0600 Subject: [PATCH 0019/1096] Fix another long line. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1a19dab221bca..8e96a7e9bec1d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -174,7 +174,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - mir::Terminator::SwitchInt { ref discr, switch_ty: _, ref values, ref targets } => { + mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_val = self.read_lvalue(discr); let index = values.iter().position(|v| discr_val == self.eval_constant(v)) From 0a2f43e55350b5ddcee7953987df16c2dfa03c7f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 20:49:25 -0600 Subject: [PATCH 0020/1096] Write fn call return values directly into an lvalue the caller provides. --- src/interpreter.rs | 53 ++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8e96a7e9bec1d..06766d81c1620 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -26,8 +26,6 @@ enum Pointer { /// /// ```text /// +-----------------------+ -/// | ReturnPointer | return value -/// + - - - - - - - - - - - + /// | Arg(0) | /// | Arg(1) | arguments /// | ... | @@ -48,6 +46,7 @@ enum Pointer { /// ``` #[derive(Debug)] struct Frame { + return_ptr: Pointer, offset: usize, num_args: usize, num_vars: usize, @@ -57,23 +56,19 @@ struct Frame { impl Frame { fn size(&self) -> usize { - 1 + self.num_args + self.num_vars + self.num_temps - } - - fn return_val_offset(&self) -> usize { - self.offset + self.num_args + self.num_vars + self.num_temps } - fn arg_offset(&self, i: u32) -> usize { - self.offset + 1 + i as usize + fn arg_offset(&self, i: usize) -> usize { + self.offset + i } - fn var_offset(&self, i: u32) -> usize { - self.offset + 1 + self.num_args + i as usize + fn var_offset(&self, i: usize) -> usize { + self.offset + self.num_args + i } - fn temp_offset(&self, i: u32) -> usize { - self.offset + 1 + self.num_args + self.num_vars + i as usize + fn temp_offset(&self, i: usize) -> usize { + self.offset + self.num_args + self.num_vars + i } } @@ -89,13 +84,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Interpreter { tcx: tcx, mir_map: mir_map, - value_stack: Vec::new(), + value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value. call_stack: Vec::new(), } } - fn push_stack_frame(&mut self, mir: &Mir, args: &[Value]) { + fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { self.call_stack.push(Frame { + return_ptr: return_ptr, offset: self.value_stack.len(), num_args: mir.arg_decls.len(), num_vars: mir.var_decls.len(), @@ -106,7 +102,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); for (i, arg) in args.iter().enumerate() { - self.value_stack[frame.offset + 1 + i] = arg.clone(); + self.value_stack[frame.arg_offset(i)] = arg.clone(); } } @@ -115,8 +111,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { self.value_stack.truncate(frame.offset); } - fn call(&mut self, mir: &Mir, args: &[Value]) -> Value { - self.push_stack_frame(mir, args); + fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { + self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; loop { @@ -156,10 +152,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let arg_vals: Vec = args.iter().map(|arg| self.eval_operand(arg)).collect(); - // FIXME: Pass the destination lvalue such that the ReturnPointer inside - // the function call will point to the destination. - let return_val = self.call(mir, &arg_vals); - self.write_pointer(ptr, return_val); + self.call(mir, &arg_vals, ptr); block = targets[0]; } else { panic!("tried to call a non-function value: {:?}", func_val); @@ -190,19 +183,17 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - let ret_val = self.read_lvalue(&mir::Lvalue::ReturnPointer); self.pop_stack_frame(); - ret_val } fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer { let frame = self.call_stack.last().expect("missing call frame"); match *lvalue { - mir::Lvalue::ReturnPointer => Pointer::Stack(frame.return_val_offset()), - mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i)), - mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i)), - mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i)), + mir::Lvalue::ReturnPointer => frame.return_ptr, + mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), + mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), + mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), _ => unimplemented!(), } } @@ -319,10 +310,12 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> let item = tcx.map.expect_item(id); println!("Interpreting: {}", item.name); + let mut interpreter = Interpreter::new(tcx, mir_map); - let val = interpreter.call(mir, &[]); - let val_str = format!("{:?}", val); + let return_ptr = Pointer::Stack(0); + interpreter.call(mir, &[], return_ptr); + let val_str = format!("{:?}", interpreter.read_pointer(return_ptr)); if !check_expected(&val_str, attr) { println!("=> {}\n", val_str); } From e05df509fb62a0398669b8e08f6e70dea8231306 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 20 Nov 2015 20:52:33 -0600 Subject: [PATCH 0021/1096] Refactor push_stack_frame. --- src/interpreter.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 06766d81c1620..f880e1cd35fa8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -90,20 +90,22 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { - self.call_stack.push(Frame { + let frame = Frame { return_ptr: return_ptr, offset: self.value_stack.len(), num_args: mir.arg_decls.len(), num_vars: mir.var_decls.len(), num_temps: mir.temp_decls.len(), - }); + }; - let frame = self.call_stack.last().unwrap(); self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); for (i, arg) in args.iter().enumerate() { self.value_stack[frame.arg_offset(i)] = arg.clone(); } + + self.call_stack.push(frame); + } fn pop_stack_frame(&mut self) { From 12bce479b31760d4f92e82adca31b99c2185ec41 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 21 Nov 2015 01:07:32 -0600 Subject: [PATCH 0022/1096] Support ADT aggregate rvalues and allocation. --- src/interpreter.rs | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f880e1cd35fa8..0939909968c9e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -13,6 +13,7 @@ enum Value { Uninit, Bool(bool), Int(i64), // FIXME: Should be bit-width aware. + Adt { variant: usize, data: Pointer }, Func(def_id::DefId), } @@ -22,6 +23,14 @@ enum Pointer { // TODO(tsion): Heap } +impl Pointer { + fn offset(self, i: usize) -> Self { + match self { + Pointer::Stack(p) => Pointer::Stack(p + i), + } + } +} + /// A stack frame: /// /// ```text @@ -51,12 +60,12 @@ struct Frame { num_args: usize, num_vars: usize, num_temps: usize, - // aggregates + num_aggregate_fields: usize, } impl Frame { fn size(&self) -> usize { - self.num_args + self.num_vars + self.num_temps + self.num_args + self.num_vars + self.num_temps + self.num_aggregate_fields } fn arg_offset(&self, i: usize) -> usize { @@ -96,6 +105,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { num_args: mir.arg_decls.len(), num_vars: mir.var_decls.len(), num_temps: mir.temp_decls.len(), + num_aggregate_fields: 0, }; self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); @@ -113,6 +123,13 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { self.value_stack.truncate(frame.offset); } + fn allocate_aggregate(&mut self, size: usize) -> Pointer { + let frame = self.call_stack.last_mut().expect("missing call frame"); + let ptr = Pointer::Stack(self.value_stack.len()); + self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); + ptr + } + fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; @@ -245,14 +262,23 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - // mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), - // ref operands) => { - // let num_fields = adt_def.variants[variant].fields.len(); - // debug_assert_eq!(num_fields, operands.len()); + mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), + ref operands) => { + let max_fields = adt_def.variants + .iter() + .map(|v| v.fields.len()) + .max() + .unwrap_or(0); + + let ptr = self.allocate_aggregate(max_fields); - // let data = operands.iter().map(|op| self.eval_operand(op)).collect(); - // Value::Adt(variant, data) - // } + for (i, operand) in operands.iter().enumerate() { + let val = self.eval_operand(operand); + self.write_pointer(ptr.offset(i), val); + } + + Value::Adt { variant: variant, data: ptr } + } _ => unimplemented!(), } From 2010b14ac821d69401dd778d164194030939b0d2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 21 Nov 2015 01:31:09 -0600 Subject: [PATCH 0023/1096] Add initial support for matching on enums. This adds support for: * the Switch terminator * the Downcast projection rvalue * the Index projection rvalue --- src/interpreter.rs | 57 +++++++++++++++++++++++++++++++++++++++++----- test/basic.rs | 23 +++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 0939909968c9e..bf0f3af491a41 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -12,8 +12,8 @@ const TRACE_EXECUTION: bool = false; enum Value { Uninit, Bool(bool), - Int(i64), // FIXME: Should be bit-width aware. - Adt { variant: usize, data: Pointer }, + Int(i64), // FIXME(tsion): Should be bit-width aware. + Adt { variant: usize, data_ptr: Pointer }, Func(def_id::DefId), } @@ -125,8 +125,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn allocate_aggregate(&mut self, size: usize) -> Pointer { let frame = self.call_stack.last_mut().expect("missing call frame"); + frame.num_aggregate_fields += size; + let ptr = Pointer::Stack(self.value_stack.len()); - self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); + self.value_stack.extend(iter::repeat(Value::Uninit).take(size)); ptr } @@ -195,9 +197,18 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { block = targets[index]; } + mir::Terminator::Switch { ref discr, ref targets, .. } => { + let discr_val = self.read_lvalue(discr); + + if let Value::Adt { variant, .. } = discr_val { + block = targets[variant]; + } else { + panic!("Switch on non-Adt value: {:?}", discr_val); + } + } + // mir::Terminator::Diverge => unimplemented!(), // mir::Terminator::Panic { target } => unimplemented!(), - // mir::Terminator::Switch { ref discr, adt_def, ref targets } => unimplemented!(), _ => unimplemented!(), } } @@ -213,6 +224,37 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), + + mir::Lvalue::Projection(ref proj) => { + // proj.base: Lvalue + // proj.elem: ProjectionElem + + let base_ptr = self.eval_lvalue(&proj.base); + + match proj.elem { + mir::ProjectionElem::Field(field) => { + base_ptr.offset(field.index()) + } + + mir::ProjectionElem::Downcast(_, variant) => { + let adt_val = self.read_pointer(base_ptr); + + match adt_val { + Value::Adt { variant: actual_variant, data_ptr } => { + debug_assert_eq!(variant, actual_variant); + data_ptr + } + + _ => panic!("Downcast attempted on non-Adt: {:?}", adt_val), + } + } + + mir::ProjectionElem::Deref => unimplemented!(), + mir::ProjectionElem::Index(ref _operand) => unimplemented!(), + mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), + } + } + _ => unimplemented!(), } } @@ -262,7 +304,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, substs), + mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), ref operands) => { let max_fields = adt_def.variants .iter() @@ -277,7 +319,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { self.write_pointer(ptr.offset(i), val); } - Value::Adt { variant: variant, data: ptr } + Value::Adt { variant: variant, data_ptr: ptr } } _ => unimplemented!(), @@ -293,6 +335,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Literal::Value { ref value } => self.eval_constant(value), mir::Literal::Item { def_id, substs: _ } => { + // FIXME(tsion): Only items of function type shoud be wrapped into Func + // values. One test currently fails because a unit-like enum variant gets + // wrapped into Func here instead of a Value::Adt. Value::Func(def_id) } } diff --git a/test/basic.rs b/test/basic.rs index bd60733b3d07d..330bab89fea90 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -107,4 +107,27 @@ fn match_int() -> i32 { // } // } +enum MyOption { + Some { data: T }, + None, +} + +#[miri_run(expected = "Int(13)")] +fn match_opt_some() -> i32 { + let x = MyOption::Some { data: 13 }; + match x { + MyOption::Some { data } => data, + MyOption::None => 42, + } +} + +// #[miri_run(expected = "Int(42)")] +// fn match_opt_none() -> i32 { +// let x = MyOption::None; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } + fn main() {} From 61e4d0d82a03a7aed6f6d8573fe2c6833b21104d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 21 Nov 2015 17:58:52 -0600 Subject: [PATCH 0024/1096] Ignore graphviz debug output files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f6..507684b6bfd33 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.dot From 064c3521c3375212e32ad086029c00657777a073 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 21 Nov 2015 21:20:06 -0600 Subject: [PATCH 0025/1096] Restructure into separate binary and library. --- Cargo.toml | 4 ++++ src/lib.rs | 7 +++++++ src/main.rs | 6 ++---- 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 8963f7a6ab8d8..9396477cd3ccf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,7 @@ authors = ["Scott Olson "] description = "An experimental interpreter for Rust MIR." repository = "https://github.com/tsion/miri" license = "ISC" + +[[bin]] +name = "miri" +doc = false diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000..da83e7db925ac --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +#![feature(rustc_private)] + +extern crate rustc; +extern crate rustc_mir; +extern crate syntax; + +pub mod interpreter; diff --git a/src/main.rs b/src/main.rs index 808b43e9643fa..7867526f26d42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,10 @@ #![feature(rustc_private)] +extern crate miri; extern crate rustc; extern crate rustc_driver; -extern crate rustc_mir; -extern crate syntax; - -mod interpreter; +use miri::interpreter; use rustc::session::Session; use rustc_driver::{driver, CompilerCalls, Compilation}; From caaec388b5dea182daacf99826ad5aad576d8cc9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 4 Dec 2015 13:09:14 -0600 Subject: [PATCH 0026/1096] Update for changes in rustc. --- src/interpreter.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bf0f3af491a41..81a606513a8aa 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,6 @@ use rustc::middle::{const_eval, def_id, ty}; +use rustc::mir::repr::{self as mir, Mir}; use rustc_mir::mir_map::MirMap; -use rustc_mir::repr::{self as mir, Mir}; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; @@ -335,7 +335,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Literal::Value { ref value } => self.eval_constant(value), mir::Literal::Item { def_id, substs: _ } => { - // FIXME(tsion): Only items of function type shoud be wrapped into Func + // FIXME(tsion): Only items of function type should be wrapped into Func // values. One test currently fails because a unit-like enum variant gets // wrapped into Func here instead of a Value::Adt. Value::Func(def_id) @@ -356,6 +356,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { const_eval::ConstVal::Struct(_node_id) => unimplemented!(), const_eval::ConstVal::Tuple(_node_id) => unimplemented!(), const_eval::ConstVal::Function(_def_id) => unimplemented!(), + const_eval::ConstVal::Array(_, _) => unimplemented!(), + const_eval::ConstVal::Repeat(_, _) => unimplemented!(), } } From 96128cff85255e397546fc4a92a36584bce855cb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Dec 2015 00:43:29 -0600 Subject: [PATCH 0027/1096] Update for changes in rustc. --- src/interpreter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 81a606513a8aa..4c608eb20cbb2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Terminator::Return => break, mir::Terminator::Goto { target } => block = target, - mir::Terminator::Call { ref data, targets } => { + mir::Terminator::Call { ref data, targets: (success_target, _panic_target) } => { let mir::CallData { ref destination, ref func, ref args } = *data; let ptr = self.eval_lvalue(destination); @@ -174,16 +174,16 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { args.iter().map(|arg| self.eval_operand(arg)).collect(); self.call(mir, &arg_vals, ptr); - block = targets[0]; + block = success_target } else { panic!("tried to call a non-function value: {:?}", func_val); } } - mir::Terminator::If { ref cond, targets } => { + mir::Terminator::If { ref cond, targets: (then_target, else_target) } => { match self.eval_operand(cond) { - Value::Bool(true) => block = targets[0], - Value::Bool(false) => block = targets[1], + Value::Bool(true) => block = then_target, + Value::Bool(false) => block = else_target, cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), } } From 5e0ba54d00a519dc864e1e7abda28569d51a26b1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Dec 2015 00:46:03 -0600 Subject: [PATCH 0028/1096] Move miri binary source into src/bin. --- src/{main.rs => bin/miri.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main.rs => bin/miri.rs} (100%) diff --git a/src/main.rs b/src/bin/miri.rs similarity index 100% rename from src/main.rs rename to src/bin/miri.rs From 97a68ad0f908c9b2ce14a8fa433c3e7c8bd80e16 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Dec 2015 00:46:32 -0600 Subject: [PATCH 0029/1096] Ignore generated documentation. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 507684b6bfd33..6dcf24f20c2cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target +/doc *.dot From df96c61591e9ad2c37b410450878ce14e726b8e4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Dec 2015 14:03:01 -0600 Subject: [PATCH 0030/1096] Implement cross-crate fn calls by loading Mir from crate metadata. --- src/interpreter.rs | 13 +++++++++++-- test/basic.rs | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 4c608eb20cbb2..b2bb03b04cd77 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,5 @@ use rustc::middle::{const_eval, def_id, ty}; +use rustc::middle::cstore::CrateStore; use rustc::mir::repr::{self as mir, Mir}; use rustc_mir::mir_map::MirMap; use syntax::ast::Attribute; @@ -168,8 +169,16 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let func_val = self.eval_operand(func); if let Value::Func(def_id) = func_val { - let node_id = self.tcx.map.as_local_node_id(def_id).unwrap(); - let mir = &self.mir_map[&node_id]; + let mir_data; + let mir = match self.tcx.map.as_local_node_id(def_id) { + Some(node_id) => self.mir_map.get(&node_id).unwrap(), + None => { + let cstore = &self.tcx.sess.cstore; + mir_data = cstore.maybe_get_item_mir(self.tcx, def_id).unwrap(); + &mir_data + } + }; + let arg_vals: Vec = args.iter().map(|arg| self.eval_operand(arg)).collect(); diff --git a/test/basic.rs b/test/basic.rs index 330bab89fea90..9ed032bd40102 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -130,4 +130,10 @@ fn match_opt_some() -> i32 { // } // } +/// Test calling a very simple function from the standard library. +#[miri_run(expected = "Int(1)")] +fn cross_crate_fn_call() -> i32 { + if 1i32.is_positive() { 1 } else { 0 } +} + fn main() {} From 2e38c5ba29954ca6a10a1f235c30a6b3771c70ff Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Dec 2015 14:03:26 -0600 Subject: [PATCH 0031/1096] Add (commented) test for basic use of std::option::Option. --- test/basic.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/basic.rs b/test/basic.rs index 9ed032bd40102..e818e9f86c5da 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -113,7 +113,7 @@ enum MyOption { } #[miri_run(expected = "Int(13)")] -fn match_opt_some() -> i32 { +fn match_my_opt_some() -> i32 { let x = MyOption::Some { data: 13 }; match x { MyOption::Some { data } => data, @@ -121,6 +121,15 @@ fn match_opt_some() -> i32 { } } +// #[miri_run(expected = "Int(13)")] +// fn match_opt_some() -> i32 { +// let x = Some(13); +// match x { +// Some(data) => data, +// None => 42, +// } +// } + // #[miri_run(expected = "Int(42)")] // fn match_opt_none() -> i32 { // let x = MyOption::None; From fbf49715c96c01dcd7745160638461f7011d41da Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Dec 2015 22:22:34 -0600 Subject: [PATCH 0032/1096] Update for upstream addition of ItemKind. --- src/interpreter.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b2bb03b04cd77..f5a60770f7fd2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -331,7 +331,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Value::Adt { variant: variant, data_ptr: ptr } } - _ => unimplemented!(), + ref r => panic!("can't handle rvalue: {:?}", r), } } @@ -343,12 +343,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match constant.literal { mir::Literal::Value { ref value } => self.eval_constant(value), - mir::Literal::Item { def_id, substs: _ } => { - // FIXME(tsion): Only items of function type should be wrapped into Func - // values. One test currently fails because a unit-like enum variant gets - // wrapped into Func here instead of a Value::Adt. - Value::Func(def_id) - } + mir::Literal::Item { def_id, kind, .. } => match kind { + mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), + _ => panic!("can't handle item literal: {:?}", constant.literal), + }, } } } From 947c1badd1f56571c34f9fcb114845f3aadd1d99 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Dec 2015 22:04:34 -0600 Subject: [PATCH 0033/1096] Uncomment other MyOption test. --- test/basic.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/basic.rs b/test/basic.rs index e818e9f86c5da..a4d0b4eb8b2d6 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -121,6 +121,15 @@ fn match_my_opt_some() -> i32 { } } +#[miri_run(expected = "Int(42)")] +fn match_my_opt_none() -> i32 { + let x = MyOption::None; + match x { + MyOption::Some { data } => data, + MyOption::None => 42, + } +} + // #[miri_run(expected = "Int(13)")] // fn match_opt_some() -> i32 { // let x = Some(13); @@ -130,15 +139,6 @@ fn match_my_opt_some() -> i32 { // } // } -// #[miri_run(expected = "Int(42)")] -// fn match_opt_none() -> i32 { -// let x = MyOption::None; -// match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, -// } -// } - /// Test calling a very simple function from the standard library. #[miri_run(expected = "Int(1)")] fn cross_crate_fn_call() -> i32 { From a3ca2db48aff772b0ff4e317c22681332eda0293 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Dec 2015 22:24:05 -0600 Subject: [PATCH 0034/1096] Add support for references. --- src/interpreter.rs | 26 ++++++++++++++++++-------- test/basic.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f5a60770f7fd2..e290a8d0c7310 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -14,6 +14,7 @@ enum Value { Uninit, Bool(bool), Int(i64), // FIXME(tsion): Should be bit-width aware. + Pointer(Pointer), Adt { variant: usize, data_ptr: Pointer }, Func(def_id::DefId), } @@ -247,18 +248,23 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::ProjectionElem::Downcast(_, variant) => { let adt_val = self.read_pointer(base_ptr); + if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { + debug_assert_eq!(variant, actual_variant); + data_ptr + } else { + panic!("Downcast attempted on non-ADT: {:?}", adt_val) + } + } - match adt_val { - Value::Adt { variant: actual_variant, data_ptr } => { - debug_assert_eq!(variant, actual_variant); - data_ptr - } - - _ => panic!("Downcast attempted on non-Adt: {:?}", adt_val), + mir::ProjectionElem::Deref => { + let ptr_val = self.read_pointer(base_ptr); + if let Value::Pointer(ptr) = ptr_val { + ptr + } else { + panic!("Deref attempted on non-pointer: {:?}", ptr_val) } } - mir::ProjectionElem::Deref => unimplemented!(), mir::ProjectionElem::Index(ref _operand) => unimplemented!(), mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), } @@ -313,6 +319,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } + mir::Rvalue::Ref(_region, _kind, ref lvalue) => { + Value::Pointer(self.eval_lvalue(lvalue)) + } + mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), ref operands) => { let max_fields = adt_def.variants diff --git a/test/basic.rs b/test/basic.rs index a4d0b4eb8b2d6..923abb1b80ea4 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -94,6 +94,34 @@ fn match_int() -> i32 { } } +#[miri_run(expected = "Int(1)")] +fn one_line_ref() -> i32 { + *&1 +} + +#[miri_run(expected = "Int(1)")] +fn basic_ref() -> i32 { + let x = &1; + *x +} + +#[miri_run(expected = "Int(3)")] +fn basic_ref_mut() -> i32 { + let x = &mut 1; + *x += 2; + *x +} + +#[miri_run(expected = "Int(3)")] +fn basic_ref_mut_var() -> i32 { + let mut a = 1; + { + let x = &mut a; + *x += 2; + } + a +} + // #[miri_run(expected = "Int(4)")] // fn match_int_range() -> i32 { // let n = 42; From 01c10e23a7491ee5121c1510cf53636ff1d5239a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 30 Dec 2015 12:10:44 -0600 Subject: [PATCH 0035/1096] Ignore generated MIR and PNG files. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 6dcf24f20c2cb..7b8cc5f430cbf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /doc *.dot +*.mir +*.png From 56ceebf86978a783d3607f40d9b993306254817b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Jan 2016 21:05:08 -0600 Subject: [PATCH 0036/1096] Update for changed in Rust master. --- src/interpreter.rs | 36 ++++++++++++++++++++++-------------- test/basic.rs | 24 ++++++++++++------------ 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e290a8d0c7310..10c9ef7dbd1cf 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -57,7 +57,10 @@ impl Pointer { /// ``` #[derive(Debug)] struct Frame { - return_ptr: Pointer, + /// A pointer to a stack cell to write the return value of the current call, if it's not a + /// divering call. + return_ptr: Option, + offset: usize, num_args: usize, num_vars: usize, @@ -100,7 +103,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { + fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { let frame = Frame { return_ptr: return_ptr, offset: self.value_stack.len(), @@ -134,11 +137,12 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { ptr } - fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Pointer) { + fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; loop { + if TRACE_EXECUTION { println!("Entering block: {:?}", block); } let block_data = mir.basic_block_data(block); for stmt in &block_data.statements { @@ -157,16 +161,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - if TRACE_EXECUTION { println!("{:?}", block_data.terminator); } + if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } - match block_data.terminator { + match *block_data.terminator() { mir::Terminator::Return => break, mir::Terminator::Goto { target } => block = target, - mir::Terminator::Call { ref data, targets: (success_target, _panic_target) } => { - let mir::CallData { ref destination, ref func, ref args } = *data; - - let ptr = self.eval_lvalue(destination); + mir::Terminator::Call { ref func, ref args, ref kind } => { + let ptr = kind.destination().map(|dest| self.eval_lvalue(&dest)); let func_val = self.eval_operand(func); if let Value::Func(def_id) = func_val { @@ -184,7 +186,13 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { args.iter().map(|arg| self.eval_operand(arg)).collect(); self.call(mir, &arg_vals, ptr); - block = success_target + + match *kind { + mir::CallKind::Converging { target: success_target, .. } | + mir::CallKind::ConvergingCleanup { targets: (success_target, _), .. } + => { block = success_target; } + _ => {} + } } else { panic!("tried to call a non-function value: {:?}", func_val); } @@ -217,8 +225,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - // mir::Terminator::Diverge => unimplemented!(), - // mir::Terminator::Panic { target } => unimplemented!(), + // mir::Terminator::Resume => unimplemented!(), _ => unimplemented!(), } } @@ -230,7 +237,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let frame = self.call_stack.last().expect("missing call frame"); match *lvalue { - mir::Lvalue::ReturnPointer => frame.return_ptr, + mir::Lvalue::ReturnPointer => + frame.return_ptr.expect("ReturnPointer used in a function with no return value"), mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), @@ -405,7 +413,7 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> let mut interpreter = Interpreter::new(tcx, mir_map); let return_ptr = Pointer::Stack(0); - interpreter.call(mir, &[], return_ptr); + interpreter.call(mir, &[], Some(return_ptr)); let val_str = format!("{:?}", interpreter.read_pointer(return_ptr)); if !check_expected(&val_str, attr) { diff --git a/test/basic.rs b/test/basic.rs index 923abb1b80ea4..301bbad3d8935 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -122,18 +122,18 @@ fn basic_ref_mut_var() -> i32 { a } -// #[miri_run(expected = "Int(4)")] -// fn match_int_range() -> i32 { -// let n = 42; -// match n { -// 0...9 => 0, -// 10...19 => 1, -// 20...29 => 2, -// 30...39 => 3, -// 40...49 => 4, -// _ => 5, -// } -// } +#[miri_run(expected = "Int(4)")] +fn match_int_range() -> i32 { + let n = 42; + match n { + 0...9 => 0, + 10...19 => 1, + 20...29 => 2, + 30...39 => 3, + 40...49 => 4, + _ => 5, + } +} enum MyOption { Some { data: T }, From 685d9f7e392b829e145283f63a160c9c4084a6fc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 7 Jan 2016 16:08:53 -0600 Subject: [PATCH 0037/1096] Match Terminators exhaustively. --- src/interpreter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 10c9ef7dbd1cf..515459dd60a62 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -225,8 +225,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - // mir::Terminator::Resume => unimplemented!(), - _ => unimplemented!(), + mir::Terminator::Resume => unimplemented!(), } } From 416bc12669a4860bd56873d0de204c5a09c94655 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 11 Jan 2016 20:30:15 -0600 Subject: [PATCH 0038/1096] Update for changes in rustc master. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 515459dd60a62..764deac8d14eb 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -168,7 +168,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Terminator::Goto { target } => block = target, mir::Terminator::Call { ref func, ref args, ref kind } => { - let ptr = kind.destination().map(|dest| self.eval_lvalue(&dest)); + let ptr = kind.destination().map(|dest| self.eval_lvalue(dest)); let func_val = self.eval_operand(func); if let Value::Func(def_id) = func_val { From f9ee6a0a3080c32b6cb5390e0980cb6972048f57 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 15 Jan 2016 16:04:32 -0600 Subject: [PATCH 0039/1096] Disable tests that fail on rustc master due to () rvalues. --- test/basic.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/basic.rs b/test/basic.rs index 301bbad3d8935..9b55f8073b0e6 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -47,18 +47,18 @@ fn call() -> i32 { increment(1) } -#[miri_run(expected = "Int(3628800)")] -fn factorial_loop() -> i32 { - let mut product = 1; - let mut i = 1; - - while i <= 10 { - product *= i; - i += 1; - } +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_loop() -> i32 { +// let mut product = 1; +// let mut i = 1; + +// while i <= 10 { +// product *= i; +// i += 1; +// } - product -} +// product +// } #[miri_run(expected = "Int(3628800)")] fn factorial_recursive() -> i32 { @@ -112,15 +112,15 @@ fn basic_ref_mut() -> i32 { *x } -#[miri_run(expected = "Int(3)")] -fn basic_ref_mut_var() -> i32 { - let mut a = 1; - { - let x = &mut a; - *x += 2; - } - a -} +// #[miri_run(expected = "Int(3)")] +// fn basic_ref_mut_var() -> i32 { +// let mut a = 1; +// { +// let x = &mut a; +// *x += 2; +// } +// a +// } #[miri_run(expected = "Int(4)")] fn match_int_range() -> i32 { From dcb2f0f800f67377c04143e40c4a1ea5dd300b6d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 15 Jan 2016 16:29:10 -0600 Subject: [PATCH 0040/1096] Enable a test that works now with rustc master. --- test/basic.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/basic.rs b/test/basic.rs index 9b55f8073b0e6..70bb9c3bb88a6 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -158,14 +158,14 @@ fn match_my_opt_none() -> i32 { } } -// #[miri_run(expected = "Int(13)")] -// fn match_opt_some() -> i32 { -// let x = Some(13); -// match x { -// Some(data) => data, -// None => 42, -// } -// } +#[miri_run(expected = "Int(13)")] +fn match_opt_some() -> i32 { + let x = Some(13); + match x { + Some(data) => data, + None => 42, + } +} /// Test calling a very simple function from the standard library. #[miri_run(expected = "Int(1)")] From a8cb824e51da7c1d369c0d8acd99f1e818ca2592 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 2 Feb 2016 04:47:28 -0600 Subject: [PATCH 0041/1096] Add licenses and readme. --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ README.md | 21 ++++++ 3 files changed, 247 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000000000..a32595fa70bc1 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2016 The Miri Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000000000..1f9d89a5862b5 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Miri Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..cd74686cedae2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# miri + +An experimental interpreter for [Rust][rust]'s [mid-level +intermediate representation][mir] (MIR). + +[rust]: https://www.rust-lang.org +[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md + +## License + +Licensed under either of + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be dual licensed as above, without any +additional terms or conditions. From b19b24a2d99dd90d213576a74502a32c5811f90d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 10 Feb 2016 11:54:22 -0600 Subject: [PATCH 0042/1096] Add note about usask research course. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd74686cedae2..4c591dff015e3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # miri An experimental interpreter for [Rust][rust]'s [mid-level -intermediate representation][mir] (MIR). +intermediate representation][mir] (MIR). This project is part of my course work +for an undergraduate research course at the [University of Saskatchewan][usask]. -[rust]: https://www.rust-lang.org +[rust]: https://www.rust-lang.org/ [mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md +[usask]: https://www.usask.ca/ ## License From 1c7738d38a09aecbd88d1e8eb540619ffa733468 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 19:06:22 -0600 Subject: [PATCH 0043/1096] Update for changes in rustc master. --- src/interpreter.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 764deac8d14eb..9308a6dc80e2e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,7 +1,7 @@ use rustc::middle::{const_eval, def_id, ty}; use rustc::middle::cstore::CrateStore; use rustc::mir::repr::{self as mir, Mir}; -use rustc_mir::mir_map::MirMap; +use rustc::mir::mir_map::MirMap; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; @@ -154,10 +154,6 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let value = self.eval_rvalue(rvalue); self.write_pointer(ptr, value); } - - mir::StatementKind::Drop(_kind, ref _lv) => { - // TODO - }, } } @@ -167,14 +163,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Terminator::Return => break, mir::Terminator::Goto { target } => block = target, - mir::Terminator::Call { ref func, ref args, ref kind } => { - let ptr = kind.destination().map(|dest| self.eval_lvalue(dest)); + mir::Terminator::Call { ref func, ref args, ref destination, .. } => { + let ptr = destination.as_ref().map(|&(ref lv, _)| self.eval_lvalue(lv)); let func_val = self.eval_operand(func); if let Value::Func(def_id) = func_val { let mir_data; let mir = match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => self.mir_map.get(&node_id).unwrap(), + Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), None => { let cstore = &self.tcx.sess.cstore; mir_data = cstore.maybe_get_item_mir(self.tcx, def_id).unwrap(); @@ -187,11 +183,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { self.call(mir, &arg_vals, ptr); - match *kind { - mir::CallKind::Converging { target: success_target, .. } | - mir::CallKind::ConvergingCleanup { targets: (success_target, _), .. } - => { block = success_target; } - _ => {} + if let Some((_, target)) = *destination { + block = target; } } else { panic!("tried to call a non-function value: {:?}", func_val); @@ -225,6 +218,11 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } + mir::Terminator::Drop { target, .. } => { + // TODO: Handle destructors and dynamic drop. + block = target; + } + mir::Terminator::Resume => unimplemented!(), } } @@ -403,7 +401,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) { - for (&id, mir) in mir_map { + for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { if attr.check_name("miri_run") { let item = tcx.map.expect_item(id); From d4c0ef420d24dee2650813d83470e2ce6267f069 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 18 Feb 2016 19:24:42 -0600 Subject: [PATCH 0044/1096] Remove old comments. --- src/interpreter.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 9308a6dc80e2e..d912fe47a70d4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -241,9 +241,6 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), mir::Lvalue::Projection(ref proj) => { - // proj.base: Lvalue - // proj.elem: ProjectionElem - let base_ptr = self.eval_lvalue(&proj.base); match proj.elem { From b26388659891322e5382e754dc211816e20a22ce Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 25 Feb 2016 16:06:50 -0600 Subject: [PATCH 0045/1096] Update for changes in rustc master. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d912fe47a70d4..ce923f2b34eb9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -244,7 +244,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let base_ptr = self.eval_lvalue(&proj.base); match proj.elem { - mir::ProjectionElem::Field(field) => { + mir::ProjectionElem::Field(field, _) => { base_ptr.offset(field.index()) } From 2776f55d0cb961ae9fef2a70917381baccdfa935 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 27 Feb 2016 19:20:25 -0600 Subject: [PATCH 0046/1096] WIP: Switching to a new byte-based value representation. --- Cargo.lock | 8 + Cargo.toml | 11 +- src/interpreter.rs | 613 +++++++++++++++++++++++++++------------------ src/lib.rs | 1 + test/new_values.rs | 176 +++++++++++++ 5 files changed, 561 insertions(+), 248 deletions(-) create mode 100644 test/new_values.rs diff --git a/Cargo.lock b/Cargo.lock index b86c754ccdf18..8fc715429970f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,12 @@ [root] name = "miri" version = "0.1.0" +dependencies = [ + "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 9396477cd3ccf..a369f6070c86b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,14 @@ [package] -name = "miri" -version = "0.1.0" authors = ["Scott Olson "] description = "An experimental interpreter for Rust MIR." -repository = "https://github.com/tsion/miri" license = "ISC" +name = "miri" +repository = "https://github.com/tsion/miri" +version = "0.1.0" [[bin]] -name = "miri" doc = false +name = "miri" + +[dependencies] +byteorder = "0.4.2" diff --git a/src/interpreter.rs b/src/interpreter.rs index ce923f2b34eb9..01db533661ed3 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,37 +1,127 @@ +// TODO(tsion): Remove this. +#![allow(unused_imports, dead_code, unused_variables)] + +use byteorder; +use byteorder::ByteOrder; use rustc::middle::{const_eval, def_id, ty}; use rustc::middle::cstore::CrateStore; use rustc::mir::repr::{self as mir, Mir}; use rustc::mir::mir_map::MirMap; +use std::collections::HashMap; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; use std::iter; -const TRACE_EXECUTION: bool = false; +const TRACE_EXECUTION: bool = true; -#[derive(Clone, Debug, PartialEq)] -enum Value { - Uninit, - Bool(bool), - Int(i64), // FIXME(tsion): Should be bit-width aware. - Pointer(Pointer), - Adt { variant: usize, data_ptr: Pointer }, - Func(def_id::DefId), -} +mod memory { + use byteorder; + use byteorder::ByteOrder; + use rustc::middle::ty; + use std::collections::HashMap; + use std::mem; -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum Pointer { - Stack(usize), - // TODO(tsion): Heap -} + pub struct Memory { + next_id: u64, + alloc_map: HashMap, + } + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub struct AllocId(u64); + + // TODO(tsion): Remove this hack. + pub fn alloc_id_hack(i: u64) -> AllocId { + AllocId(i) + } + + // TODO(tsion): Shouldn't clone values. + #[derive(Clone, Debug)] + pub struct Value { + pub bytes: Vec, + // TODO(tsion): relocations + // TODO(tsion): undef mask + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct Pointer { + pub alloc_id: AllocId, + pub offset: usize, + pub repr: Repr, + } + + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum Repr { + Int, + + StackFrame { + locals: Vec, + } + } + + impl Memory { + pub fn new() -> Self { + Memory { next_id: 0, alloc_map: HashMap::new() } + } + + pub fn allocate(&mut self, size: usize) -> AllocId { + let id = AllocId(self.next_id); + let val = Value { bytes: vec![0; size] }; + self.alloc_map.insert(self.next_id, val); + self.next_id += 1; + id + } + + pub fn allocate_int(&mut self, n: i64) -> AllocId { + let id = self.allocate(mem::size_of::()); + byteorder::NativeEndian::write_i64(&mut self.value_mut(id).unwrap().bytes, n); + id + } + + pub fn value(&self, id: AllocId) -> Option<&Value> { + self.alloc_map.get(&id.0) + } + + pub fn value_mut(&mut self, id: AllocId) -> Option<&mut Value> { + self.alloc_map.get_mut(&id.0) + } + } + + impl Pointer { + pub fn offset(self, i: usize) -> Self { + Pointer { offset: self.offset + i, ..self } + } + } + + impl Repr { + // TODO(tsion): Cache these outputs. + pub fn from_ty(ty: ty::Ty) -> Self { + match ty.sty { + ty::TyInt(_) => Repr::Int, + _ => unimplemented!(), + } + } -impl Pointer { - fn offset(self, i: usize) -> Self { - match self { - Pointer::Stack(p) => Pointer::Stack(p + i), + pub fn size(&self) -> usize { + match *self { + Repr::Int => 8, + Repr::StackFrame { ref locals } => + locals.iter().map(Repr::size).fold(0, |a, b| a + b) + } } } } +use self::memory::{Pointer, Repr, Value}; + +// #[derive(Clone, Debug, PartialEq)] +// enum Value { +// Uninit, +// Bool(bool), +// Int(i64), // FIXME(tsion): Should be bit-width aware. +// Pointer(Pointer), +// Adt { variant: usize, data_ptr: Pointer }, +// Func(def_id::DefId), +// } /// A stack frame: /// @@ -55,42 +145,43 @@ impl Pointer { /// | Aggregates | aggregates /// +-----------------------+ /// ``` -#[derive(Debug)] -struct Frame { - /// A pointer to a stack cell to write the return value of the current call, if it's not a - /// divering call. - return_ptr: Option, - - offset: usize, - num_args: usize, - num_vars: usize, - num_temps: usize, - num_aggregate_fields: usize, -} - -impl Frame { - fn size(&self) -> usize { - self.num_args + self.num_vars + self.num_temps + self.num_aggregate_fields - } - - fn arg_offset(&self, i: usize) -> usize { - self.offset + i - } - - fn var_offset(&self, i: usize) -> usize { - self.offset + self.num_args + i - } - - fn temp_offset(&self, i: usize) -> usize { - self.offset + self.num_args + self.num_vars + i - } -} +// #[derive(Debug)] +// struct Frame { +// /// A pointer to a stack cell to write the return value of the current call, if it's not a +// /// divering call. +// return_ptr: Option, + +// offset: usize, +// num_args: usize, +// num_vars: usize, +// num_temps: usize, +// num_aggregate_fields: usize, +// } + +// impl Frame { +// fn size(&self) -> usize { +// self.num_args + self.num_vars + self.num_temps + self.num_aggregate_fields +// } + +// fn arg_offset(&self, i: usize) -> usize { +// self.offset + i +// } + +// fn var_offset(&self, i: usize) -> usize { +// self.offset + self.num_args + i +// } + +// fn temp_offset(&self, i: usize) -> usize { +// self.offset + self.num_args + self.num_vars + i +// } +// } struct Interpreter<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, mir_map: &'a MirMap<'tcx>, - value_stack: Vec, - call_stack: Vec, + // value_stack: Vec, + // call_stack: Vec, + memory: memory::Memory, } impl<'a, 'tcx> Interpreter<'a, 'tcx> { @@ -98,47 +189,47 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Interpreter { tcx: tcx, mir_map: mir_map, - value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value. - call_stack: Vec::new(), + // value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value. + // call_stack: Vec::new(), + memory: memory::Memory::new(), } } - fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { - let frame = Frame { - return_ptr: return_ptr, - offset: self.value_stack.len(), - num_args: mir.arg_decls.len(), - num_vars: mir.var_decls.len(), - num_temps: mir.temp_decls.len(), - num_aggregate_fields: 0, - }; + // fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { + // let frame = Frame { + // return_ptr: return_ptr, + // offset: self.value_stack.len(), + // num_args: mir.arg_decls.len(), + // num_vars: mir.var_decls.len(), + // num_temps: mir.temp_decls.len(), + // num_aggregate_fields: 0, + // }; - self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); - - for (i, arg) in args.iter().enumerate() { - self.value_stack[frame.arg_offset(i)] = arg.clone(); - } + // self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); - self.call_stack.push(frame); + // for (i, arg) in args.iter().enumerate() { + // self.value_stack[frame.arg_offset(i)] = arg.clone(); + // } - } + // self.call_stack.push(frame); + // } - fn pop_stack_frame(&mut self) { - let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none"); - self.value_stack.truncate(frame.offset); - } + // fn pop_stack_frame(&mut self) { + // let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none"); + // self.value_stack.truncate(frame.offset); + // } - fn allocate_aggregate(&mut self, size: usize) -> Pointer { - let frame = self.call_stack.last_mut().expect("missing call frame"); - frame.num_aggregate_fields += size; + // fn allocate_aggregate(&mut self, size: usize) -> Pointer { + // let frame = self.call_stack.last_mut().expect("missing call frame"); + // frame.num_aggregate_fields += size; - let ptr = Pointer::Stack(self.value_stack.len()); - self.value_stack.extend(iter::repeat(Value::Uninit).take(size)); - ptr - } + // let ptr = Pointer::Stack(self.value_stack.len()); + // self.value_stack.extend(iter::repeat(Value::Uninit).take(size)); + // ptr + // } fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { - self.push_stack_frame(mir, args, return_ptr); + // self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; loop { @@ -150,9 +241,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match stmt.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - let ptr = self.eval_lvalue(lvalue); - let value = self.eval_rvalue(rvalue); - self.write_pointer(ptr, value); + let ptr = self.lvalue_to_ptr(lvalue); + self.eval_rvalue_into(rvalue, ptr); } } } @@ -163,60 +253,60 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Terminator::Return => break, mir::Terminator::Goto { target } => block = target, - mir::Terminator::Call { ref func, ref args, ref destination, .. } => { - let ptr = destination.as_ref().map(|&(ref lv, _)| self.eval_lvalue(lv)); - let func_val = self.eval_operand(func); - - if let Value::Func(def_id) = func_val { - let mir_data; - let mir = match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), - None => { - let cstore = &self.tcx.sess.cstore; - mir_data = cstore.maybe_get_item_mir(self.tcx, def_id).unwrap(); - &mir_data - } - }; - - let arg_vals: Vec = - args.iter().map(|arg| self.eval_operand(arg)).collect(); - - self.call(mir, &arg_vals, ptr); - - if let Some((_, target)) = *destination { - block = target; - } - } else { - panic!("tried to call a non-function value: {:?}", func_val); - } - } - - mir::Terminator::If { ref cond, targets: (then_target, else_target) } => { - match self.eval_operand(cond) { - Value::Bool(true) => block = then_target, - Value::Bool(false) => block = else_target, - cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), - } - } - - mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_val = self.read_lvalue(discr); - - let index = values.iter().position(|v| discr_val == self.eval_constant(v)) - .expect("discriminant matched no values"); - - block = targets[index]; - } - - mir::Terminator::Switch { ref discr, ref targets, .. } => { - let discr_val = self.read_lvalue(discr); - - if let Value::Adt { variant, .. } = discr_val { - block = targets[variant]; - } else { - panic!("Switch on non-Adt value: {:?}", discr_val); - } - } + // mir::Terminator::Call { ref func, ref args, ref destination, .. } => { + // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); + // let func_val = self.operand_to_ptr(func); + + // if let Value::Func(def_id) = func_val { + // let mir_data; + // let mir = match self.tcx.map.as_local_node_id(def_id) { + // Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), + // None => { + // let cstore = &self.tcx.sess.cstore; + // mir_data = cstore.maybe_get_item_mir(self.tcx, def_id).unwrap(); + // &mir_data + // } + // }; + + // let arg_vals: Vec = + // args.iter().map(|arg| self.operand_to_ptr(arg)).collect(); + + // self.call(mir, &arg_vals, ptr); + + // if let Some((_, target)) = *destination { + // block = target; + // } + // } else { + // panic!("tried to call a non-function value: {:?}", func_val); + // } + // } + + // mir::Terminator::If { ref cond, targets: (then_target, else_target) } => { + // match self.operand_to_ptr(cond) { + // Value::Bool(true) => block = then_target, + // Value::Bool(false) => block = else_target, + // cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), + // } + // } + + // mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => { + // let discr_val = self.read_lvalue(discr); + + // let index = values.iter().position(|v| discr_val == self.eval_constant(v)) + // .expect("discriminant matched no values"); + + // block = targets[index]; + // } + + // mir::Terminator::Switch { ref discr, ref targets, .. } => { + // let discr_val = self.read_lvalue(discr); + + // if let Value::Adt { variant, .. } = discr_val { + // block = targets[variant]; + // } else { + // panic!("Switch on non-Adt value: {:?}", discr_val); + // } + // } mir::Terminator::Drop { target, .. } => { // TODO: Handle destructors and dynamic drop. @@ -224,139 +314,159 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } mir::Terminator::Resume => unimplemented!(), + _ => unimplemented!(), } } - self.pop_stack_frame(); + // self.pop_stack_frame(); } - fn eval_lvalue(&self, lvalue: &mir::Lvalue) -> Pointer { - let frame = self.call_stack.last().expect("missing call frame"); - + fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> Pointer { match *lvalue { - mir::Lvalue::ReturnPointer => - frame.return_ptr.expect("ReturnPointer used in a function with no return value"), - mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), - mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), - mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), - - mir::Lvalue::Projection(ref proj) => { - let base_ptr = self.eval_lvalue(&proj.base); - - match proj.elem { - mir::ProjectionElem::Field(field, _) => { - base_ptr.offset(field.index()) - } - - mir::ProjectionElem::Downcast(_, variant) => { - let adt_val = self.read_pointer(base_ptr); - if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { - debug_assert_eq!(variant, actual_variant); - data_ptr - } else { - panic!("Downcast attempted on non-ADT: {:?}", adt_val) - } - } - - mir::ProjectionElem::Deref => { - let ptr_val = self.read_pointer(base_ptr); - if let Value::Pointer(ptr) = ptr_val { - ptr - } else { - panic!("Deref attempted on non-pointer: {:?}", ptr_val) - } - } - - mir::ProjectionElem::Index(ref _operand) => unimplemented!(), - mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), - } - } + mir::Lvalue::ReturnPointer => Pointer { + alloc_id: self::memory::alloc_id_hack(0), + offset: 0, + repr: Repr::Int, + }, _ => unimplemented!(), } + + // let frame = self.call_stack.last().expect("missing call frame"); + + // match *lvalue { + // mir::Lvalue::ReturnPointer => + // frame.return_ptr.expect("ReturnPointer used in a function with no return value"), + // mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), + // mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), + // mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), + + // mir::Lvalue::Projection(ref proj) => { + // let base_ptr = self.lvalue_to_ptr(&proj.base); + + // match proj.elem { + // mir::ProjectionElem::Field(field, _) => { + // base_ptr.offset(field.index()) + // } + + // mir::ProjectionElem::Downcast(_, variant) => { + // let adt_val = self.read_pointer(base_ptr); + // if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { + // debug_assert_eq!(variant, actual_variant); + // data_ptr + // } else { + // panic!("Downcast attempted on non-ADT: {:?}", adt_val) + // } + // } + + // mir::ProjectionElem::Deref => { + // let ptr_val = self.read_pointer(base_ptr); + // if let Value::Pointer(ptr) = ptr_val { + // ptr + // } else { + // panic!("Deref attempted on non-pointer: {:?}", ptr_val) + // } + // } + + // mir::ProjectionElem::Index(ref _operand) => unimplemented!(), + // mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), + // } + // } + + // _ => unimplemented!(), + // } } - fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Value, right: Value) -> Value { - match (left, right) { - (Value::Int(l), Value::Int(r)) => { - match bin_op { - mir::BinOp::Add => Value::Int(l + r), - mir::BinOp::Sub => Value::Int(l - r), - mir::BinOp::Mul => Value::Int(l * r), - mir::BinOp::Div => Value::Int(l / r), - mir::BinOp::Rem => Value::Int(l % r), - mir::BinOp::BitXor => Value::Int(l ^ r), - mir::BinOp::BitAnd => Value::Int(l & r), - mir::BinOp::BitOr => Value::Int(l | r), - mir::BinOp::Shl => Value::Int(l << r), - mir::BinOp::Shr => Value::Int(l >> r), - mir::BinOp::Eq => Value::Bool(l == r), - mir::BinOp::Lt => Value::Bool(l < r), - mir::BinOp::Le => Value::Bool(l <= r), - mir::BinOp::Ne => Value::Bool(l != r), - mir::BinOp::Ge => Value::Bool(l >= r), - mir::BinOp::Gt => Value::Bool(l > r), - } + fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, out: Pointer) { + match (left.repr, right.repr, out.repr) { + (Repr::Int, Repr::Int, Repr::Int) => { + let l = byteorder::NativeEndian::read_i64(&self.memory.value(left.alloc_id).unwrap().bytes); + let r = byteorder::NativeEndian::read_i64(&self.memory.value(right.alloc_id).unwrap().bytes); + let n = match bin_op { + mir::BinOp::Add => l + r, + mir::BinOp::Sub => l - r, + mir::BinOp::Mul => l * r, + mir::BinOp::Div => l / r, + mir::BinOp::Rem => l % r, + mir::BinOp::BitXor => l ^ r, + mir::BinOp::BitAnd => l & r, + mir::BinOp::BitOr => l | r, + mir::BinOp::Shl => l << r, + mir::BinOp::Shr => l >> r, + _ => unimplemented!(), + // mir::BinOp::Eq => Value::Bool(l == r), + // mir::BinOp::Lt => Value::Bool(l < r), + // mir::BinOp::Le => Value::Bool(l <= r), + // mir::BinOp::Ne => Value::Bool(l != r), + // mir::BinOp::Ge => Value::Bool(l >= r), + // mir::BinOp::Gt => Value::Bool(l > r), + }; + byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(out.alloc_id).unwrap().bytes, n); } _ => unimplemented!(), } } - fn eval_rvalue(&mut self, rvalue: &mir::Rvalue) -> Value { + fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, out: Pointer) { match *rvalue { - mir::Rvalue::Use(ref operand) => self.eval_operand(operand), + mir::Rvalue::Use(ref operand) => { + let ptr = self.operand_to_ptr(operand); + let val = self.read_pointer(ptr); + self.write_pointer(out, val); + } mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { - let left_val = self.eval_operand(left); - let right_val = self.eval_operand(right); - self.eval_binary_op(bin_op, left_val, right_val) + let left_ptr = self.operand_to_ptr(left); + let right_ptr = self.operand_to_ptr(right); + self.eval_binary_op(bin_op, left_ptr, right_ptr, out) } mir::Rvalue::UnaryOp(un_op, ref operand) => { - match (un_op, self.eval_operand(operand)) { - (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n), - (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n), - _ => unimplemented!(), - } + unimplemented!() + // match (un_op, self.operand_to_ptr(operand)) { + // (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n), + // (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n), + // _ => unimplemented!(), + // } } - mir::Rvalue::Ref(_region, _kind, ref lvalue) => { - Value::Pointer(self.eval_lvalue(lvalue)) - } + // mir::Rvalue::Ref(_region, _kind, ref lvalue) => { + // Value::Pointer(self.lvalue_to_ptr(lvalue)) + // } - mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), - ref operands) => { - let max_fields = adt_def.variants - .iter() - .map(|v| v.fields.len()) - .max() - .unwrap_or(0); + // mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), + // ref operands) => { + // let max_fields = adt_def.variants + // .iter() + // .map(|v| v.fields.len()) + // .max() + // .unwrap_or(0); - let ptr = self.allocate_aggregate(max_fields); + // let ptr = self.allocate_aggregate(max_fields); - for (i, operand) in operands.iter().enumerate() { - let val = self.eval_operand(operand); - self.write_pointer(ptr.offset(i), val); - } + // for (i, operand) in operands.iter().enumerate() { + // let val = self.operand_to_ptr(operand); + // self.write_pointer(ptr.offset(i), val); + // } - Value::Adt { variant: variant, data_ptr: ptr } - } + // Value::Adt { variant: variant, data_ptr: ptr } + // } ref r => panic!("can't handle rvalue: {:?}", r), } } - fn eval_operand(&mut self, op: &mir::Operand) -> Value { + fn operand_to_ptr(&mut self, op: &mir::Operand) -> Pointer { match *op { - mir::Operand::Consume(ref lvalue) => self.read_lvalue(lvalue), + mir::Operand::Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), mir::Operand::Constant(ref constant) => { match constant.literal { mir::Literal::Value { ref value } => self.eval_constant(value), mir::Literal::Item { def_id, kind, .. } => match kind { - mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), + // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), _ => panic!("can't handle item literal: {:?}", constant.literal), }, } @@ -364,14 +474,19 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn eval_constant(&self, const_val: &const_eval::ConstVal) -> Value { + fn eval_constant(&mut self, const_val: &const_eval::ConstVal) -> Pointer { match *const_val { const_eval::ConstVal::Float(_f) => unimplemented!(), - const_eval::ConstVal::Int(i) => Value::Int(i), + // const_eval::ConstVal::Int(i) => Value::new_int(i), + const_eval::ConstVal::Int(i) => Pointer { + alloc_id: self.memory.allocate_int(i), + offset: 0, + repr: Repr::Int, + }, const_eval::ConstVal::Uint(_u) => unimplemented!(), const_eval::ConstVal::Str(ref _s) => unimplemented!(), const_eval::ConstVal::ByteStr(ref _bs) => unimplemented!(), - const_eval::ConstVal::Bool(b) => Value::Bool(b), + const_eval::ConstVal::Bool(b) => unimplemented!(), const_eval::ConstVal::Struct(_node_id) => unimplemented!(), const_eval::ConstVal::Tuple(_node_id) => unimplemented!(), const_eval::ConstVal::Function(_def_id) => unimplemented!(), @@ -380,19 +495,19 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value { - self.read_pointer(self.eval_lvalue(lvalue)) - } + // fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value { + // self.read_pointer(self.lvalue_to_ptr(lvalue)) + // } fn read_pointer(&self, p: Pointer) -> Value { - match p { - Pointer::Stack(offset) => self.value_stack[offset].clone(), - } + self.memory.value(p.alloc_id).unwrap().clone() } fn write_pointer(&mut self, p: Pointer, val: Value) { - match p { - Pointer::Stack(offset) => self.value_stack[offset] = val, + // TODO(tsion): Remove panics. + let alloc = self.memory.value_mut(p.alloc_id).unwrap(); + for (i, byte) in val.bytes.into_iter().enumerate() { + alloc.bytes[p.offset + i] = byte; } } } @@ -406,10 +521,20 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> println!("Interpreting: {}", item.name); let mut interpreter = Interpreter::new(tcx, mir_map); - let return_ptr = Pointer::Stack(0); - interpreter.call(mir, &[], Some(return_ptr)); + let return_ptr = match mir.return_ty { + ty::FnOutput::FnConverging(ty) => { + let repr = Repr::from_ty(ty); + Some(Pointer { + alloc_id: interpreter.memory.allocate(repr.size()), + offset: 0, + repr: repr, + }) + } + ty::FnOutput::FnDiverging => None, + }; + interpreter.call(mir, &[], return_ptr.clone()); - let val_str = format!("{:?}", interpreter.read_pointer(return_ptr)); + let val_str = format!("{:?}", interpreter.read_pointer(return_ptr.unwrap())); if !check_expected(&val_str, attr) { println!("=> {}\n", val_str); } diff --git a/src/lib.rs b/src/lib.rs index da83e7db925ac..c7be3a0340cb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] +extern crate byteorder; extern crate rustc; extern crate rustc_mir; extern crate syntax; diff --git a/test/new_values.rs b/test/new_values.rs new file mode 100644 index 0000000000000..228aec8ca9d75 --- /dev/null +++ b/test/new_values.rs @@ -0,0 +1,176 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run(expected = "Int(1)")] +fn ret() -> i32 { + 1 +} + +// #[miri_run(expected = "Int(-1)")] +// fn neg() -> i32 { +// -1 +// } + +#[miri_run(expected = "Int(3)")] +fn add() -> i32 { + 1 + 2 +} + +// #[miri_run(expected = "Int(3)")] +// fn indirect_add() -> i32 { +// let x = 1; +// let y = 2; +// x + y +// } + +// #[miri_run(expected = "Int(25)")] +// fn arith() -> i32 { +// 3*3 + 4*4 +// } + +// #[miri_run(expected = "Int(0)")] +// fn if_false() -> i32 { +// if false { 1 } else { 0 } +// } + +// #[miri_run(expected = "Int(1)")] +// fn if_true() -> i32 { +// if true { 1 } else { 0 } +// } + +// #[miri_run(expected = "Int(2)")] +// fn call() -> i32 { +// fn increment(x: i32) -> i32 { +// x + 1 +// } + +// increment(1) +// } + +// // #[miri_run(expected = "Int(3628800)")] +// // fn factorial_loop() -> i32 { +// // let mut product = 1; +// // let mut i = 1; + +// // while i <= 10 { +// // product *= i; +// // i += 1; +// // } + +// // product +// // } + +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_recursive() -> i32 { +// fn fact(n: i32) -> i32 { +// if n == 0 { +// 1 +// } else { +// n * fact(n - 1) +// } +// } + +// fact(10) +// } + +// #[miri_run(expected = "Int(1)")] +// fn match_bool() -> i32 { +// let b = true; +// match b { +// true => 1, +// false => 0, +// } +// } + +// #[miri_run(expected = "Int(20)")] +// fn match_int() -> i32 { +// let n = 2; +// match n { +// 0 => 0, +// 1 => 10, +// 2 => 20, +// 3 => 30, +// _ => 100, +// } +// } + +// #[miri_run(expected = "Int(1)")] +// fn one_line_ref() -> i32 { +// *&1 +// } + +// #[miri_run(expected = "Int(1)")] +// fn basic_ref() -> i32 { +// let x = &1; +// *x +// } + +// #[miri_run(expected = "Int(3)")] +// fn basic_ref_mut() -> i32 { +// let x = &mut 1; +// *x += 2; +// *x +// } + +// // #[miri_run(expected = "Int(3)")] +// // fn basic_ref_mut_var() -> i32 { +// // let mut a = 1; +// // { +// // let x = &mut a; +// // *x += 2; +// // } +// // a +// // } + +// #[miri_run(expected = "Int(4)")] +// fn match_int_range() -> i32 { +// let n = 42; +// match n { +// 0...9 => 0, +// 10...19 => 1, +// 20...29 => 2, +// 30...39 => 3, +// 40...49 => 4, +// _ => 5, +// } +// } + +// enum MyOption { +// Some { data: T }, +// None, +// } + +// #[miri_run(expected = "Int(13)")] +// fn match_my_opt_some() -> i32 { +// let x = MyOption::Some { data: 13 }; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } + +// #[miri_run(expected = "Int(42)")] +// fn match_my_opt_none() -> i32 { +// let x = MyOption::None; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } + +// #[miri_run(expected = "Int(13)")] +// fn match_opt_some() -> i32 { +// let x = Some(13); +// match x { +// Some(data) => data, +// None => 42, +// } +// } + +// /// Test calling a very simple function from the standard library. +// #[miri_run(expected = "Int(1)")] +// fn cross_crate_fn_call() -> i32 { +// if 1i32.is_positive() { 1 } else { 0 } +// } + +// fn main() {} From 21f97a436b94865cf9afd8a75a52c694e3d5e57e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 27 Feb 2016 22:10:10 -0600 Subject: [PATCH 0047/1096] WIP: Rename eval_constant to const_to_ptr. --- src/interpreter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 01db533661ed3..e34aa952c4265 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -292,7 +292,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); - // let index = values.iter().position(|v| discr_val == self.eval_constant(v)) + // let index = values.iter().position(|v| discr_val == self.const_to_ptr(v)) // .expect("discriminant matched no values"); // block = targets[index]; @@ -463,7 +463,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Operand::Constant(ref constant) => { match constant.literal { - mir::Literal::Value { ref value } => self.eval_constant(value), + mir::Literal::Value { ref value } => self.const_to_ptr(value), mir::Literal::Item { def_id, kind, .. } => match kind { // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), @@ -474,7 +474,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn eval_constant(&mut self, const_val: &const_eval::ConstVal) -> Pointer { + fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> Pointer { match *const_val { const_eval::ConstVal::Float(_f) => unimplemented!(), // const_eval::ConstVal::Int(i) => Value::new_int(i), From 255927bc0c77d275ee176f47dd7a47001cc40b56 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 28 Feb 2016 00:49:27 -0600 Subject: [PATCH 0048/1096] WIP: Support unary integer ops again. --- src/interpreter.rs | 15 ++++++++------- test/new_values.rs | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e34aa952c4265..b4933365db809 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -423,12 +423,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } mir::Rvalue::UnaryOp(un_op, ref operand) => { - unimplemented!() - // match (un_op, self.operand_to_ptr(operand)) { - // (mir::UnOp::Not, Value::Int(n)) => Value::Int(!n), - // (mir::UnOp::Neg, Value::Int(n)) => Value::Int(-n), - // _ => unimplemented!(), - // } + let ptr = self.operand_to_ptr(operand); + let m = byteorder::NativeEndian::read_i64(&self.memory.value(ptr.alloc_id).unwrap().bytes); + let n = match (un_op, ptr.repr) { + (mir::UnOp::Not, Repr::Int) => !m, + (mir::UnOp::Neg, Repr::Int) => -m, + _ => unimplemented!(), + }; + byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(out.alloc_id).unwrap().bytes, n); } // mir::Rvalue::Ref(_region, _kind, ref lvalue) => { @@ -477,7 +479,6 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> Pointer { match *const_val { const_eval::ConstVal::Float(_f) => unimplemented!(), - // const_eval::ConstVal::Int(i) => Value::new_int(i), const_eval::ConstVal::Int(i) => Pointer { alloc_id: self.memory.allocate_int(i), offset: 0, diff --git a/test/new_values.rs b/test/new_values.rs index 228aec8ca9d75..160840138ec1d 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -6,10 +6,10 @@ fn ret() -> i32 { 1 } -// #[miri_run(expected = "Int(-1)")] -// fn neg() -> i32 { -// -1 -// } +#[miri_run(expected = "Int(-1)")] +fn neg() -> i32 { + -1 +} #[miri_run(expected = "Int(3)")] fn add() -> i32 { From 83fbfb95c03d2cb3dee8b1adf9e24687b2cdf99a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 28 Feb 2016 01:07:03 -0600 Subject: [PATCH 0049/1096] WIP: Add EvalError/EvalResult and apply throughout. --- src/interpreter.rs | 60 +++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b4933365db809..5284cd62998af 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -8,11 +8,12 @@ use rustc::middle::cstore::CrateStore; use rustc::mir::repr::{self as mir, Mir}; use rustc::mir::mir_map::MirMap; use std::collections::HashMap; +use std::error::Error; +use std::fmt; +use std::iter; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; -use std::iter; - const TRACE_EXECUTION: bool = true; mod memory { @@ -113,6 +114,27 @@ mod memory { } use self::memory::{Pointer, Repr, Value}; +#[derive(Clone, Debug)] +pub struct EvalError; + +pub type EvalResult = Result; + +impl Error for EvalError { + fn description(&self) -> &str { + "error during MIR evaluation" + } + + fn cause(&self) -> Option<&Error> { + None + } +} + +impl fmt::Display for EvalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + // #[derive(Clone, Debug, PartialEq)] // enum Value { // Uninit, @@ -228,7 +250,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // ptr // } - fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { + fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) -> EvalResult<()> { // self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; @@ -241,8 +263,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match stmt.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - let ptr = self.lvalue_to_ptr(lvalue); - self.eval_rvalue_into(rvalue, ptr); + let ptr = try!(self.lvalue_to_ptr(lvalue)); + try!(self.eval_rvalue_into(rvalue, ptr)); } } } @@ -319,10 +341,12 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } // self.pop_stack_frame(); + + Ok(()) } - fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> Pointer { - match *lvalue { + fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> EvalResult { + let ptr = match *lvalue { mir::Lvalue::ReturnPointer => Pointer { alloc_id: self::memory::alloc_id_hack(0), offset: 0, @@ -330,7 +354,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { }, _ => unimplemented!(), - } + }; + + Ok(ptr) // let frame = self.call_stack.last().expect("missing call frame"); @@ -408,22 +434,22 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, out: Pointer) { + fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, out: Pointer) -> EvalResult<()> { match *rvalue { mir::Rvalue::Use(ref operand) => { - let ptr = self.operand_to_ptr(operand); + let ptr = try!(self.operand_to_ptr(operand)); let val = self.read_pointer(ptr); self.write_pointer(out, val); } mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { - let left_ptr = self.operand_to_ptr(left); - let right_ptr = self.operand_to_ptr(right); + let left_ptr = try!(self.operand_to_ptr(left)); + let right_ptr = try!(self.operand_to_ptr(right)); self.eval_binary_op(bin_op, left_ptr, right_ptr, out) } mir::Rvalue::UnaryOp(un_op, ref operand) => { - let ptr = self.operand_to_ptr(operand); + let ptr = try!(self.operand_to_ptr(operand)); let m = byteorder::NativeEndian::read_i64(&self.memory.value(ptr.alloc_id).unwrap().bytes); let n = match (un_op, ptr.repr) { (mir::UnOp::Not, Repr::Int) => !m, @@ -457,15 +483,17 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { ref r => panic!("can't handle rvalue: {:?}", r), } + + Ok(()) } - fn operand_to_ptr(&mut self, op: &mir::Operand) -> Pointer { + fn operand_to_ptr(&mut self, op: &mir::Operand) -> EvalResult { match *op { mir::Operand::Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), mir::Operand::Constant(ref constant) => { match constant.literal { - mir::Literal::Value { ref value } => self.const_to_ptr(value), + mir::Literal::Value { ref value } => Ok(self.const_to_ptr(value)), mir::Literal::Item { def_id, kind, .. } => match kind { // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), @@ -533,7 +561,7 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> } ty::FnOutput::FnDiverging => None, }; - interpreter.call(mir, &[], return_ptr.clone()); + interpreter.call(mir, &[], return_ptr.clone()).unwrap(); let val_str = format!("{:?}", interpreter.read_pointer(return_ptr.unwrap())); if !check_expected(&val_str, attr) { From 53403ee8bd9d9fde1a025dba5e88392cfdbc6035 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 4 Mar 2016 23:17:31 -0600 Subject: [PATCH 0050/1096] WIP: Add some support for tuples. Optimize memory copies. --- src/interpreter.rs | 181 +++++++++++++++++++++++++++------------------ test/new_values.rs | 18 +++++ 2 files changed, 129 insertions(+), 70 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5284cd62998af..c031876753616 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -3,8 +3,10 @@ use byteorder; use byteorder::ByteOrder; -use rustc::middle::{const_eval, def_id, ty}; +use rustc::middle::const_eval; +use rustc::middle::def_id; use rustc::middle::cstore::CrateStore; +use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::repr::{self as mir, Mir}; use rustc::mir::mir_map::MirMap; use std::collections::HashMap; @@ -22,6 +24,9 @@ mod memory { use rustc::middle::ty; use std::collections::HashMap; use std::mem; + use std::ops::Add; + use std::ptr; + use super::{EvalError, EvalResult}; pub struct Memory { next_id: u64, @@ -36,7 +41,8 @@ mod memory { AllocId(i) } - // TODO(tsion): Shouldn't clone values. + // TODO(tsion): Shouldn't clone Values. (Audit the rest of the code.) + // TODO(tsion): Rename to Allocation. #[derive(Clone, Debug)] pub struct Value { pub bytes: Vec, @@ -51,13 +57,19 @@ mod memory { pub repr: Repr, } + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct FieldRepr { + pub offset: usize, + pub repr: Repr, + } + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { Int, - - StackFrame { - locals: Vec, - } + Aggregate { + size: usize, + fields: Vec, + }, } impl Memory { @@ -65,7 +77,7 @@ mod memory { Memory { next_id: 0, alloc_map: HashMap::new() } } - pub fn allocate(&mut self, size: usize) -> AllocId { + pub fn allocate_raw(&mut self, size: usize) -> AllocId { let id = AllocId(self.next_id); let val = Value { bytes: vec![0; size] }; self.alloc_map.insert(self.next_id, val); @@ -73,24 +85,52 @@ mod memory { id } + pub fn allocate(&mut self, repr: Repr) -> Pointer { + Pointer { + alloc_id: self.allocate_raw(repr.size()), + offset: 0, + repr: repr, + } + } + pub fn allocate_int(&mut self, n: i64) -> AllocId { - let id = self.allocate(mem::size_of::()); + let id = self.allocate_raw(mem::size_of::()); byteorder::NativeEndian::write_i64(&mut self.value_mut(id).unwrap().bytes, n); id } - pub fn value(&self, id: AllocId) -> Option<&Value> { - self.alloc_map.get(&id.0) + pub fn value(&self, id: AllocId) -> EvalResult<&Value> { + self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) } - pub fn value_mut(&mut self, id: AllocId) -> Option<&mut Value> { - self.alloc_map.get_mut(&id.0) + pub fn value_mut(&mut self, id: AllocId) -> EvalResult<&mut Value> { + self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) + } + + pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { + let src_bytes = try!(self.value_mut(src.alloc_id)) + .bytes[src.offset..src.offset + size].as_mut_ptr(); + let dest_bytes = try!(self.value_mut(dest.alloc_id)) + .bytes[dest.offset..dest.offset + size].as_mut_ptr(); + + // SAFE: The above indexing would have panicked if there weren't at least `size` bytes + // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and + // `dest` could possibly overlap. + unsafe { + if src.alloc_id == dest.alloc_id { + ptr::copy(src_bytes, dest_bytes, size); + } else { + ptr::copy_nonoverlapping(src_bytes, dest_bytes, size); + } + } + + Ok(()) } } impl Pointer { - pub fn offset(self, i: usize) -> Self { - Pointer { offset: self.offset + i, ..self } + pub fn offset(&self, i: usize) -> Self { + Pointer { offset: self.offset + i, ..self.clone() } } } @@ -99,6 +139,18 @@ mod memory { pub fn from_ty(ty: ty::Ty) -> Self { match ty.sty { ty::TyInt(_) => Repr::Int, + + ty::TyTuple(ref fields) => { + let mut size = 0; + let fields = fields.iter().map(|ty| { + let repr = Repr::from_ty(ty); + let old_size = size; + size += repr.size(); + FieldRepr { offset: old_size, repr: repr } + }).collect(); + Repr::Aggregate { size: size, fields: fields } + }, + _ => unimplemented!(), } } @@ -106,8 +158,7 @@ mod memory { pub fn size(&self) -> usize { match *self { Repr::Int => 8, - Repr::StackFrame { ref locals } => - locals.iter().map(Repr::size).fold(0, |a, b| a + b) + Repr::Aggregate { size, .. } => size, } } } @@ -115,7 +166,9 @@ mod memory { use self::memory::{Pointer, Repr, Value}; #[derive(Clone, Debug)] -pub struct EvalError; +pub enum EvalError { + DanglingPointerDeref +} pub type EvalResult = Result; @@ -199,21 +252,23 @@ impl fmt::Display for EvalError { // } struct Interpreter<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, + tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, // value_stack: Vec, // call_stack: Vec, memory: memory::Memory, + return_ptr: Option, } impl<'a, 'tcx> Interpreter<'a, 'tcx> { - fn new(tcx: &'a ty::ctxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { + fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { tcx: tcx, mir_map: mir_map, // value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value. // call_stack: Vec::new(), memory: memory::Memory::new(), + return_ptr: None, } } @@ -251,6 +306,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) -> EvalResult<()> { + self.return_ptr = return_ptr; // self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; @@ -264,7 +320,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match stmt.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { let ptr = try!(self.lvalue_to_ptr(lvalue)); - try!(self.eval_rvalue_into(rvalue, ptr)); + try!(self.eval_rvalue_into(rvalue, &ptr)); } } } @@ -347,12 +403,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> EvalResult { let ptr = match *lvalue { - mir::Lvalue::ReturnPointer => Pointer { - alloc_id: self::memory::alloc_id_hack(0), - offset: 0, - repr: Repr::Int, - }, - + mir::Lvalue::ReturnPointer => + self.return_ptr.clone().expect("fn has no return pointer"), _ => unimplemented!(), }; @@ -403,9 +455,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } } - fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, out: Pointer) { - match (left.repr, right.repr, out.repr) { - (Repr::Int, Repr::Int, Repr::Int) => { + fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) { + match (left.repr, right.repr, &dest.repr) { + (Repr::Int, Repr::Int, &Repr::Int) => { let l = byteorder::NativeEndian::read_i64(&self.memory.value(left.alloc_id).unwrap().bytes); let r = byteorder::NativeEndian::read_i64(&self.memory.value(right.alloc_id).unwrap().bytes); let n = match bin_op { @@ -427,25 +479,24 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // mir::BinOp::Ge => Value::Bool(l >= r), // mir::BinOp::Gt => Value::Bool(l > r), }; - byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(out.alloc_id).unwrap().bytes, n); + byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(dest.alloc_id).unwrap().bytes, n); } - - _ => unimplemented!(), + (ref l, ref r, ref o) => + panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), } } - fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, out: Pointer) -> EvalResult<()> { + fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, dest: &Pointer) -> EvalResult<()> { match *rvalue { mir::Rvalue::Use(ref operand) => { - let ptr = try!(self.operand_to_ptr(operand)); - let val = self.read_pointer(ptr); - self.write_pointer(out, val); + let src = try!(self.operand_to_ptr(operand)); + try!(self.memory.copy(&src, dest, src.repr.size())); } mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { let left_ptr = try!(self.operand_to_ptr(left)); let right_ptr = try!(self.operand_to_ptr(right)); - self.eval_binary_op(bin_op, left_ptr, right_ptr, out) + self.eval_binary_op(bin_op, left_ptr, right_ptr, dest); } mir::Rvalue::UnaryOp(un_op, ref operand) => { @@ -454,9 +505,23 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { let n = match (un_op, ptr.repr) { (mir::UnOp::Not, Repr::Int) => !m, (mir::UnOp::Neg, Repr::Int) => -m, - _ => unimplemented!(), + (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), }; - byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(out.alloc_id).unwrap().bytes, n); + byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(dest.alloc_id).unwrap().bytes, n); + } + + mir::Rvalue::Aggregate(mir::AggregateKind::Tuple, ref operands) => { + match dest.repr { + Repr::Aggregate { ref fields, .. } => { + for (field, operand) in fields.iter().zip(operands) { + let src = try!(self.operand_to_ptr(operand)); + try!(self.memory.copy(&src, &dest.offset(field.offset), src.repr.size())); + } + } + + _ => panic!("attempted to write tuple rvalue '{:?}' into non-aggregate pointer '{:?}'", + rvalue, dest) + } } // mir::Rvalue::Ref(_region, _kind, ref lvalue) => { @@ -523,25 +588,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { const_eval::ConstVal::Repeat(_, _) => unimplemented!(), } } - - // fn read_lvalue(&self, lvalue: &mir::Lvalue) -> Value { - // self.read_pointer(self.lvalue_to_ptr(lvalue)) - // } - - fn read_pointer(&self, p: Pointer) -> Value { - self.memory.value(p.alloc_id).unwrap().clone() - } - - fn write_pointer(&mut self, p: Pointer, val: Value) { - // TODO(tsion): Remove panics. - let alloc = self.memory.value_mut(p.alloc_id).unwrap(); - for (i, byte) in val.bytes.into_iter().enumerate() { - alloc.bytes[p.offset + i] = byte; - } - } } -pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx>) { +pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { if attr.check_name("miri_run") { @@ -549,23 +598,15 @@ pub fn interpret_start_points<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &MirMap<'tcx> println!("Interpreting: {}", item.name); - let mut interpreter = Interpreter::new(tcx, mir_map); + let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { - ty::FnOutput::FnConverging(ty) => { - let repr = Repr::from_ty(ty); - Some(Pointer { - alloc_id: interpreter.memory.allocate(repr.size()), - offset: 0, - repr: repr, - }) - } - ty::FnOutput::FnDiverging => None, + ty::FnConverging(ty) => Some(miri.memory.allocate(Repr::from_ty(ty))), + ty::FnDiverging => None, }; - interpreter.call(mir, &[], return_ptr.clone()).unwrap(); + miri.call(mir, &[], return_ptr.clone()).unwrap(); - let val_str = format!("{:?}", interpreter.read_pointer(return_ptr.unwrap())); - if !check_expected(&val_str, attr) { - println!("=> {}\n", val_str); + if let Some(ret) = return_ptr { + println!("Returned: {:?}\n", miri.memory.value(ret.alloc_id).unwrap()); } } } diff --git a/test/new_values.rs b/test/new_values.rs index 160840138ec1d..3e5f53af589a4 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -16,6 +16,24 @@ fn add() -> i32 { 1 + 2 } +#[miri_run(expected = "Int(3)")] +fn empty() {} + +#[miri_run(expected = "Int(3)")] +fn tuple() -> (i32,) { + (1,) +} + +#[miri_run(expected = "Int(3)")] +fn tuple_2() -> (i32, i32) { + (1, 2) +} + +#[miri_run(expected = "Int(3)")] +fn tuple_5() -> (i32, i32, i32, i32, i32) { + (1, 2, 3, 4, 5) +} + // #[miri_run(expected = "Int(3)")] // fn indirect_add() -> i32 { // let x = 1; From 3d9a91d0f704dbc61afe30b6a0b2a0f158383ce5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 4 Mar 2016 23:22:37 -0600 Subject: [PATCH 0051/1096] Remove alloc_id_hack. --- src/interpreter.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c031876753616..4ef85dad3fca1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -36,11 +36,6 @@ mod memory { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); - // TODO(tsion): Remove this hack. - pub fn alloc_id_hack(i: u64) -> AllocId { - AllocId(i) - } - // TODO(tsion): Shouldn't clone Values. (Audit the rest of the code.) // TODO(tsion): Rename to Allocation. #[derive(Clone, Debug)] From 45677b453c191124138950f20d11b52a9a9407f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 4 Mar 2016 23:44:49 -0600 Subject: [PATCH 0052/1096] Rename memory "values" to "allocations". --- src/interpreter.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 4ef85dad3fca1..bc3ce81bdae2d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -30,16 +30,15 @@ mod memory { pub struct Memory { next_id: u64, - alloc_map: HashMap, + alloc_map: HashMap, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); - // TODO(tsion): Shouldn't clone Values. (Audit the rest of the code.) - // TODO(tsion): Rename to Allocation. + // TODO(tsion): Shouldn't clone Allocation. (Audit the rest of the code.) #[derive(Clone, Debug)] - pub struct Value { + pub struct Allocation { pub bytes: Vec, // TODO(tsion): relocations // TODO(tsion): undef mask @@ -74,8 +73,8 @@ mod memory { pub fn allocate_raw(&mut self, size: usize) -> AllocId { let id = AllocId(self.next_id); - let val = Value { bytes: vec![0; size] }; - self.alloc_map.insert(self.next_id, val); + let alloc = Allocation { bytes: vec![0; size] }; + self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; id } @@ -90,22 +89,22 @@ mod memory { pub fn allocate_int(&mut self, n: i64) -> AllocId { let id = self.allocate_raw(mem::size_of::()); - byteorder::NativeEndian::write_i64(&mut self.value_mut(id).unwrap().bytes, n); + byteorder::NativeEndian::write_i64(&mut self.get_mut(id).unwrap().bytes, n); id } - pub fn value(&self, id: AllocId) -> EvalResult<&Value> { + pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) } - pub fn value_mut(&mut self, id: AllocId) -> EvalResult<&mut Value> { + pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { - let src_bytes = try!(self.value_mut(src.alloc_id)) + let src_bytes = try!(self.get_mut(src.alloc_id)) .bytes[src.offset..src.offset + size].as_mut_ptr(); - let dest_bytes = try!(self.value_mut(dest.alloc_id)) + let dest_bytes = try!(self.get_mut(dest.alloc_id)) .bytes[dest.offset..dest.offset + size].as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes @@ -158,7 +157,7 @@ mod memory { } } } -use self::memory::{Pointer, Repr, Value}; +use self::memory::{Pointer, Repr, Allocation}; #[derive(Clone, Debug)] pub enum EvalError { @@ -300,7 +299,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // ptr // } - fn call(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) -> EvalResult<()> { + fn call(&mut self, mir: &Mir, args: &[Allocation], return_ptr: Option) -> EvalResult<()> { self.return_ptr = return_ptr; // self.push_stack_frame(mir, args, return_ptr); let mut block = mir::START_BLOCK; @@ -453,8 +452,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) { match (left.repr, right.repr, &dest.repr) { (Repr::Int, Repr::Int, &Repr::Int) => { - let l = byteorder::NativeEndian::read_i64(&self.memory.value(left.alloc_id).unwrap().bytes); - let r = byteorder::NativeEndian::read_i64(&self.memory.value(right.alloc_id).unwrap().bytes); + let l = byteorder::NativeEndian::read_i64(&self.memory.get(left.alloc_id).unwrap().bytes); + let r = byteorder::NativeEndian::read_i64(&self.memory.get(right.alloc_id).unwrap().bytes); let n = match bin_op { mir::BinOp::Add => l + r, mir::BinOp::Sub => l - r, @@ -474,7 +473,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // mir::BinOp::Ge => Value::Bool(l >= r), // mir::BinOp::Gt => Value::Bool(l > r), }; - byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(dest.alloc_id).unwrap().bytes, n); + byteorder::NativeEndian::write_i64(&mut self.memory.get_mut(dest.alloc_id).unwrap().bytes, n); } (ref l, ref r, ref o) => panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), @@ -496,13 +495,13 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Rvalue::UnaryOp(un_op, ref operand) => { let ptr = try!(self.operand_to_ptr(operand)); - let m = byteorder::NativeEndian::read_i64(&self.memory.value(ptr.alloc_id).unwrap().bytes); + let m = byteorder::NativeEndian::read_i64(&self.memory.get(ptr.alloc_id).unwrap().bytes); let n = match (un_op, ptr.repr) { (mir::UnOp::Not, Repr::Int) => !m, (mir::UnOp::Neg, Repr::Int) => -m, (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), }; - byteorder::NativeEndian::write_i64(&mut self.memory.value_mut(dest.alloc_id).unwrap().bytes, n); + byteorder::NativeEndian::write_i64(&mut self.memory.get_mut(dest.alloc_id).unwrap().bytes, n); } mir::Rvalue::Aggregate(mir::AggregateKind::Tuple, ref operands) => { @@ -601,7 +600,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) miri.call(mir, &[], return_ptr.clone()).unwrap(); if let Some(ret) = return_ptr { - println!("Returned: {:?}\n", miri.memory.value(ret.alloc_id).unwrap()); + println!("Returned: {:?}\n", miri.memory.get(ret.alloc_id).unwrap()); } } } From 6686944ded5a3a2a436ddcdb2da5b39ea9debf9d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 5 Mar 2016 00:23:37 -0600 Subject: [PATCH 0053/1096] Refactor memory range access error checnking. --- src/interpreter.rs | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bc3ce81bdae2d..7390d5bb8cf32 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -101,11 +101,21 @@ mod memory { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } + fn get_bytes(&self, ptr: &Pointer, size: usize) -> EvalResult<&[u8]> { + let alloc = try!(self.get(ptr.alloc_id)); + try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) + } + + fn get_bytes_mut(&mut self, ptr: &Pointer, size: usize) -> EvalResult<&mut [u8]> { + let alloc = try!(self.get_mut(ptr.alloc_id)); + try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) + } + pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { - let src_bytes = try!(self.get_mut(src.alloc_id)) - .bytes[src.offset..src.offset + size].as_mut_ptr(); - let dest_bytes = try!(self.get_mut(dest.alloc_id)) - .bytes[dest.offset..dest.offset + size].as_mut_ptr(); + let src_bytes = try!(self.get_bytes_mut(src, size)).as_mut_ptr(); + let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and @@ -122,6 +132,15 @@ mod memory { } } + impl Allocation { + fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { + if start >= self.bytes.len() || end > self.bytes.len() { + return Err(EvalError::PointerOutOfBounds); + } + Ok(()) + } + } + impl Pointer { pub fn offset(&self, i: usize) -> Self { Pointer { offset: self.offset + i, ..self.clone() } @@ -161,19 +180,21 @@ use self::memory::{Pointer, Repr, Allocation}; #[derive(Clone, Debug)] pub enum EvalError { - DanglingPointerDeref + DanglingPointerDeref, + PointerOutOfBounds, } pub type EvalResult = Result; impl Error for EvalError { fn description(&self) -> &str { - "error during MIR evaluation" + match *self { + EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", + EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", + } } - fn cause(&self) -> Option<&Error> { - None - } + fn cause(&self) -> Option<&Error> { None } } impl fmt::Display for EvalError { From a29a6e0db9f2cd702c8bf3157234e5410c905c70 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 5 Mar 2016 00:45:54 -0600 Subject: [PATCH 0054/1096] Refactor integer reading and writing. --- src/interpreter.rs | 53 +++++++++++++++++++++++++--------------------- test/new_values.rs | 14 ++++++------ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 7390d5bb8cf32..9dc35703c7b30 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -87,12 +87,6 @@ mod memory { } } - pub fn allocate_int(&mut self, n: i64) -> AllocId { - let id = self.allocate_raw(mem::size_of::()); - byteorder::NativeEndian::write_i64(&mut self.get_mut(id).unwrap().bytes, n); - id - } - pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) } @@ -130,6 +124,16 @@ mod memory { Ok(()) } + + pub fn read_int(&self, ptr: &Pointer) -> EvalResult { + let bytes = try!(self.get_bytes(ptr, Repr::Int.size())); + Ok(byteorder::NativeEndian::read_i64(bytes)) + } + + pub fn write_int(&mut self, ptr: &Pointer, n: i64) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); + Ok(byteorder::NativeEndian::write_i64(bytes, n)) + } } impl Allocation { @@ -170,7 +174,7 @@ mod memory { pub fn size(&self) -> usize { match *self { - Repr::Int => 8, + Repr::Int => mem::size_of::(), Repr::Aggregate { size, .. } => size, } } @@ -470,11 +474,12 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } } - fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) { - match (left.repr, right.repr, &dest.repr) { - (Repr::Int, Repr::Int, &Repr::Int) => { - let l = byteorder::NativeEndian::read_i64(&self.memory.get(left.alloc_id).unwrap().bytes); - let r = byteorder::NativeEndian::read_i64(&self.memory.get(right.alloc_id).unwrap().bytes); + fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) + -> EvalResult<()> { + match (&left.repr, &right.repr, &dest.repr) { + (&Repr::Int, &Repr::Int, &Repr::Int) => { + let l = try!(self.memory.read_int(&left)); + let r = try!(self.memory.read_int(&right)); let n = match bin_op { mir::BinOp::Add => l + r, mir::BinOp::Sub => l - r, @@ -494,9 +499,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // mir::BinOp::Ge => Value::Bool(l >= r), // mir::BinOp::Gt => Value::Bool(l > r), }; - byteorder::NativeEndian::write_i64(&mut self.memory.get_mut(dest.alloc_id).unwrap().bytes, n); + self.memory.write_int(dest, n) } - (ref l, ref r, ref o) => + (l, r, o) => panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), } } @@ -511,18 +516,18 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { let left_ptr = try!(self.operand_to_ptr(left)); let right_ptr = try!(self.operand_to_ptr(right)); - self.eval_binary_op(bin_op, left_ptr, right_ptr, dest); + try!(self.eval_binary_op(bin_op, left_ptr, right_ptr, dest)); } mir::Rvalue::UnaryOp(un_op, ref operand) => { let ptr = try!(self.operand_to_ptr(operand)); - let m = byteorder::NativeEndian::read_i64(&self.memory.get(ptr.alloc_id).unwrap().bytes); + let m = try!(self.memory.read_int(&ptr)); let n = match (un_op, ptr.repr) { (mir::UnOp::Not, Repr::Int) => !m, (mir::UnOp::Neg, Repr::Int) => -m, (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), }; - byteorder::NativeEndian::write_i64(&mut self.memory.get_mut(dest.alloc_id).unwrap().bytes, n); + try!(self.memory.write_int(dest, n)); } mir::Rvalue::Aggregate(mir::AggregateKind::Tuple, ref operands) => { @@ -573,7 +578,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { mir::Operand::Constant(ref constant) => { match constant.literal { - mir::Literal::Value { ref value } => Ok(self.const_to_ptr(value)), + mir::Literal::Value { ref value } => self.const_to_ptr(value), mir::Literal::Item { def_id, kind, .. } => match kind { // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), @@ -584,14 +589,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> Pointer { + fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { match *const_val { const_eval::ConstVal::Float(_f) => unimplemented!(), - const_eval::ConstVal::Int(i) => Pointer { - alloc_id: self.memory.allocate_int(i), - offset: 0, - repr: Repr::Int, - }, + const_eval::ConstVal::Int(n) => { + let ptr = self.memory.allocate(Repr::Int); + try!(self.memory.write_int(&ptr, n)); + Ok(ptr) + } const_eval::ConstVal::Uint(_u) => unimplemented!(), const_eval::ConstVal::Str(ref _s) => unimplemented!(), const_eval::ConstVal::ByteStr(ref _bs) => unimplemented!(), diff --git a/test/new_values.rs b/test/new_values.rs index 3e5f53af589a4..68f639b9965e6 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -1,35 +1,35 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -#[miri_run(expected = "Int(1)")] +#[miri_run] fn ret() -> i32 { 1 } -#[miri_run(expected = "Int(-1)")] +#[miri_run] fn neg() -> i32 { -1 } -#[miri_run(expected = "Int(3)")] +#[miri_run] fn add() -> i32 { 1 + 2 } -#[miri_run(expected = "Int(3)")] +#[miri_run] fn empty() {} -#[miri_run(expected = "Int(3)")] +#[miri_run] fn tuple() -> (i32,) { (1,) } -#[miri_run(expected = "Int(3)")] +#[miri_run] fn tuple_2() -> (i32, i32) { (1, 2) } -#[miri_run(expected = "Int(3)")] +#[miri_run] fn tuple_5() -> (i32, i32, i32, i32, i32) { (1, 2, 3, 4, 5) } From 7c5ea621560d55b00f046b2b474bb7ce554a4d73 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 5 Mar 2016 00:48:23 -0600 Subject: [PATCH 0055/1096] Move memory module to its own file. --- src/interpreter.rs | 166 +-------------------------------------------- src/lib.rs | 1 + src/memory.rs | 160 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 164 deletions(-) create mode 100644 src/memory.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 9dc35703c7b30..2d97de1032d68 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -16,171 +16,9 @@ use std::iter; use syntax::ast::Attribute; use syntax::attr::AttrMetaMethods; -const TRACE_EXECUTION: bool = true; - -mod memory { - use byteorder; - use byteorder::ByteOrder; - use rustc::middle::ty; - use std::collections::HashMap; - use std::mem; - use std::ops::Add; - use std::ptr; - use super::{EvalError, EvalResult}; - - pub struct Memory { - next_id: u64, - alloc_map: HashMap, - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub struct AllocId(u64); - - // TODO(tsion): Shouldn't clone Allocation. (Audit the rest of the code.) - #[derive(Clone, Debug)] - pub struct Allocation { - pub bytes: Vec, - // TODO(tsion): relocations - // TODO(tsion): undef mask - } - - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct Pointer { - pub alloc_id: AllocId, - pub offset: usize, - pub repr: Repr, - } - - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct FieldRepr { - pub offset: usize, - pub repr: Repr, - } - - #[derive(Clone, Debug, PartialEq, Eq)] - pub enum Repr { - Int, - Aggregate { - size: usize, - fields: Vec, - }, - } - - impl Memory { - pub fn new() -> Self { - Memory { next_id: 0, alloc_map: HashMap::new() } - } - - pub fn allocate_raw(&mut self, size: usize) -> AllocId { - let id = AllocId(self.next_id); - let alloc = Allocation { bytes: vec![0; size] }; - self.alloc_map.insert(self.next_id, alloc); - self.next_id += 1; - id - } - - pub fn allocate(&mut self, repr: Repr) -> Pointer { - Pointer { - alloc_id: self.allocate_raw(repr.size()), - offset: 0, - repr: repr, - } - } - - pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { - self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) - } - - pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { - self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) - } - - fn get_bytes(&self, ptr: &Pointer, size: usize) -> EvalResult<&[u8]> { - let alloc = try!(self.get(ptr.alloc_id)); - try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); - Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) - } - - fn get_bytes_mut(&mut self, ptr: &Pointer, size: usize) -> EvalResult<&mut [u8]> { - let alloc = try!(self.get_mut(ptr.alloc_id)); - try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); - Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) - } - - pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { - let src_bytes = try!(self.get_bytes_mut(src, size)).as_mut_ptr(); - let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); +use memory::{self, Pointer, Repr, Allocation}; - // SAFE: The above indexing would have panicked if there weren't at least `size` bytes - // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and - // `dest` could possibly overlap. - unsafe { - if src.alloc_id == dest.alloc_id { - ptr::copy(src_bytes, dest_bytes, size); - } else { - ptr::copy_nonoverlapping(src_bytes, dest_bytes, size); - } - } - - Ok(()) - } - - pub fn read_int(&self, ptr: &Pointer) -> EvalResult { - let bytes = try!(self.get_bytes(ptr, Repr::Int.size())); - Ok(byteorder::NativeEndian::read_i64(bytes)) - } - - pub fn write_int(&mut self, ptr: &Pointer, n: i64) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); - Ok(byteorder::NativeEndian::write_i64(bytes, n)) - } - } - - impl Allocation { - fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { - if start >= self.bytes.len() || end > self.bytes.len() { - return Err(EvalError::PointerOutOfBounds); - } - Ok(()) - } - } - - impl Pointer { - pub fn offset(&self, i: usize) -> Self { - Pointer { offset: self.offset + i, ..self.clone() } - } - } - - impl Repr { - // TODO(tsion): Cache these outputs. - pub fn from_ty(ty: ty::Ty) -> Self { - match ty.sty { - ty::TyInt(_) => Repr::Int, - - ty::TyTuple(ref fields) => { - let mut size = 0; - let fields = fields.iter().map(|ty| { - let repr = Repr::from_ty(ty); - let old_size = size; - size += repr.size(); - FieldRepr { offset: old_size, repr: repr } - }).collect(); - Repr::Aggregate { size: size, fields: fields } - }, - - _ => unimplemented!(), - } - } - - pub fn size(&self) -> usize { - match *self { - Repr::Int => mem::size_of::(), - Repr::Aggregate { size, .. } => size, - } - } - } -} -use self::memory::{Pointer, Repr, Allocation}; +const TRACE_EXECUTION: bool = true; #[derive(Clone, Debug)] pub enum EvalError { diff --git a/src/lib.rs b/src/lib.rs index c7be3a0340cb9..036b87c2c7f69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ extern crate rustc_mir; extern crate syntax; pub mod interpreter; +mod memory; diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000000000..0fe2b735671b0 --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,160 @@ +use byteorder; +use byteorder::ByteOrder; +use rustc::middle::ty; +use std::collections::HashMap; +use std::mem; +use std::ptr; + +use interpreter::{EvalError, EvalResult}; + +pub struct Memory { + next_id: u64, + alloc_map: HashMap, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct AllocId(u64); + +// TODO(tsion): Shouldn't clone Allocation. (Audit the rest of the code.) +#[derive(Clone, Debug)] +pub struct Allocation { + pub bytes: Vec, + // TODO(tsion): relocations + // TODO(tsion): undef mask +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Pointer { + pub alloc_id: AllocId, + pub offset: usize, + pub repr: Repr, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FieldRepr { + pub offset: usize, + pub repr: Repr, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Repr { + Int, + Aggregate { + size: usize, + fields: Vec, + }, +} + +impl Memory { + pub fn new() -> Self { + Memory { next_id: 0, alloc_map: HashMap::new() } + } + + pub fn allocate_raw(&mut self, size: usize) -> AllocId { + let id = AllocId(self.next_id); + let alloc = Allocation { bytes: vec![0; size] }; + self.alloc_map.insert(self.next_id, alloc); + self.next_id += 1; + id + } + + pub fn allocate(&mut self, repr: Repr) -> Pointer { + Pointer { + alloc_id: self.allocate_raw(repr.size()), + offset: 0, + repr: repr, + } + } + + pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { + self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) + } + + pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { + self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) + } + + fn get_bytes(&self, ptr: &Pointer, size: usize) -> EvalResult<&[u8]> { + let alloc = try!(self.get(ptr.alloc_id)); + try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) + } + + fn get_bytes_mut(&mut self, ptr: &Pointer, size: usize) -> EvalResult<&mut [u8]> { + let alloc = try!(self.get_mut(ptr.alloc_id)); + try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) + } + + pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { + let src_bytes = try!(self.get_bytes_mut(src, size)).as_mut_ptr(); + let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); + + // SAFE: The above indexing would have panicked if there weren't at least `size` bytes + // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and + // `dest` could possibly overlap. + unsafe { + if src.alloc_id == dest.alloc_id { + ptr::copy(src_bytes, dest_bytes, size); + } else { + ptr::copy_nonoverlapping(src_bytes, dest_bytes, size); + } + } + + Ok(()) + } + + pub fn read_int(&self, ptr: &Pointer) -> EvalResult { + let bytes = try!(self.get_bytes(ptr, Repr::Int.size())); + Ok(byteorder::NativeEndian::read_i64(bytes)) + } + + pub fn write_int(&mut self, ptr: &Pointer, n: i64) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); + Ok(byteorder::NativeEndian::write_i64(bytes, n)) + } +} + +impl Allocation { + fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { + if start >= self.bytes.len() || end > self.bytes.len() { + return Err(EvalError::PointerOutOfBounds); + } + Ok(()) + } +} + +impl Pointer { + pub fn offset(&self, i: usize) -> Self { + Pointer { offset: self.offset + i, ..self.clone() } + } +} + +impl Repr { + // TODO(tsion): Cache these outputs. + pub fn from_ty(ty: ty::Ty) -> Self { + match ty.sty { + ty::TyInt(_) => Repr::Int, + + ty::TyTuple(ref fields) => { + let mut size = 0; + let fields = fields.iter().map(|ty| { + let repr = Repr::from_ty(ty); + let old_size = size; + size += repr.size(); + FieldRepr { offset: old_size, repr: repr } + }).collect(); + Repr::Aggregate { size: size, fields: fields } + }, + + _ => unimplemented!(), + } + } + + pub fn size(&self) -> usize { + match *self { + Repr::Int => mem::size_of::(), + Repr::Aggregate { size, .. } => size, + } + } +} From b0683c4c72d3476ca8d9fe28bf52347f58949a3a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 5 Mar 2016 00:50:53 -0600 Subject: [PATCH 0056/1096] Remove Allocation's Clone impl. --- src/memory.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 0fe2b735671b0..efaf33867de17 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -15,8 +15,7 @@ pub struct Memory { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); -// TODO(tsion): Shouldn't clone Allocation. (Audit the rest of the code.) -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Allocation { pub bytes: Vec, // TODO(tsion): relocations From e78465956864198acdd5ce8c0dcb465c9db5e48a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 5 Mar 2016 00:52:14 -0600 Subject: [PATCH 0057/1096] Sort imports. --- src/interpreter.rs | 7 +++---- src/memory.rs | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2d97de1032d68..e5328fe90c7de 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,14 +1,13 @@ // TODO(tsion): Remove this. #![allow(unused_imports, dead_code, unused_variables)] -use byteorder; -use byteorder::ByteOrder; +use byteorder::{self, ByteOrder}; use rustc::middle::const_eval; -use rustc::middle::def_id; use rustc::middle::cstore::CrateStore; +use rustc::middle::def_id; use rustc::middle::ty::{self, TyCtxt}; -use rustc::mir::repr::{self as mir, Mir}; use rustc::mir::mir_map::MirMap; +use rustc::mir::repr::{self as mir, Mir}; use std::collections::HashMap; use std::error::Error; use std::fmt; diff --git a/src/memory.rs b/src/memory.rs index efaf33867de17..4c7738557333b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,4 @@ -use byteorder; -use byteorder::ByteOrder; +use byteorder::{self, ByteOrder}; use rustc::middle::ty; use std::collections::HashMap; use std::mem; From 4f4e0b5051f82ed3032fcb8beb576e2b8212faa4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 6 Mar 2016 04:23:24 -0600 Subject: [PATCH 0058/1096] Implement a call stack using the new memory system. --- src/interpreter.rs | 181 +++++++++++++++++++-------------------------- test/new_values.rs | 22 +++--- 2 files changed, 87 insertions(+), 116 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e5328fe90c7de..f9a00fc9166d1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -54,66 +54,43 @@ impl fmt::Display for EvalError { // Func(def_id::DefId), // } -/// A stack frame: -/// -/// ```text -/// +-----------------------+ -/// | Arg(0) | -/// | Arg(1) | arguments -/// | ... | -/// | Arg(num_args - 1) | -/// + - - - - - - - - - - - + -/// | Var(0) | -/// | Var(1) | variables -/// | ... | -/// | Var(num_vars - 1) | -/// + - - - - - - - - - - - + -/// | Temp(0) | -/// | Temp(1) | temporaries -/// | ... | -/// | Temp(num_temps - 1) | -/// + - - - - - - - - - - - + -/// | Aggregates | aggregates -/// +-----------------------+ -/// ``` -// #[derive(Debug)] -// struct Frame { -// /// A pointer to a stack cell to write the return value of the current call, if it's not a -// /// divering call. -// return_ptr: Option, - -// offset: usize, -// num_args: usize, -// num_vars: usize, -// num_temps: usize, -// num_aggregate_fields: usize, -// } +/// A stack frame. +#[derive(Debug)] +struct Frame { + /// A pointer for writing the return value of the current call, if it's not a diverging call. + return_ptr: Option, -// impl Frame { -// fn size(&self) -> usize { -// self.num_args + self.num_vars + self.num_temps + self.num_aggregate_fields -// } + /// The list of locals for the current function, stored in order as + /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` + /// and the temporaries at `self.temp_offset`. + locals: Vec, -// fn arg_offset(&self, i: usize) -> usize { -// self.offset + i -// } + /// The offset of the first variable in `self.locals`. + var_offset: usize, -// fn var_offset(&self, i: usize) -> usize { -// self.offset + self.num_args + i -// } + /// The offset of the first temporary in `self.locals`. + temp_offset: usize, +} -// fn temp_offset(&self, i: usize) -> usize { -// self.offset + self.num_args + self.num_vars + i -// } -// } +impl Frame { + fn arg_ptr(&self, i: u32) -> Pointer { + self.locals[i as usize].clone() + } + + fn var_ptr(&self, i: u32) -> Pointer { + self.locals[self.var_offset + i as usize].clone() + } + + fn temp_ptr(&self, i: u32) -> Pointer { + self.locals[self.temp_offset + i as usize].clone() + } +} struct Interpreter<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, - // value_stack: Vec, - // call_stack: Vec, memory: memory::Memory, - return_ptr: Option, + stack: Vec, } impl<'a, 'tcx> Interpreter<'a, 'tcx> { @@ -121,49 +98,50 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Interpreter { tcx: tcx, mir_map: mir_map, - // value_stack: vec![Value::Uninit], // Allocate a spot for the top-level return value. - // call_stack: Vec::new(), memory: memory::Memory::new(), - return_ptr: None, + stack: Vec::new(), + } + } + + fn push_stack_frame(&mut self, mir: &Mir, args: &[&mir::Operand], return_ptr: Option) + -> EvalResult<()> + { + let num_args = mir.arg_decls.len(); + let num_vars = mir.var_decls.len(); + assert_eq!(args.len(), num_args); + + let arg_tys = mir.arg_decls.iter().map(|a| a.ty); + let var_tys = mir.var_decls.iter().map(|v| v.ty); + let temp_tys = mir.temp_decls.iter().map(|t| t.ty); + + let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + self.memory.allocate(Repr::from_ty(ty)) + }).collect(); + + for (dest, operand) in locals[..num_args].iter().zip(args) { + let src = try!(self.operand_to_ptr(operand)); + try!(self.memory.copy(&src, dest, dest.repr.size())); } + + self.stack.push(Frame { + return_ptr: return_ptr, + locals: locals, + var_offset: num_args, + temp_offset: num_args + num_vars, + }); + + Ok(()) } - // fn push_stack_frame(&mut self, mir: &Mir, args: &[Value], return_ptr: Option) { - // let frame = Frame { - // return_ptr: return_ptr, - // offset: self.value_stack.len(), - // num_args: mir.arg_decls.len(), - // num_vars: mir.var_decls.len(), - // num_temps: mir.temp_decls.len(), - // num_aggregate_fields: 0, - // }; - - // self.value_stack.extend(iter::repeat(Value::Uninit).take(frame.size())); - - // for (i, arg) in args.iter().enumerate() { - // self.value_stack[frame.arg_offset(i)] = arg.clone(); - // } - - // self.call_stack.push(frame); - // } - - // fn pop_stack_frame(&mut self) { - // let frame = self.call_stack.pop().expect("tried to pop stack frame, but there were none"); - // self.value_stack.truncate(frame.offset); - // } - - // fn allocate_aggregate(&mut self, size: usize) -> Pointer { - // let frame = self.call_stack.last_mut().expect("missing call frame"); - // frame.num_aggregate_fields += size; - - // let ptr = Pointer::Stack(self.value_stack.len()); - // self.value_stack.extend(iter::repeat(Value::Uninit).take(size)); - // ptr - // } - - fn call(&mut self, mir: &Mir, args: &[Allocation], return_ptr: Option) -> EvalResult<()> { - self.return_ptr = return_ptr; - // self.push_stack_frame(mir, args, return_ptr); + fn pop_stack_frame(&mut self) { + let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + // TODO(tsion): Deallocate local variables. + } + + fn call(&mut self, mir: &Mir, args: &[&mir::Operand], return_ptr: Option) + -> EvalResult<()> + { + try!(self.push_stack_frame(mir, args, return_ptr)); let mut block = mir::START_BLOCK; loop { @@ -252,29 +230,24 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - // self.pop_stack_frame(); - + self.pop_stack_frame(); Ok(()) } fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> EvalResult { + let frame = self.stack.last().expect("no call frames exists"); + let ptr = match *lvalue { - mir::Lvalue::ReturnPointer => - self.return_ptr.clone().expect("fn has no return pointer"), - _ => unimplemented!(), + mir::Lvalue::ReturnPointer => frame.return_ptr.clone() + .expect("ReturnPointer used in a function with no return value"), + mir::Lvalue::Arg(i) => frame.arg_ptr(i), + mir::Lvalue::Var(i) => frame.var_ptr(i), + mir::Lvalue::Temp(i) => frame.temp_ptr(i), + ref l => panic!("can't handle lvalue: {:?}", l), }; Ok(ptr) - // let frame = self.call_stack.last().expect("missing call frame"); - - // match *lvalue { - // mir::Lvalue::ReturnPointer => - // frame.return_ptr.expect("ReturnPointer used in a function with no return value"), - // mir::Lvalue::Arg(i) => Pointer::Stack(frame.arg_offset(i as usize)), - // mir::Lvalue::Var(i) => Pointer::Stack(frame.var_offset(i as usize)), - // mir::Lvalue::Temp(i) => Pointer::Stack(frame.temp_offset(i as usize)), - // mir::Lvalue::Projection(ref proj) => { // let base_ptr = self.lvalue_to_ptr(&proj.base); diff --git a/test/new_values.rs b/test/new_values.rs index 68f639b9965e6..36c82649c5163 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -34,17 +34,17 @@ fn tuple_5() -> (i32, i32, i32, i32, i32) { (1, 2, 3, 4, 5) } -// #[miri_run(expected = "Int(3)")] -// fn indirect_add() -> i32 { -// let x = 1; -// let y = 2; -// x + y -// } +#[miri_run] +fn indirect_add() -> i32 { + let x = 1; + let y = 2; + x + y +} -// #[miri_run(expected = "Int(25)")] -// fn arith() -> i32 { -// 3*3 + 4*4 -// } +#[miri_run] +fn arith() -> i32 { + 3*3 + 4*4 +} // #[miri_run(expected = "Int(0)")] // fn if_false() -> i32 { @@ -190,5 +190,3 @@ fn tuple_5() -> (i32, i32, i32, i32, i32) { // fn cross_crate_fn_call() -> i32 { // if 1i32.is_positive() { 1 } else { 0 } // } - -// fn main() {} From d330bd6a32c1386def539c851d9fc1f76354eb22 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 03:32:02 -0600 Subject: [PATCH 0059/1096] Glob import variants before matching and simplify some code. --- src/interpreter.rs | 118 +++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f9a00fc9166d1..df5ad1114519b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -150,22 +150,19 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { for stmt in &block_data.statements { if TRACE_EXECUTION { println!("{:?}", stmt); } - - match stmt.kind { - mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - let ptr = try!(self.lvalue_to_ptr(lvalue)); - try!(self.eval_rvalue_into(rvalue, &ptr)); - } - } + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + let ptr = try!(self.lvalue_to_ptr(lvalue)); + try!(self.eval_rvalue_into(rvalue, &ptr)); } if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } + use rustc::mir::repr::Terminator::*; match *block_data.terminator() { - mir::Terminator::Return => break, - mir::Terminator::Goto { target } => block = target, + Return => break, + Goto { target } => block = target, - // mir::Terminator::Call { ref func, ref args, ref destination, .. } => { + // Call { ref func, ref args, ref destination, .. } => { // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); // let func_val = self.operand_to_ptr(func); @@ -193,7 +190,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } // } - // mir::Terminator::If { ref cond, targets: (then_target, else_target) } => { + // If { ref cond, targets: (then_target, else_target) } => { + // let cond_ptr = try!(self.operand_to_ptr(cond)); // match self.operand_to_ptr(cond) { // Value::Bool(true) => block = then_target, // Value::Bool(false) => block = else_target, @@ -201,7 +199,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } // } - // mir::Terminator::SwitchInt { ref discr, ref values, ref targets, .. } => { + // SwitchInt { ref discr, ref values, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); // let index = values.iter().position(|v| discr_val == self.const_to_ptr(v)) @@ -210,7 +208,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // block = targets[index]; // } - // mir::Terminator::Switch { ref discr, ref targets, .. } => { + // Switch { ref discr, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); // if let Value::Adt { variant, .. } = discr_val { @@ -220,12 +218,12 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } // } - mir::Terminator::Drop { target, .. } => { + Drop { target, .. } => { // TODO: Handle destructors and dynamic drop. block = target; } - mir::Terminator::Resume => unimplemented!(), + Resume => unimplemented!(), _ => unimplemented!(), } } @@ -237,12 +235,13 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> EvalResult { let frame = self.stack.last().expect("no call frames exists"); + use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - mir::Lvalue::ReturnPointer => frame.return_ptr.clone() + ReturnPointer => frame.return_ptr.clone() .expect("ReturnPointer used in a function with no return value"), - mir::Lvalue::Arg(i) => frame.arg_ptr(i), - mir::Lvalue::Var(i) => frame.var_ptr(i), - mir::Lvalue::Temp(i) => frame.temp_ptr(i), + Arg(i) => frame.arg_ptr(i), + Var(i) => frame.var_ptr(i), + Temp(i) => frame.temp_ptr(i), ref l => panic!("can't handle lvalue: {:?}", l), }; @@ -286,28 +285,29 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) -> EvalResult<()> { + use rustc::mir::repr::BinOp::*; match (&left.repr, &right.repr, &dest.repr) { (&Repr::Int, &Repr::Int, &Repr::Int) => { let l = try!(self.memory.read_int(&left)); let r = try!(self.memory.read_int(&right)); let n = match bin_op { - mir::BinOp::Add => l + r, - mir::BinOp::Sub => l - r, - mir::BinOp::Mul => l * r, - mir::BinOp::Div => l / r, - mir::BinOp::Rem => l % r, - mir::BinOp::BitXor => l ^ r, - mir::BinOp::BitAnd => l & r, - mir::BinOp::BitOr => l | r, - mir::BinOp::Shl => l << r, - mir::BinOp::Shr => l >> r, - _ => unimplemented!(), - // mir::BinOp::Eq => Value::Bool(l == r), - // mir::BinOp::Lt => Value::Bool(l < r), - // mir::BinOp::Le => Value::Bool(l <= r), - // mir::BinOp::Ne => Value::Bool(l != r), - // mir::BinOp::Ge => Value::Bool(l >= r), - // mir::BinOp::Gt => Value::Bool(l > r), + Add => l + r, + Sub => l - r, + Mul => l * r, + Div => l / r, + Rem => l % r, + BitXor => l ^ r, + BitAnd => l & r, + BitOr => l | r, + Shl => l << r, + Shr => l >> r, + _ => unimplemented!(), + // Eq => Value::Bool(l == r), + // Lt => Value::Bool(l < r), + // Le => Value::Bool(l <= r), + // Ne => Value::Bool(l != r), + // Ge => Value::Bool(l >= r), + // Gt => Value::Bool(l > r), }; self.memory.write_int(dest, n) } @@ -317,19 +317,20 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, dest: &Pointer) -> EvalResult<()> { + use rustc::mir::repr::Rvalue::*; match *rvalue { - mir::Rvalue::Use(ref operand) => { + Use(ref operand) => { let src = try!(self.operand_to_ptr(operand)); try!(self.memory.copy(&src, dest, src.repr.size())); } - mir::Rvalue::BinaryOp(bin_op, ref left, ref right) => { + BinaryOp(bin_op, ref left, ref right) => { let left_ptr = try!(self.operand_to_ptr(left)); let right_ptr = try!(self.operand_to_ptr(right)); try!(self.eval_binary_op(bin_op, left_ptr, right_ptr, dest)); } - mir::Rvalue::UnaryOp(un_op, ref operand) => { + UnaryOp(un_op, ref operand) => { let ptr = try!(self.operand_to_ptr(operand)); let m = try!(self.memory.read_int(&ptr)); let n = match (un_op, ptr.repr) { @@ -340,7 +341,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { try!(self.memory.write_int(dest, n)); } - mir::Rvalue::Aggregate(mir::AggregateKind::Tuple, ref operands) => { + Aggregate(mir::AggregateKind::Tuple, ref operands) => { match dest.repr { Repr::Aggregate { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { @@ -354,11 +355,11 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - // mir::Rvalue::Ref(_region, _kind, ref lvalue) => { + // Ref(_region, _kind, ref lvalue) => { // Value::Pointer(self.lvalue_to_ptr(lvalue)) // } - // mir::Rvalue::Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), + // Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), // ref operands) => { // let max_fields = adt_def.variants // .iter() @@ -383,14 +384,16 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn operand_to_ptr(&mut self, op: &mir::Operand) -> EvalResult { + use rustc::mir::repr::Operand::*; match *op { - mir::Operand::Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), + Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), - mir::Operand::Constant(ref constant) => { + Constant(ref constant) => { + use rustc::mir::repr::Literal::*; match constant.literal { - mir::Literal::Value { ref value } => self.const_to_ptr(value), + Value { ref value } => self.const_to_ptr(value), - mir::Literal::Item { def_id, kind, .. } => match kind { + Item { def_id, kind, .. } => match kind { // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), _ => panic!("can't handle item literal: {:?}", constant.literal), }, @@ -400,22 +403,23 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { + use rustc::middle::const_eval::ConstVal::*; match *const_val { - const_eval::ConstVal::Float(_f) => unimplemented!(), - const_eval::ConstVal::Int(n) => { + Float(_f) => unimplemented!(), + Int(n) => { let ptr = self.memory.allocate(Repr::Int); try!(self.memory.write_int(&ptr, n)); Ok(ptr) } - const_eval::ConstVal::Uint(_u) => unimplemented!(), - const_eval::ConstVal::Str(ref _s) => unimplemented!(), - const_eval::ConstVal::ByteStr(ref _bs) => unimplemented!(), - const_eval::ConstVal::Bool(b) => unimplemented!(), - const_eval::ConstVal::Struct(_node_id) => unimplemented!(), - const_eval::ConstVal::Tuple(_node_id) => unimplemented!(), - const_eval::ConstVal::Function(_def_id) => unimplemented!(), - const_eval::ConstVal::Array(_, _) => unimplemented!(), - const_eval::ConstVal::Repeat(_, _) => unimplemented!(), + Uint(_u) => unimplemented!(), + Str(ref _s) => unimplemented!(), + ByteStr(ref _bs) => unimplemented!(), + Bool(b) => unimplemented!(), + Struct(_node_id) => unimplemented!(), + Tuple(_node_id) => unimplemented!(), + Function(_def_id) => unimplemented!(), + Array(_, _) => unimplemented!(), + Repeat(_, _) => unimplemented!(), } } } From a1adc55370924ee9b5bf0bbbb62126c5c7781969 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 04:44:03 -0600 Subject: [PATCH 0060/1096] Add basic bool support. --- src/interpreter.rs | 10 ++++++++-- src/memory.rs | 32 ++++++++++++++++++++++++++------ test/new_values.rs | 5 +++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index df5ad1114519b..3abc4231edd75 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -22,6 +22,7 @@ const TRACE_EXECUTION: bool = true; #[derive(Clone, Debug)] pub enum EvalError { DanglingPointerDeref, + InvalidBool, PointerOutOfBounds, } @@ -31,6 +32,7 @@ impl Error for EvalError { fn description(&self) -> &str { match *self { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", + EvalError::InvalidBool => "invalid boolean value read", EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", } } @@ -405,7 +407,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { use rustc::middle::const_eval::ConstVal::*; match *const_val { - Float(_f) => unimplemented!(), + Float(_f) => unimplemented!(), Int(n) => { let ptr = self.memory.allocate(Repr::Int); try!(self.memory.write_int(&ptr, n)); @@ -414,7 +416,11 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Uint(_u) => unimplemented!(), Str(ref _s) => unimplemented!(), ByteStr(ref _bs) => unimplemented!(), - Bool(b) => unimplemented!(), + Bool(b) => { + let ptr = self.memory.allocate(Repr::Bool); + try!(self.memory.write_bool(&ptr, b)); + Ok(ptr) + }, Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), diff --git a/src/memory.rs b/src/memory.rs index 4c7738557333b..9cc3c63622c1b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -36,6 +36,7 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { + Bool, Int, Aggregate { size: usize, @@ -103,22 +104,38 @@ impl Memory { } pub fn read_int(&self, ptr: &Pointer) -> EvalResult { - let bytes = try!(self.get_bytes(ptr, Repr::Int.size())); - Ok(byteorder::NativeEndian::read_i64(bytes)) + self.get_bytes(ptr, Repr::Int.size()).map(byteorder::NativeEndian::read_i64) } pub fn write_int(&mut self, ptr: &Pointer, n: i64) -> EvalResult<()> { let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); - Ok(byteorder::NativeEndian::write_i64(bytes, n)) + byteorder::NativeEndian::write_i64(bytes, n); + Ok(()) + } + + pub fn read_bool(&self, ptr: &Pointer) -> EvalResult { + let bytes = try!(self.get_bytes(ptr, 1)); + match bytes[0] { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(EvalError::InvalidBool), + } + } + + pub fn write_bool(&mut self, ptr: &Pointer, b: bool) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 1)); + bytes[0] = b as u8; + Ok(()) } } impl Allocation { fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { - if start >= self.bytes.len() || end > self.bytes.len() { - return Err(EvalError::PointerOutOfBounds); + if start < self.bytes.len() && end <= self.bytes.len() { + Ok(()) + } else { + Err(EvalError::PointerOutOfBounds) } - Ok(()) } } @@ -132,6 +149,8 @@ impl Repr { // TODO(tsion): Cache these outputs. pub fn from_ty(ty: ty::Ty) -> Self { match ty.sty { + ty::TyBool => Repr::Bool, + ty::TyInt(_) => Repr::Int, ty::TyTuple(ref fields) => { @@ -151,6 +170,7 @@ impl Repr { pub fn size(&self) -> usize { match *self { + Repr::Bool => 1, Repr::Int => mem::size_of::(), Repr::Aggregate { size, .. } => size, } diff --git a/test/new_values.rs b/test/new_values.rs index 36c82649c5163..b4a506a9fb469 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -46,6 +46,11 @@ fn arith() -> i32 { 3*3 + 4*4 } +#[miri_run] +fn boolean() -> bool { + true +} + // #[miri_run(expected = "Int(0)")] // fn if_false() -> i32 { // if false { 1 } else { 0 } From 586bc5d1da55e776b39601ad06f00c7cae0a0f5e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 04:48:12 -0600 Subject: [PATCH 0061/1096] Reimplement 'if' conditions. --- src/interpreter.rs | 16 +++++++--------- test/new_values.rs | 16 ++++++++-------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3abc4231edd75..6e01f8e7944fb 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -162,8 +162,15 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { use rustc::mir::repr::Terminator::*; match *block_data.terminator() { Return => break, + Goto { target } => block = target, + If { ref cond, targets: (then_target, else_target) } => { + let cond_ptr = try!(self.operand_to_ptr(cond)); + let cond = try!(self.memory.read_bool(&cond_ptr)); + block = if cond { then_target } else { else_target } + } + // Call { ref func, ref args, ref destination, .. } => { // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); // let func_val = self.operand_to_ptr(func); @@ -192,15 +199,6 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } // } - // If { ref cond, targets: (then_target, else_target) } => { - // let cond_ptr = try!(self.operand_to_ptr(cond)); - // match self.operand_to_ptr(cond) { - // Value::Bool(true) => block = then_target, - // Value::Bool(false) => block = else_target, - // cond_val => panic!("Non-boolean `if` condition value: {:?}", cond_val), - // } - // } - // SwitchInt { ref discr, ref values, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); diff --git a/test/new_values.rs b/test/new_values.rs index b4a506a9fb469..880ac71abc305 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -51,15 +51,15 @@ fn boolean() -> bool { true } -// #[miri_run(expected = "Int(0)")] -// fn if_false() -> i32 { -// if false { 1 } else { 0 } -// } +#[miri_run] +fn if_false() -> i32 { + if false { 1 } else { 0 } +} -// #[miri_run(expected = "Int(1)")] -// fn if_true() -> i32 { -// if true { 1 } else { 0 } -// } +#[miri_run] +fn if_true() -> i32 { + if true { 1 } else { 0 } +} // #[miri_run(expected = "Int(2)")] // fn call() -> i32 { From 6d6cd1f420462e980c10354bb72b64d0a3745a2e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 04:50:44 -0600 Subject: [PATCH 0062/1096] Rename 'block' to 'current_block'. --- src/interpreter.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6e01f8e7944fb..da0128f5539de 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -144,11 +144,11 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { -> EvalResult<()> { try!(self.push_stack_frame(mir, args, return_ptr)); - let mut block = mir::START_BLOCK; + let mut current_block = mir::START_BLOCK; loop { - if TRACE_EXECUTION { println!("Entering block: {:?}", block); } - let block_data = mir.basic_block_data(block); + if TRACE_EXECUTION { println!("Entering block: {:?}", current_block); } + let block_data = mir.basic_block_data(current_block); for stmt in &block_data.statements { if TRACE_EXECUTION { println!("{:?}", stmt); } @@ -163,12 +163,12 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match *block_data.terminator() { Return => break, - Goto { target } => block = target, + Goto { target } => current_block = target, If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = try!(self.operand_to_ptr(cond)); let cond = try!(self.memory.read_bool(&cond_ptr)); - block = if cond { then_target } else { else_target } + current_block = if cond { then_target } else { else_target }; } // Call { ref func, ref args, ref destination, .. } => { @@ -192,7 +192,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // self.call(mir, &arg_vals, ptr); // if let Some((_, target)) = *destination { - // block = target; + // current_block = target; // } // } else { // panic!("tried to call a non-function value: {:?}", func_val); @@ -205,14 +205,14 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // let index = values.iter().position(|v| discr_val == self.const_to_ptr(v)) // .expect("discriminant matched no values"); - // block = targets[index]; + // current_block = targets[index]; // } // Switch { ref discr, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); // if let Value::Adt { variant, .. } = discr_val { - // block = targets[variant]; + // current_block = targets[variant]; // } else { // panic!("Switch on non-Adt value: {:?}", discr_val); // } @@ -220,7 +220,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Drop { target, .. } => { // TODO: Handle destructors and dynamic drop. - block = target; + current_block = target; } Resume => unimplemented!(), From 3ae6b80ab11c427d210894f61335b098beaaf2ba Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 07:10:52 -0600 Subject: [PATCH 0063/1096] Remove Pointer's repr field and make it Copy. This required refactoring throughout and math operators have been temporarily commented out. --- src/interpreter.rs | 202 +++++++++++++++++++++++---------------------- src/memory.rs | 25 +++--- test/new_values.rs | 36 ++++---- 3 files changed, 135 insertions(+), 128 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index da0128f5539de..c08ef00612b27 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -57,8 +57,10 @@ impl fmt::Display for EvalError { // } /// A stack frame. -#[derive(Debug)] -struct Frame { +struct Frame<'a, 'tcx: 'a> { + /// The MIR for the fucntion called on this frame. + mir: &'a Mir<'tcx>, + /// A pointer for writing the return value of the current call, if it's not a diverging call. return_ptr: Option, @@ -74,17 +76,17 @@ struct Frame { temp_offset: usize, } -impl Frame { +impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { fn arg_ptr(&self, i: u32) -> Pointer { - self.locals[i as usize].clone() + self.locals[i as usize] } fn var_ptr(&self, i: u32) -> Pointer { - self.locals[self.var_offset + i as usize].clone() + self.locals[self.var_offset + i as usize] } fn temp_ptr(&self, i: u32) -> Pointer { - self.locals[self.temp_offset + i as usize].clone() + self.locals[self.temp_offset + i as usize] } } @@ -92,10 +94,10 @@ struct Interpreter<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, memory: memory::Memory, - stack: Vec, + stack: Vec>, } -impl<'a, 'tcx> Interpreter<'a, 'tcx> { +impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { tcx: tcx, @@ -105,27 +107,29 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { } } - fn push_stack_frame(&mut self, mir: &Mir, args: &[&mir::Operand], return_ptr: Option) - -> EvalResult<()> - { + fn push_stack_frame(&mut self, mir: &'a Mir<'tcx>, args: &[&mir::Operand<'tcx>], + return_ptr: Option) -> EvalResult<()> { let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); + let num_temps = mir.temp_decls.len(); assert_eq!(args.len(), num_args); - let arg_tys = mir.arg_decls.iter().map(|a| a.ty); - let var_tys = mir.var_decls.iter().map(|v| v.ty); - let temp_tys = mir.temp_decls.iter().map(|t| t.ty); - - let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - self.memory.allocate(Repr::from_ty(ty)) - }).collect(); + let mut locals = Vec::with_capacity(num_args + num_vars + num_temps); - for (dest, operand) in locals[..num_args].iter().zip(args) { - let src = try!(self.operand_to_ptr(operand)); - try!(self.memory.copy(&src, dest, dest.repr.size())); + for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { + let repr = Repr::from_ty(arg_decl.ty); + let dest = self.memory.allocate(&repr); + let src = try!(self.operand_to_ptr(arg_operand)); + try!(self.memory.copy(src, dest, repr.size())); + locals.push(dest); } - self.stack.push(Frame { + let var_tys = mir.var_decls.iter().map(|v| v.ty); + let temp_tys = mir.temp_decls.iter().map(|t| t.ty); + locals.extend(var_tys.chain(temp_tys).map(|ty| self.memory.allocate(&Repr::from_ty(ty)))); + + self.stack.push(Frame { + mir: mir, return_ptr: return_ptr, locals: locals, var_offset: num_args, @@ -140,9 +144,8 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn call(&mut self, mir: &Mir, args: &[&mir::Operand], return_ptr: Option) - -> EvalResult<()> - { + fn call(&mut self, mir: &'a Mir<'tcx>, args: &[&mir::Operand<'tcx>], + return_ptr: Option) -> EvalResult<()> { try!(self.push_stack_frame(mir, args, return_ptr)); let mut current_block = mir::START_BLOCK; @@ -153,8 +156,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { for stmt in &block_data.statements { if TRACE_EXECUTION { println!("{:?}", stmt); } let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let ptr = try!(self.lvalue_to_ptr(lvalue)); - try!(self.eval_rvalue_into(rvalue, &ptr)); + try!(self.eval_assignment(lvalue, rvalue)); } if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } @@ -167,7 +169,7 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = try!(self.operand_to_ptr(cond)); - let cond = try!(self.memory.read_bool(&cond_ptr)); + let cond = try!(self.memory.read_bool(cond_ptr)); current_block = if cond { then_target } else { else_target }; } @@ -232,15 +234,15 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Ok(()) } - fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue) -> EvalResult { - let frame = self.stack.last().expect("no call frames exists"); + fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { + let frame = self.current_frame(); use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - ReturnPointer => frame.return_ptr.clone() - .expect("ReturnPointer used in a function with no return value"), - Arg(i) => frame.arg_ptr(i), - Var(i) => frame.var_ptr(i), + ReturnPointer => + frame.return_ptr.expect("ReturnPointer used in a function with no return value"), + Arg(i) => frame.arg_ptr(i), + Var(i) => frame.var_ptr(i), Temp(i) => frame.temp_ptr(i), ref l => panic!("can't handle lvalue: {:?}", l), }; @@ -283,75 +285,79 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { // } } - fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: &Pointer) - -> EvalResult<()> { - use rustc::mir::repr::BinOp::*; - match (&left.repr, &right.repr, &dest.repr) { - (&Repr::Int, &Repr::Int, &Repr::Int) => { - let l = try!(self.memory.read_int(&left)); - let r = try!(self.memory.read_int(&right)); - let n = match bin_op { - Add => l + r, - Sub => l - r, - Mul => l * r, - Div => l / r, - Rem => l % r, - BitXor => l ^ r, - BitAnd => l & r, - BitOr => l | r, - Shl => l << r, - Shr => l >> r, - _ => unimplemented!(), - // Eq => Value::Bool(l == r), - // Lt => Value::Bool(l < r), - // Le => Value::Bool(l <= r), - // Ne => Value::Bool(l != r), - // Ge => Value::Bool(l >= r), - // Gt => Value::Bool(l > r), - }; - self.memory.write_int(dest, n) - } - (l, r, o) => - panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), - } - } + // fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: Pointer) + // -> EvalResult<()> { + // use rustc::mir::repr::BinOp::*; + // match (&left.repr, &right.repr, &dest.repr) { + // (&Repr::Int, &Repr::Int, &Repr::Int) => { + // let l = try!(self.memory.read_int(left)); + // let r = try!(self.memory.read_int(right)); + // let n = match bin_op { + // Add => l + r, + // Sub => l - r, + // Mul => l * r, + // Div => l / r, + // Rem => l % r, + // BitXor => l ^ r, + // BitAnd => l & r, + // BitOr => l | r, + // Shl => l << r, + // Shr => l >> r, + // _ => unimplemented!(), + // // Eq => Value::Bool(l == r), + // // Lt => Value::Bool(l < r), + // // Le => Value::Bool(l <= r), + // // Ne => Value::Bool(l != r), + // // Ge => Value::Bool(l >= r), + // // Gt => Value::Bool(l > r), + // }; + // self.memory.write_int(dest, n) + // } + // (l, r, o) => + // panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), + // } + // } + + fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) + -> EvalResult<()> + { + let dest = try!(self.lvalue_to_ptr(lvalue)); + let dest_ty = self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); + let dest_repr = Repr::from_ty(dest_ty); - fn eval_rvalue_into(&mut self, rvalue: &mir::Rvalue, dest: &Pointer) -> EvalResult<()> { use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { let src = try!(self.operand_to_ptr(operand)); - try!(self.memory.copy(&src, dest, src.repr.size())); + self.memory.copy(src, dest, dest_repr.size()) } - BinaryOp(bin_op, ref left, ref right) => { - let left_ptr = try!(self.operand_to_ptr(left)); - let right_ptr = try!(self.operand_to_ptr(right)); - try!(self.eval_binary_op(bin_op, left_ptr, right_ptr, dest)); - } - - UnaryOp(un_op, ref operand) => { - let ptr = try!(self.operand_to_ptr(operand)); - let m = try!(self.memory.read_int(&ptr)); - let n = match (un_op, ptr.repr) { - (mir::UnOp::Not, Repr::Int) => !m, - (mir::UnOp::Neg, Repr::Int) => -m, - (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), - }; - try!(self.memory.write_int(dest, n)); - } + // BinaryOp(bin_op, ref left, ref right) => + // self.eval_binary_op(lvalue, bin_op, left, right), + + // UnaryOp(un_op, ref operand) => { + // let ptr = try!(self.operand_to_ptr(operand)); + // let m = try!(self.memory.read_int(ptr)); + // let n = match (un_op, ptr.repr) { + // (mir::UnOp::Not, Repr::Int) => !m, + // (mir::UnOp::Neg, Repr::Int) => -m, + // (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), + // }; + // self.memory.write_int(dest, n) + // } Aggregate(mir::AggregateKind::Tuple, ref operands) => { - match dest.repr { + match dest_repr { Repr::Aggregate { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { let src = try!(self.operand_to_ptr(operand)); - try!(self.memory.copy(&src, &dest.offset(field.offset), src.repr.size())); + try!(self.memory.copy(src, dest.offset(field.offset), + field.repr.size())); } + Ok(()) } - _ => panic!("attempted to write tuple rvalue '{:?}' into non-aggregate pointer '{:?}'", - rvalue, dest) + _ => unimplemented!(), } } @@ -379,11 +385,9 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { ref r => panic!("can't handle rvalue: {:?}", r), } - - Ok(()) } - fn operand_to_ptr(&mut self, op: &mir::Operand) -> EvalResult { + fn operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), @@ -407,16 +411,16 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { match *const_val { Float(_f) => unimplemented!(), Int(n) => { - let ptr = self.memory.allocate(Repr::Int); - try!(self.memory.write_int(&ptr, n)); + let ptr = self.memory.allocate(&Repr::Int); + try!(self.memory.write_int(ptr, n)); Ok(ptr) } Uint(_u) => unimplemented!(), Str(ref _s) => unimplemented!(), ByteStr(ref _bs) => unimplemented!(), Bool(b) => { - let ptr = self.memory.allocate(Repr::Bool); - try!(self.memory.write_bool(&ptr, b)); + let ptr = self.memory.allocate(&Repr::Bool); + try!(self.memory.write_bool(ptr, b)); Ok(ptr) }, Struct(_node_id) => unimplemented!(), @@ -426,6 +430,10 @@ impl<'a, 'tcx> Interpreter<'a, 'tcx> { Repeat(_, _) => unimplemented!(), } } + + fn current_frame(&self) -> &Frame<'a, 'tcx> { + self.stack.last().expect("no call frames exist") + } } pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { @@ -438,10 +446,10 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => Some(miri.memory.allocate(Repr::from_ty(ty))), + ty::FnConverging(ty) => Some(miri.memory.allocate(&Repr::from_ty(ty))), ty::FnDiverging => None, }; - miri.call(mir, &[], return_ptr.clone()).unwrap(); + miri.call(mir, &[], return_ptr).unwrap(); if let Some(ret) = return_ptr { println!("Returned: {:?}\n", miri.memory.get(ret.alloc_id).unwrap()); diff --git a/src/memory.rs b/src/memory.rs index 9cc3c63622c1b..e6d3f5096fad5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -21,11 +21,10 @@ pub struct Allocation { // TODO(tsion): undef mask } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Pointer { pub alloc_id: AllocId, pub offset: usize, - pub repr: Repr, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -57,11 +56,10 @@ impl Memory { id } - pub fn allocate(&mut self, repr: Repr) -> Pointer { + pub fn allocate(&mut self, repr: &Repr) -> Pointer { Pointer { alloc_id: self.allocate_raw(repr.size()), offset: 0, - repr: repr, } } @@ -73,19 +71,19 @@ impl Memory { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } - fn get_bytes(&self, ptr: &Pointer, size: usize) -> EvalResult<&[u8]> { + fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { let alloc = try!(self.get(ptr.alloc_id)); try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes_mut(&mut self, ptr: &Pointer, size: usize) -> EvalResult<&mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { let alloc = try!(self.get_mut(ptr.alloc_id)); try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - pub fn copy(&mut self, src: &Pointer, dest: &Pointer, size: usize) -> EvalResult<()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { let src_bytes = try!(self.get_bytes_mut(src, size)).as_mut_ptr(); let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); @@ -103,17 +101,17 @@ impl Memory { Ok(()) } - pub fn read_int(&self, ptr: &Pointer) -> EvalResult { + pub fn read_int(&self, ptr: Pointer) -> EvalResult { self.get_bytes(ptr, Repr::Int.size()).map(byteorder::NativeEndian::read_i64) } - pub fn write_int(&mut self, ptr: &Pointer, n: i64) -> EvalResult<()> { + pub fn write_int(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); byteorder::NativeEndian::write_i64(bytes, n); Ok(()) } - pub fn read_bool(&self, ptr: &Pointer) -> EvalResult { + pub fn read_bool(&self, ptr: Pointer) -> EvalResult { let bytes = try!(self.get_bytes(ptr, 1)); match bytes[0] { 0 => Ok(false), @@ -122,7 +120,7 @@ impl Memory { } } - pub fn write_bool(&mut self, ptr: &Pointer, b: bool) -> EvalResult<()> { + pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<()> { let bytes = try!(self.get_bytes_mut(ptr, 1)); bytes[0] = b as u8; Ok(()) @@ -140,8 +138,9 @@ impl Allocation { } impl Pointer { - pub fn offset(&self, i: usize) -> Self { - Pointer { offset: self.offset + i, ..self.clone() } + pub fn offset(self, i: usize) -> Self { + // TODO(tsion): Check for offset out of bounds. + Pointer { offset: self.offset + i, ..self } } } diff --git a/test/new_values.rs b/test/new_values.rs index 880ac71abc305..64b56b01e171f 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -6,15 +6,15 @@ fn ret() -> i32 { 1 } -#[miri_run] -fn neg() -> i32 { - -1 -} +// #[miri_run] +// fn neg() -> i32 { +// -1 +// } -#[miri_run] -fn add() -> i32 { - 1 + 2 -} +// #[miri_run] +// fn add() -> i32 { +// 1 + 2 +// } #[miri_run] fn empty() {} @@ -34,17 +34,17 @@ fn tuple_5() -> (i32, i32, i32, i32, i32) { (1, 2, 3, 4, 5) } -#[miri_run] -fn indirect_add() -> i32 { - let x = 1; - let y = 2; - x + y -} +// #[miri_run] +// fn indirect_add() -> i32 { +// let x = 1; +// let y = 2; +// x + y +// } -#[miri_run] -fn arith() -> i32 { - 3*3 + 4*4 -} +// #[miri_run] +// fn arith() -> i32 { +// 3*3 + 4*4 +// } #[miri_run] fn boolean() -> bool { From 83adde623f5ba4c7fb1aee5200658a677e8a7046 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 07:19:43 -0600 Subject: [PATCH 0064/1096] Simplfy memory allocation. --- src/interpreter.rs | 12 +++++++----- src/memory.rs | 8 ++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c08ef00612b27..b5023b5b9fd27 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { let repr = Repr::from_ty(arg_decl.ty); - let dest = self.memory.allocate(&repr); + let dest = self.memory.allocate(repr.size()); let src = try!(self.operand_to_ptr(arg_operand)); try!(self.memory.copy(src, dest, repr.size())); locals.push(dest); @@ -126,7 +126,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); - locals.extend(var_tys.chain(temp_tys).map(|ty| self.memory.allocate(&Repr::from_ty(ty)))); + locals.extend(var_tys.chain(temp_tys).map(|ty| { + self.memory.allocate(Repr::from_ty(ty).size()) + })); self.stack.push(Frame { mir: mir, @@ -411,7 +413,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *const_val { Float(_f) => unimplemented!(), Int(n) => { - let ptr = self.memory.allocate(&Repr::Int); + let ptr = self.memory.allocate(Repr::Int.size()); try!(self.memory.write_int(ptr, n)); Ok(ptr) } @@ -419,7 +421,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Str(ref _s) => unimplemented!(), ByteStr(ref _bs) => unimplemented!(), Bool(b) => { - let ptr = self.memory.allocate(&Repr::Bool); + let ptr = self.memory.allocate(Repr::Bool.size()); try!(self.memory.write_bool(ptr, b)); Ok(ptr) }, @@ -446,7 +448,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => Some(miri.memory.allocate(&Repr::from_ty(ty))), + ty::FnConverging(ty) => Some(miri.memory.allocate(Repr::from_ty(ty).size())), ty::FnDiverging => None, }; miri.call(mir, &[], return_ptr).unwrap(); diff --git a/src/memory.rs b/src/memory.rs index e6d3f5096fad5..6ebdf17f82cad 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -48,17 +48,13 @@ impl Memory { Memory { next_id: 0, alloc_map: HashMap::new() } } - pub fn allocate_raw(&mut self, size: usize) -> AllocId { + pub fn allocate(&mut self, size: usize) -> Pointer { let id = AllocId(self.next_id); let alloc = Allocation { bytes: vec![0; size] }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; - id - } - - pub fn allocate(&mut self, repr: &Repr) -> Pointer { Pointer { - alloc_id: self.allocate_raw(repr.size()), + alloc_id: id, offset: 0, } } From 619daf0129027fcbb526cb5beef19f470a43c6ae Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 07:48:38 -0600 Subject: [PATCH 0065/1096] Re-add support for integer binops. --- src/interpreter.rs | 166 ++++++++++++++++++++++----------------------- test/new_values.rs | 28 ++++---- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b5023b5b9fd27..d37a95113bb82 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -236,90 +236,39 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(()) } - fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { - let frame = self.current_frame(); - - use rustc::mir::repr::Lvalue::*; - let ptr = match *lvalue { - ReturnPointer => - frame.return_ptr.expect("ReturnPointer used in a function with no return value"), - Arg(i) => frame.arg_ptr(i), - Var(i) => frame.var_ptr(i), - Temp(i) => frame.temp_ptr(i), - ref l => panic!("can't handle lvalue: {:?}", l), + fn eval_binary_op( + &mut self, bin_op: mir::BinOp, left_operand: &mir::Operand<'tcx>, + right_operand: &mir::Operand<'tcx>, dest: Pointer + ) -> EvalResult<()> { + // FIXME(tsion): Check for non-integer binary operations. + let left = try!(self.operand_to_ptr(left_operand)); + let right = try!(self.operand_to_ptr(right_operand)); + let l = try!(self.memory.read_int(left)); + let r = try!(self.memory.read_int(right)); + + use rustc::mir::repr::BinOp::*; + let n = match bin_op { + Add => l + r, + Sub => l - r, + Mul => l * r, + Div => l / r, + Rem => l % r, + BitXor => l ^ r, + BitAnd => l & r, + BitOr => l | r, + Shl => l << r, + Shr => l >> r, + _ => unimplemented!(), + // Eq => Value::Bool(l == r), + // Lt => Value::Bool(l < r), + // Le => Value::Bool(l <= r), + // Ne => Value::Bool(l != r), + // Ge => Value::Bool(l >= r), + // Gt => Value::Bool(l > r), }; - - Ok(ptr) - - // mir::Lvalue::Projection(ref proj) => { - // let base_ptr = self.lvalue_to_ptr(&proj.base); - - // match proj.elem { - // mir::ProjectionElem::Field(field, _) => { - // base_ptr.offset(field.index()) - // } - - // mir::ProjectionElem::Downcast(_, variant) => { - // let adt_val = self.read_pointer(base_ptr); - // if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { - // debug_assert_eq!(variant, actual_variant); - // data_ptr - // } else { - // panic!("Downcast attempted on non-ADT: {:?}", adt_val) - // } - // } - - // mir::ProjectionElem::Deref => { - // let ptr_val = self.read_pointer(base_ptr); - // if let Value::Pointer(ptr) = ptr_val { - // ptr - // } else { - // panic!("Deref attempted on non-pointer: {:?}", ptr_val) - // } - // } - - // mir::ProjectionElem::Index(ref _operand) => unimplemented!(), - // mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), - // } - // } - - // _ => unimplemented!(), - // } + self.memory.write_int(dest, n) } - // fn eval_binary_op(&mut self, bin_op: mir::BinOp, left: Pointer, right: Pointer, dest: Pointer) - // -> EvalResult<()> { - // use rustc::mir::repr::BinOp::*; - // match (&left.repr, &right.repr, &dest.repr) { - // (&Repr::Int, &Repr::Int, &Repr::Int) => { - // let l = try!(self.memory.read_int(left)); - // let r = try!(self.memory.read_int(right)); - // let n = match bin_op { - // Add => l + r, - // Sub => l - r, - // Mul => l * r, - // Div => l / r, - // Rem => l % r, - // BitXor => l ^ r, - // BitAnd => l & r, - // BitOr => l | r, - // Shl => l << r, - // Shr => l >> r, - // _ => unimplemented!(), - // // Eq => Value::Bool(l == r), - // // Lt => Value::Bool(l < r), - // // Le => Value::Bool(l <= r), - // // Ne => Value::Bool(l != r), - // // Ge => Value::Bool(l >= r), - // // Gt => Value::Bool(l > r), - // }; - // self.memory.write_int(dest, n) - // } - // (l, r, o) => - // panic!("unhandled binary operation: {:?}({:?}, {:?}) into {:?}", bin_op, l, r, o), - // } - // } - fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { @@ -334,8 +283,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.copy(src, dest, dest_repr.size()) } - // BinaryOp(bin_op, ref left, ref right) => - // self.eval_binary_op(lvalue, bin_op, left, right), + BinaryOp(bin_op, ref left, ref right) => + self.eval_binary_op(bin_op, left, right, dest), // UnaryOp(un_op, ref operand) => { // let ptr = try!(self.operand_to_ptr(operand)); @@ -408,6 +357,57 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { + let frame = self.current_frame(); + + use rustc::mir::repr::Lvalue::*; + let ptr = match *lvalue { + ReturnPointer => + frame.return_ptr.expect("ReturnPointer used in a function with no return value"), + Arg(i) => frame.arg_ptr(i), + Var(i) => frame.var_ptr(i), + Temp(i) => frame.temp_ptr(i), + ref l => panic!("can't handle lvalue: {:?}", l), + }; + + Ok(ptr) + + // mir::Lvalue::Projection(ref proj) => { + // let base_ptr = self.lvalue_to_ptr(&proj.base); + + // match proj.elem { + // mir::ProjectionElem::Field(field, _) => { + // base_ptr.offset(field.index()) + // } + + // mir::ProjectionElem::Downcast(_, variant) => { + // let adt_val = self.read_pointer(base_ptr); + // if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { + // debug_assert_eq!(variant, actual_variant); + // data_ptr + // } else { + // panic!("Downcast attempted on non-ADT: {:?}", adt_val) + // } + // } + + // mir::ProjectionElem::Deref => { + // let ptr_val = self.read_pointer(base_ptr); + // if let Value::Pointer(ptr) = ptr_val { + // ptr + // } else { + // panic!("Deref attempted on non-pointer: {:?}", ptr_val) + // } + // } + + // mir::ProjectionElem::Index(ref _operand) => unimplemented!(), + // mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), + // } + // } + + // _ => unimplemented!(), + // } + } + fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { use rustc::middle::const_eval::ConstVal::*; match *const_val { diff --git a/test/new_values.rs b/test/new_values.rs index 64b56b01e171f..8bba137da6c0f 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -11,10 +11,10 @@ fn ret() -> i32 { // -1 // } -// #[miri_run] -// fn add() -> i32 { -// 1 + 2 -// } +#[miri_run] +fn add() -> i32 { + 1 + 2 +} #[miri_run] fn empty() {} @@ -34,17 +34,17 @@ fn tuple_5() -> (i32, i32, i32, i32, i32) { (1, 2, 3, 4, 5) } -// #[miri_run] -// fn indirect_add() -> i32 { -// let x = 1; -// let y = 2; -// x + y -// } +#[miri_run] +fn indirect_add() -> i32 { + let x = 1; + let y = 2; + x + y +} -// #[miri_run] -// fn arith() -> i32 { -// 3*3 + 4*4 -// } +#[miri_run] +fn arith() -> i32 { + 3*3 + 4*4 +} #[miri_run] fn boolean() -> bool { From f72b0c9b12416fb2dc7e0a86b637afba1dffbc29 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 07:50:49 -0600 Subject: [PATCH 0066/1096] Make formatting consistent. --- src/interpreter.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d37a95113bb82..22cbd2397192d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -236,10 +236,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(()) } - fn eval_binary_op( - &mut self, bin_op: mir::BinOp, left_operand: &mir::Operand<'tcx>, - right_operand: &mir::Operand<'tcx>, dest: Pointer - ) -> EvalResult<()> { + fn eval_binary_op(&mut self, bin_op: mir::BinOp, left_operand: &mir::Operand<'tcx>, + right_operand: &mir::Operand<'tcx>, dest: Pointer) -> EvalResult<()> + { // FIXME(tsion): Check for non-integer binary operations. let left = try!(self.operand_to_ptr(left_operand)); let right = try!(self.operand_to_ptr(right_operand)); From e41af43dbf874a4a16e33470cd49ec15ed05799c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 07:57:08 -0600 Subject: [PATCH 0067/1096] Re-add support for integer unary ops. --- src/interpreter.rs | 21 +++++++++++---------- test/new_values.rs | 8 ++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 22cbd2397192d..6ad87ac6694a9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -285,16 +285,17 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { BinaryOp(bin_op, ref left, ref right) => self.eval_binary_op(bin_op, left, right, dest), - // UnaryOp(un_op, ref operand) => { - // let ptr = try!(self.operand_to_ptr(operand)); - // let m = try!(self.memory.read_int(ptr)); - // let n = match (un_op, ptr.repr) { - // (mir::UnOp::Not, Repr::Int) => !m, - // (mir::UnOp::Neg, Repr::Int) => -m, - // (_, ref p) => panic!("unhandled binary operation: {:?}({:?})", un_op, p), - // }; - // self.memory.write_int(dest, n) - // } + UnaryOp(un_op, ref operand) => { + // FIXME(tsion): Check for non-integer operations. + let ptr = try!(self.operand_to_ptr(operand)); + let m = try!(self.memory.read_int(ptr)); + use rustc::mir::repr::UnOp::*; + let n = match un_op { + Not => !m, + Neg => -m, + }; + self.memory.write_int(dest, n) + } Aggregate(mir::AggregateKind::Tuple, ref operands) => { match dest_repr { diff --git a/test/new_values.rs b/test/new_values.rs index 8bba137da6c0f..880ac71abc305 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -6,10 +6,10 @@ fn ret() -> i32 { 1 } -// #[miri_run] -// fn neg() -> i32 { -// -1 -// } +#[miri_run] +fn neg() -> i32 { + -1 +} #[miri_run] fn add() -> i32 { From 0cb7e3fae038f825c1ac0b7faf2f4be0288ba793 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 08:22:18 -0600 Subject: [PATCH 0068/1096] Support SwitchInt for integer types. --- src/interpreter.rs | 31 ++++++++++++++++++++----------- src/memory.rs | 2 +- test/new_values.rs | 46 +++++++++++++++++++++++----------------------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6ad87ac6694a9..27f0081184c11 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -171,8 +171,26 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = try!(self.operand_to_ptr(cond)); - let cond = try!(self.memory.read_bool(cond_ptr)); - current_block = if cond { then_target } else { else_target }; + let cond_val = try!(self.memory.read_bool(cond_ptr)); + current_block = if cond_val { then_target } else { else_target }; + } + + SwitchInt { ref discr, ref values, ref targets, .. } => { + // FIXME(tsion): Handle non-integer switch types. + let discr_ptr = try!(self.lvalue_to_ptr(discr)); + let discr_val = try!(self.memory.read_int(discr_ptr)); + + // Branch to the `otherwise` case by default, if no match is found. + current_block = targets[targets.len() - 1]; + + for (index, val_const) in values.iter().enumerate() { + let ptr = try!(self.const_to_ptr(val_const)); + let val = try!(self.memory.read_int(ptr)); + if discr_val == val { + current_block = targets[index]; + break; + } + } } // Call { ref func, ref args, ref destination, .. } => { @@ -203,15 +221,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // } // } - // SwitchInt { ref discr, ref values, ref targets, .. } => { - // let discr_val = self.read_lvalue(discr); - - // let index = values.iter().position(|v| discr_val == self.const_to_ptr(v)) - // .expect("discriminant matched no values"); - - // current_block = targets[index]; - // } - // Switch { ref discr, ref targets, .. } => { // let discr_val = self.read_lvalue(discr); diff --git a/src/memory.rs b/src/memory.rs index 6ebdf17f82cad..bb6cadd026a10 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -159,7 +159,7 @@ impl Repr { Repr::Aggregate { size: size, fields: fields } }, - _ => unimplemented!(), + ref t => panic!("can't convert type to repr: {:?}", t), } } diff --git a/test/new_values.rs b/test/new_values.rs index 880ac71abc305..b3da0562fc510 100644 --- a/test/new_values.rs +++ b/test/new_values.rs @@ -70,18 +70,18 @@ fn if_true() -> i32 { // increment(1) // } -// // #[miri_run(expected = "Int(3628800)")] -// // fn factorial_loop() -> i32 { -// // let mut product = 1; -// // let mut i = 1; - -// // while i <= 10 { -// // product *= i; -// // i += 1; -// // } +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_loop() -> i32 { +// let mut product = 1; +// let mut i = 1; -// // product -// // } +// while i <= 10 { +// product *= i; +// i += 1; +// } + +// product +// } // #[miri_run(expected = "Int(3628800)")] // fn factorial_recursive() -> i32 { @@ -96,7 +96,7 @@ fn if_true() -> i32 { // fact(10) // } -// #[miri_run(expected = "Int(1)")] +// #[miri_run] // fn match_bool() -> i32 { // let b = true; // match b { @@ -105,17 +105,17 @@ fn if_true() -> i32 { // } // } -// #[miri_run(expected = "Int(20)")] -// fn match_int() -> i32 { -// let n = 2; -// match n { -// 0 => 0, -// 1 => 10, -// 2 => 20, -// 3 => 30, -// _ => 100, -// } -// } +#[miri_run] +fn match_int() -> i32 { + let n = 2; + match n { + 0 => 0, + 1 => 10, + 2 => 20, + 3 => 30, + _ => 100, + } +} // #[miri_run(expected = "Int(1)")] // fn one_line_ref() -> i32 { From b530b0b027c47954961bf8f222d773248d2917f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 08:27:19 -0600 Subject: [PATCH 0069/1096] Write new-value-repr branch tests over old tests. --- test/basic.rs | 229 +++++++++++++++++++++++++-------------------- test/new_values.rs | 197 -------------------------------------- 2 files changed, 125 insertions(+), 301 deletions(-) delete mode 100644 test/new_values.rs diff --git a/test/basic.rs b/test/basic.rs index 70bb9c3bb88a6..b3da0562fc510 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -1,51 +1,74 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -#[miri_run(expected = "Int(1)")] +#[miri_run] fn ret() -> i32 { 1 } -#[miri_run(expected = "Int(-1)")] +#[miri_run] fn neg() -> i32 { -1 } -#[miri_run(expected = "Int(3)")] +#[miri_run] fn add() -> i32 { 1 + 2 } -#[miri_run(expected = "Int(3)")] +#[miri_run] +fn empty() {} + +#[miri_run] +fn tuple() -> (i32,) { + (1,) +} + +#[miri_run] +fn tuple_2() -> (i32, i32) { + (1, 2) +} + +#[miri_run] +fn tuple_5() -> (i32, i32, i32, i32, i32) { + (1, 2, 3, 4, 5) +} + +#[miri_run] fn indirect_add() -> i32 { let x = 1; let y = 2; x + y } -#[miri_run(expected = "Int(25)")] +#[miri_run] fn arith() -> i32 { 3*3 + 4*4 } -#[miri_run(expected = "Int(0)")] +#[miri_run] +fn boolean() -> bool { + true +} + +#[miri_run] fn if_false() -> i32 { if false { 1 } else { 0 } } -#[miri_run(expected = "Int(1)")] +#[miri_run] fn if_true() -> i32 { if true { 1 } else { 0 } } -#[miri_run(expected = "Int(2)")] -fn call() -> i32 { - fn increment(x: i32) -> i32 { - x + 1 - } +// #[miri_run(expected = "Int(2)")] +// fn call() -> i32 { +// fn increment(x: i32) -> i32 { +// x + 1 +// } - increment(1) -} +// increment(1) +// } // #[miri_run(expected = "Int(3628800)")] // fn factorial_loop() -> i32 { @@ -60,29 +83,29 @@ fn call() -> i32 { // product // } -#[miri_run(expected = "Int(3628800)")] -fn factorial_recursive() -> i32 { - fn fact(n: i32) -> i32 { - if n == 0 { - 1 - } else { - n * fact(n - 1) - } - } +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_recursive() -> i32 { +// fn fact(n: i32) -> i32 { +// if n == 0 { +// 1 +// } else { +// n * fact(n - 1) +// } +// } - fact(10) -} +// fact(10) +// } -#[miri_run(expected = "Int(1)")] -fn match_bool() -> i32 { - let b = true; - match b { - true => 1, - false => 0, - } -} +// #[miri_run] +// fn match_bool() -> i32 { +// let b = true; +// match b { +// true => 1, +// false => 0, +// } +// } -#[miri_run(expected = "Int(20)")] +#[miri_run] fn match_int() -> i32 { let n = 2; match n { @@ -94,83 +117,81 @@ fn match_int() -> i32 { } } -#[miri_run(expected = "Int(1)")] -fn one_line_ref() -> i32 { - *&1 -} - -#[miri_run(expected = "Int(1)")] -fn basic_ref() -> i32 { - let x = &1; - *x -} +// #[miri_run(expected = "Int(1)")] +// fn one_line_ref() -> i32 { +// *&1 +// } -#[miri_run(expected = "Int(3)")] -fn basic_ref_mut() -> i32 { - let x = &mut 1; - *x += 2; - *x -} +// #[miri_run(expected = "Int(1)")] +// fn basic_ref() -> i32 { +// let x = &1; +// *x +// } // #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut_var() -> i32 { -// let mut a = 1; -// { -// let x = &mut a; -// *x += 2; -// } -// a +// fn basic_ref_mut() -> i32 { +// let x = &mut 1; +// *x += 2; +// *x // } -#[miri_run(expected = "Int(4)")] -fn match_int_range() -> i32 { - let n = 42; - match n { - 0...9 => 0, - 10...19 => 1, - 20...29 => 2, - 30...39 => 3, - 40...49 => 4, - _ => 5, - } -} - -enum MyOption { - Some { data: T }, - None, -} +// // #[miri_run(expected = "Int(3)")] +// // fn basic_ref_mut_var() -> i32 { +// // let mut a = 1; +// // { +// // let x = &mut a; +// // *x += 2; +// // } +// // a +// // } + +// #[miri_run(expected = "Int(4)")] +// fn match_int_range() -> i32 { +// let n = 42; +// match n { +// 0...9 => 0, +// 10...19 => 1, +// 20...29 => 2, +// 30...39 => 3, +// 40...49 => 4, +// _ => 5, +// } +// } -#[miri_run(expected = "Int(13)")] -fn match_my_opt_some() -> i32 { - let x = MyOption::Some { data: 13 }; - match x { - MyOption::Some { data } => data, - MyOption::None => 42, - } -} +// enum MyOption { +// Some { data: T }, +// None, +// } -#[miri_run(expected = "Int(42)")] -fn match_my_opt_none() -> i32 { - let x = MyOption::None; - match x { - MyOption::Some { data } => data, - MyOption::None => 42, - } -} +// #[miri_run(expected = "Int(13)")] +// fn match_my_opt_some() -> i32 { +// let x = MyOption::Some { data: 13 }; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } -#[miri_run(expected = "Int(13)")] -fn match_opt_some() -> i32 { - let x = Some(13); - match x { - Some(data) => data, - None => 42, - } -} +// #[miri_run(expected = "Int(42)")] +// fn match_my_opt_none() -> i32 { +// let x = MyOption::None; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } -/// Test calling a very simple function from the standard library. -#[miri_run(expected = "Int(1)")] -fn cross_crate_fn_call() -> i32 { - if 1i32.is_positive() { 1 } else { 0 } -} +// #[miri_run(expected = "Int(13)")] +// fn match_opt_some() -> i32 { +// let x = Some(13); +// match x { +// Some(data) => data, +// None => 42, +// } +// } -fn main() {} +// /// Test calling a very simple function from the standard library. +// #[miri_run(expected = "Int(1)")] +// fn cross_crate_fn_call() -> i32 { +// if 1i32.is_positive() { 1 } else { 0 } +// } diff --git a/test/new_values.rs b/test/new_values.rs deleted file mode 100644 index b3da0562fc510..0000000000000 --- a/test/new_values.rs +++ /dev/null @@ -1,197 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] -fn ret() -> i32 { - 1 -} - -#[miri_run] -fn neg() -> i32 { - -1 -} - -#[miri_run] -fn add() -> i32 { - 1 + 2 -} - -#[miri_run] -fn empty() {} - -#[miri_run] -fn tuple() -> (i32,) { - (1,) -} - -#[miri_run] -fn tuple_2() -> (i32, i32) { - (1, 2) -} - -#[miri_run] -fn tuple_5() -> (i32, i32, i32, i32, i32) { - (1, 2, 3, 4, 5) -} - -#[miri_run] -fn indirect_add() -> i32 { - let x = 1; - let y = 2; - x + y -} - -#[miri_run] -fn arith() -> i32 { - 3*3 + 4*4 -} - -#[miri_run] -fn boolean() -> bool { - true -} - -#[miri_run] -fn if_false() -> i32 { - if false { 1 } else { 0 } -} - -#[miri_run] -fn if_true() -> i32 { - if true { 1 } else { 0 } -} - -// #[miri_run(expected = "Int(2)")] -// fn call() -> i32 { -// fn increment(x: i32) -> i32 { -// x + 1 -// } - -// increment(1) -// } - -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_loop() -> i32 { -// let mut product = 1; -// let mut i = 1; - -// while i <= 10 { -// product *= i; -// i += 1; -// } - -// product -// } - -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_recursive() -> i32 { -// fn fact(n: i32) -> i32 { -// if n == 0 { -// 1 -// } else { -// n * fact(n - 1) -// } -// } - -// fact(10) -// } - -// #[miri_run] -// fn match_bool() -> i32 { -// let b = true; -// match b { -// true => 1, -// false => 0, -// } -// } - -#[miri_run] -fn match_int() -> i32 { - let n = 2; - match n { - 0 => 0, - 1 => 10, - 2 => 20, - 3 => 30, - _ => 100, - } -} - -// #[miri_run(expected = "Int(1)")] -// fn one_line_ref() -> i32 { -// *&1 -// } - -// #[miri_run(expected = "Int(1)")] -// fn basic_ref() -> i32 { -// let x = &1; -// *x -// } - -// #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut() -> i32 { -// let x = &mut 1; -// *x += 2; -// *x -// } - -// // #[miri_run(expected = "Int(3)")] -// // fn basic_ref_mut_var() -> i32 { -// // let mut a = 1; -// // { -// // let x = &mut a; -// // *x += 2; -// // } -// // a -// // } - -// #[miri_run(expected = "Int(4)")] -// fn match_int_range() -> i32 { -// let n = 42; -// match n { -// 0...9 => 0, -// 10...19 => 1, -// 20...29 => 2, -// 30...39 => 3, -// 40...49 => 4, -// _ => 5, -// } -// } - -// enum MyOption { -// Some { data: T }, -// None, -// } - -// #[miri_run(expected = "Int(13)")] -// fn match_my_opt_some() -> i32 { -// let x = MyOption::Some { data: 13 }; -// match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, -// } -// } - -// #[miri_run(expected = "Int(42)")] -// fn match_my_opt_none() -> i32 { -// let x = MyOption::None; -// match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, -// } -// } - -// #[miri_run(expected = "Int(13)")] -// fn match_opt_some() -> i32 { -// let x = Some(13); -// match x { -// Some(data) => data, -// None => 42, -// } -// } - -// /// Test calling a very simple function from the standard library. -// #[miri_run(expected = "Int(1)")] -// fn cross_crate_fn_call() -> i32 { -// if 1i32.is_positive() { 1 } else { 0 } -// } From fd10ec9278ab302ef4223b3b7921ad062f4396a7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 7 Mar 2016 10:14:47 -0600 Subject: [PATCH 0070/1096] Clean up unused things. --- src/interpreter.rs | 48 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 27f0081184c11..d4e2d949b5d6d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,21 +1,11 @@ -// TODO(tsion): Remove this. -#![allow(unused_imports, dead_code, unused_variables)] - -use byteorder::{self, ByteOrder}; use rustc::middle::const_eval; -use rustc::middle::cstore::CrateStore; -use rustc::middle::def_id; use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; -use rustc::mir::repr::{self as mir, Mir}; -use std::collections::HashMap; +use rustc::mir::repr as mir; use std::error::Error; use std::fmt; -use std::iter; -use syntax::ast::Attribute; -use syntax::attr::AttrMetaMethods; -use memory::{self, Pointer, Repr, Allocation}; +use memory::{Memory, Pointer, Repr}; const TRACE_EXECUTION: bool = true; @@ -59,7 +49,7 @@ impl fmt::Display for EvalError { /// A stack frame. struct Frame<'a, 'tcx: 'a> { /// The MIR for the fucntion called on this frame. - mir: &'a Mir<'tcx>, + mir: &'a mir::Mir<'tcx>, /// A pointer for writing the return value of the current call, if it's not a diverging call. return_ptr: Option, @@ -93,7 +83,7 @@ impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { struct Interpreter<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, - memory: memory::Memory, + memory: Memory, stack: Vec>, } @@ -102,12 +92,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Interpreter { tcx: tcx, mir_map: mir_map, - memory: memory::Memory::new(), + memory: Memory::new(), stack: Vec::new(), } } - fn push_stack_frame(&mut self, mir: &'a Mir<'tcx>, args: &[&mir::Operand<'tcx>], + fn push_stack_frame(&mut self, mir: &'a mir::Mir<'tcx>, args: &[&mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); @@ -146,7 +136,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn call(&mut self, mir: &'a Mir<'tcx>, args: &[&mir::Operand<'tcx>], + fn call(&mut self, mir: &'a mir::Mir<'tcx>, args: &[&mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { try!(self.push_stack_frame(mir, args, return_ptr)); let mut current_block = mir::START_BLOCK; @@ -194,6 +184,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } // Call { ref func, ref args, ref destination, .. } => { + // use rustc::middle::cstore::CrateStore; // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); // let func_val = self.operand_to_ptr(func); @@ -357,7 +348,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match constant.literal { Value { ref value } => self.const_to_ptr(value), - Item { def_id, kind, .. } => match kind { + Item { kind, .. } => match kind { // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), _ => panic!("can't handle item literal: {:?}", constant.literal), }, @@ -450,6 +441,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { + use syntax::attr::AttrMetaMethods; if attr.check_name("miri_run") { let item = tcx.map.expect_item(id); @@ -469,23 +461,3 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } } } - -fn check_expected(actual: &str, attr: &Attribute) -> bool { - if let Some(meta_items) = attr.meta_item_list() { - for meta_item in meta_items { - if meta_item.check_name("expected") { - let expected = meta_item.value_str().unwrap(); - - if actual == &expected[..] { - println!("Test passed!\n"); - } else { - println!("Actual value:\t{}\nExpected value:\t{}\n", actual, expected); - } - - return true; - } - } - } - - false -} From 8a0b95bc8bbca2b11c3dbfdc418f88cc96e62398 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 11 Mar 2016 21:27:54 -0600 Subject: [PATCH 0071/1096] Support structs and single-variant enums. --- src/interpreter.rs | 78 ++++++++++++++++++++++++++++++++-------------- src/memory.rs | 23 -------------- test/basic.rs | 7 +++++ 3 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d4e2d949b5d6d..2590cd656a4d8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -5,7 +5,7 @@ use rustc::mir::repr as mir; use std::error::Error; use std::fmt; -use memory::{Memory, Pointer, Repr}; +use memory::{FieldRepr, Memory, Pointer, Repr}; const TRACE_EXECUTION: bool = true; @@ -107,7 +107,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let mut locals = Vec::with_capacity(num_args + num_vars + num_temps); for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { - let repr = Repr::from_ty(arg_decl.ty); + let repr = self.ty_to_repr(arg_decl.ty); let dest = self.memory.allocate(repr.size()); let src = try!(self.operand_to_ptr(arg_operand)); try!(self.memory.copy(src, dest, repr.size())); @@ -117,7 +117,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); locals.extend(var_tys.chain(temp_tys).map(|ty| { - self.memory.allocate(Repr::from_ty(ty).size()) + let repr = self.ty_to_repr(ty).size(); + self.memory.allocate(repr) })); self.stack.push(Frame { @@ -273,7 +274,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { { let dest = try!(self.lvalue_to_ptr(lvalue)); let dest_ty = self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); - let dest_repr = Repr::from_ty(dest_ty); + let dest_repr = self.ty_to_repr(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -297,7 +298,21 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_int(dest, n) } - Aggregate(mir::AggregateKind::Tuple, ref operands) => { + Aggregate(ref _kind, ref operands) => { + // TODO(tsion): Handle different `kind` variants. + + // let max_fields = adt_def.variants + // .iter() + // .map(|v| v.fields.len()) + // .max() + // .unwrap_or(0); + // let ptr = self.allocate_aggregate(max_fields); + // for (i, operand) in operands.iter().enumerate() { + // let val = self.operand_to_ptr(operand); + // self.write_pointer(ptr.offset(i), val); + // } + // Value::Adt { variant: variant, data_ptr: ptr } + match dest_repr { Repr::Aggregate { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { @@ -316,24 +331,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // Value::Pointer(self.lvalue_to_ptr(lvalue)) // } - // Aggregate(mir::AggregateKind::Adt(ref adt_def, variant, _substs), - // ref operands) => { - // let max_fields = adt_def.variants - // .iter() - // .map(|v| v.fields.len()) - // .max() - // .unwrap_or(0); - - // let ptr = self.allocate_aggregate(max_fields); - - // for (i, operand) in operands.iter().enumerate() { - // let val = self.operand_to_ptr(operand); - // self.write_pointer(ptr.offset(i), val); - // } - - // Value::Adt { variant: variant, data_ptr: ptr } - // } - ref r => panic!("can't handle rvalue: {:?}", r), } } @@ -433,6 +430,36 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + fn make_aggregate_repr(&self, iter: I) -> Repr where I: IntoIterator> { + let mut size = 0; + let fields = iter.into_iter().map(|ty| { + let repr = self.ty_to_repr(ty); + let old_size = size; + size += repr.size(); + FieldRepr { offset: old_size, repr: repr } + }).collect(); + Repr::Aggregate { size: size, fields: fields } + } + + // TODO(tsion): Cache these outputs. + fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { + match ty.sty { + ty::TyBool => Repr::Bool, + ty::TyInt(_) => Repr::Int, + ty::TyTuple(ref fields) => self.make_aggregate_repr(fields.iter().cloned()), + + ty::TyEnum(adt_def, ref subst) | ty::TyStruct(adt_def, ref subst) => { + // TODO(tsion): Support multi-variant enums. + assert!(adt_def.variants.len() == 1); + let field_tys = adt_def.variants[0].fields.iter().map(|f| f.ty(self.tcx, subst)); + self.make_aggregate_repr(field_tys) + } + + ref t => panic!("can't convert type to repr: {:?}", t), + } + } + + fn current_frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } @@ -449,7 +476,10 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => Some(miri.memory.allocate(Repr::from_ty(ty).size())), + ty::FnConverging(ty) => { + let repr = miri.ty_to_repr(ty).size(); + Some(miri.memory.allocate(repr)) + } ty::FnDiverging => None, }; miri.call(mir, &[], return_ptr).unwrap(); diff --git a/src/memory.rs b/src/memory.rs index bb6cadd026a10..744424a1ffb33 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,4 @@ use byteorder::{self, ByteOrder}; -use rustc::middle::ty; use std::collections::HashMap; use std::mem; use std::ptr; @@ -141,28 +140,6 @@ impl Pointer { } impl Repr { - // TODO(tsion): Cache these outputs. - pub fn from_ty(ty: ty::Ty) -> Self { - match ty.sty { - ty::TyBool => Repr::Bool, - - ty::TyInt(_) => Repr::Int, - - ty::TyTuple(ref fields) => { - let mut size = 0; - let fields = fields.iter().map(|ty| { - let repr = Repr::from_ty(ty); - let old_size = size; - size += repr.size(); - FieldRepr { offset: old_size, repr: repr } - }).collect(); - Repr::Aggregate { size: size, fields: fields } - }, - - ref t => panic!("can't convert type to repr: {:?}", t), - } - } - pub fn size(&self) -> usize { match *self { Repr::Bool => 1, diff --git a/test/basic.rs b/test/basic.rs index b3da0562fc510..795915a504112 100644 --- a/test/basic.rs +++ b/test/basic.rs @@ -61,6 +61,13 @@ fn if_true() -> i32 { if true { 1 } else { 0 } } +struct Pair { x: i64, y: i64 } + +#[miri_run] +fn pair() -> Pair { + Pair { x: 10, y: 20 } +} + // #[miri_run(expected = "Int(2)")] // fn call() -> i32 { // fn increment(x: i32) -> i32 { From bffbb89354288f38410d08443071fc2653b1b8d5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 20:23:48 -0600 Subject: [PATCH 0072/1096] Update for changes in rustc master. --- src/interpreter.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2590cd656a4d8..e807c2c2a151f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -344,11 +344,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { use rustc::mir::repr::Literal::*; match constant.literal { Value { ref value } => self.const_to_ptr(value), - - Item { kind, .. } => match kind { - // mir::ItemKind::Function | mir::ItemKind::Method => Value::Func(def_id), - _ => panic!("can't handle item literal: {:?}", constant.literal), - }, + ref l => panic!("can't handle item literal: {:?}", l), } } } From 346618cd2bac98198944341583103c16486f5a2c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 21:32:05 -0600 Subject: [PATCH 0073/1096] Fix 0-sized writes being treated as out of bounds. --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 744424a1ffb33..93e0a6d509e16 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -124,7 +124,7 @@ impl Memory { impl Allocation { fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { - if start < self.bytes.len() && end <= self.bytes.len() { + if start <= self.bytes.len() && end <= self.bytes.len() { Ok(()) } else { Err(EvalError::PointerOutOfBounds) From 11d4bf9b954d9fe3cdba1971b56ef7b69e708b24 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 21:32:24 -0600 Subject: [PATCH 0074/1096] Split tests into multiple files. --- test/basic.rs | 204 ----------------------------------------------- test/bools.rs | 26 ++++++ test/calls.rs | 30 +++++++ test/ints.rs | 54 +++++++++++++ test/loops.rs | 15 ++++ test/pointers.rs | 30 +++++++ test/products.rs | 24 ++++++ test/sums.rs | 34 ++++++++ test/trivial.rs | 11 +++ 9 files changed, 224 insertions(+), 204 deletions(-) delete mode 100644 test/basic.rs create mode 100755 test/bools.rs create mode 100644 test/calls.rs create mode 100644 test/ints.rs create mode 100644 test/loops.rs create mode 100644 test/pointers.rs create mode 100644 test/products.rs create mode 100644 test/sums.rs create mode 100644 test/trivial.rs diff --git a/test/basic.rs b/test/basic.rs deleted file mode 100644 index 795915a504112..0000000000000 --- a/test/basic.rs +++ /dev/null @@ -1,204 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] -fn ret() -> i32 { - 1 -} - -#[miri_run] -fn neg() -> i32 { - -1 -} - -#[miri_run] -fn add() -> i32 { - 1 + 2 -} - -#[miri_run] -fn empty() {} - -#[miri_run] -fn tuple() -> (i32,) { - (1,) -} - -#[miri_run] -fn tuple_2() -> (i32, i32) { - (1, 2) -} - -#[miri_run] -fn tuple_5() -> (i32, i32, i32, i32, i32) { - (1, 2, 3, 4, 5) -} - -#[miri_run] -fn indirect_add() -> i32 { - let x = 1; - let y = 2; - x + y -} - -#[miri_run] -fn arith() -> i32 { - 3*3 + 4*4 -} - -#[miri_run] -fn boolean() -> bool { - true -} - -#[miri_run] -fn if_false() -> i32 { - if false { 1 } else { 0 } -} - -#[miri_run] -fn if_true() -> i32 { - if true { 1 } else { 0 } -} - -struct Pair { x: i64, y: i64 } - -#[miri_run] -fn pair() -> Pair { - Pair { x: 10, y: 20 } -} - -// #[miri_run(expected = "Int(2)")] -// fn call() -> i32 { -// fn increment(x: i32) -> i32 { -// x + 1 -// } - -// increment(1) -// } - -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_loop() -> i32 { -// let mut product = 1; -// let mut i = 1; - -// while i <= 10 { -// product *= i; -// i += 1; -// } - -// product -// } - -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_recursive() -> i32 { -// fn fact(n: i32) -> i32 { -// if n == 0 { -// 1 -// } else { -// n * fact(n - 1) -// } -// } - -// fact(10) -// } - -// #[miri_run] -// fn match_bool() -> i32 { -// let b = true; -// match b { -// true => 1, -// false => 0, -// } -// } - -#[miri_run] -fn match_int() -> i32 { - let n = 2; - match n { - 0 => 0, - 1 => 10, - 2 => 20, - 3 => 30, - _ => 100, - } -} - -// #[miri_run(expected = "Int(1)")] -// fn one_line_ref() -> i32 { -// *&1 -// } - -// #[miri_run(expected = "Int(1)")] -// fn basic_ref() -> i32 { -// let x = &1; -// *x -// } - -// #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut() -> i32 { -// let x = &mut 1; -// *x += 2; -// *x -// } - -// // #[miri_run(expected = "Int(3)")] -// // fn basic_ref_mut_var() -> i32 { -// // let mut a = 1; -// // { -// // let x = &mut a; -// // *x += 2; -// // } -// // a -// // } - -// #[miri_run(expected = "Int(4)")] -// fn match_int_range() -> i32 { -// let n = 42; -// match n { -// 0...9 => 0, -// 10...19 => 1, -// 20...29 => 2, -// 30...39 => 3, -// 40...49 => 4, -// _ => 5, -// } -// } - -// enum MyOption { -// Some { data: T }, -// None, -// } - -// #[miri_run(expected = "Int(13)")] -// fn match_my_opt_some() -> i32 { -// let x = MyOption::Some { data: 13 }; -// match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, -// } -// } - -// #[miri_run(expected = "Int(42)")] -// fn match_my_opt_none() -> i32 { -// let x = MyOption::None; -// match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, -// } -// } - -// #[miri_run(expected = "Int(13)")] -// fn match_opt_some() -> i32 { -// let x = Some(13); -// match x { -// Some(data) => data, -// None => 42, -// } -// } - -// /// Test calling a very simple function from the standard library. -// #[miri_run(expected = "Int(1)")] -// fn cross_crate_fn_call() -> i32 { -// if 1i32.is_positive() { 1 } else { 0 } -// } diff --git a/test/bools.rs b/test/bools.rs new file mode 100755 index 0000000000000..afa71d3a7e850 --- /dev/null +++ b/test/bools.rs @@ -0,0 +1,26 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn boolean() -> bool { + true +} + +#[miri_run] +fn if_false() -> i32 { + if false { 1 } else { 0 } +} + +#[miri_run] +fn if_true() -> i32 { + if true { 1 } else { 0 } +} + +// #[miri_run] +// fn match_bool() -> i32 { +// let b = true; +// match b { +// true => 1, +// false => 0, +// } +// } diff --git a/test/calls.rs b/test/calls.rs new file mode 100644 index 0000000000000..548acb49c665b --- /dev/null +++ b/test/calls.rs @@ -0,0 +1,30 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// #[miri_run(expected = "Int(2)")] +// fn call() -> i32 { +// fn increment(x: i32) -> i32 { +// x + 1 +// } + +// increment(1) +// } + +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_recursive() -> i32 { +// fn fact(n: i32) -> i32 { +// if n == 0 { +// 1 +// } else { +// n * fact(n - 1) +// } +// } + +// fact(10) +// } + +// Test calling a very simple function from the standard library. +// #[miri_run(expected = "Int(1)")] +// fn cross_crate_fn_call() -> i32 { +// if 1i32.is_positive() { 1 } else { 0 } +// } diff --git a/test/ints.rs b/test/ints.rs new file mode 100644 index 0000000000000..0c89b6e5a8330 --- /dev/null +++ b/test/ints.rs @@ -0,0 +1,54 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn ret() -> i32 { + 1 +} + +#[miri_run] +fn neg() -> i32 { + -1 +} + +#[miri_run] +fn add() -> i32 { + 1 + 2 +} + +#[miri_run] +fn indirect_add() -> i32 { + let x = 1; + let y = 2; + x + y +} + +#[miri_run] +fn arith() -> i32 { + 3*3 + 4*4 +} + +#[miri_run] +fn match_int() -> i32 { + let n = 2; + match n { + 0 => 0, + 1 => 10, + 2 => 20, + 3 => 30, + _ => 100, + } +} + +// #[miri_run(expected = "Int(4)")] +// fn match_int_range() -> i32 { +// let n = 42; +// match n { +// 0...9 => 0, +// 10...19 => 1, +// 20...29 => 2, +// 30...39 => 3, +// 40...49 => 4, +// _ => 5, +// } +// } diff --git a/test/loops.rs b/test/loops.rs new file mode 100644 index 0000000000000..3a5a66e40ac4f --- /dev/null +++ b/test/loops.rs @@ -0,0 +1,15 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// #[miri_run(expected = "Int(3628800)")] +// fn factorial_loop() -> i32 { +// let mut product = 1; +// let mut i = 1; + +// while i <= 10 { +// product *= i; +// i += 1; +// } + +// product +// } diff --git a/test/pointers.rs b/test/pointers.rs new file mode 100644 index 0000000000000..af825a1f2efa5 --- /dev/null +++ b/test/pointers.rs @@ -0,0 +1,30 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// #[miri_run(expected = "Int(1)")] +// fn one_line_ref() -> i32 { +// *&1 +// } + +// #[miri_run(expected = "Int(1)")] +// fn basic_ref() -> i32 { +// let x = &1; +// *x +// } + +// #[miri_run(expected = "Int(3)")] +// fn basic_ref_mut() -> i32 { +// let x = &mut 1; +// *x += 2; +// *x +// } + +// #[miri_run(expected = "Int(3)")] +// fn basic_ref_mut_var() -> i32 { +// let mut a = 1; +// { +// let x = &mut a; +// *x += 2; +// } +// a +// } diff --git a/test/products.rs b/test/products.rs new file mode 100644 index 0000000000000..287b64339aef6 --- /dev/null +++ b/test/products.rs @@ -0,0 +1,24 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn tuple() -> (i32,) { + (1,) +} + +#[miri_run] +fn tuple_2() -> (i32, i32) { + (1, 2) +} + +#[miri_run] +fn tuple_5() -> (i32, i32, i32, i32, i32) { + (1, 2, 3, 4, 5) +} + +struct Pair { x: i64, y: i64 } + +#[miri_run] +fn pair() -> Pair { + Pair { x: 10, y: 20 } +} diff --git a/test/sums.rs b/test/sums.rs new file mode 100644 index 0000000000000..4f0b9a8eb7c2a --- /dev/null +++ b/test/sums.rs @@ -0,0 +1,34 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// enum MyOption { +// Some { data: T }, +// None, +// } + +// #[miri_run(expected = "Int(13)")] +// fn match_my_opt_some() -> i32 { +// let x = MyOption::Some { data: 13 }; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } + +// #[miri_run(expected = "Int(42)")] +// fn match_my_opt_none() -> i32 { +// let x = MyOption::None; +// match x { +// MyOption::Some { data } => data, +// MyOption::None => 42, +// } +// } + +// #[miri_run(expected = "Int(13)")] +// fn match_opt_some() -> i32 { +// let x = Some(13); +// match x { +// Some(data) => data, +// None => 42, +// } +// } diff --git a/test/trivial.rs b/test/trivial.rs new file mode 100644 index 0000000000000..99a1ef06186a1 --- /dev/null +++ b/test/trivial.rs @@ -0,0 +1,11 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn empty() {} + +#[miri_run] +fn unit_var() { + let x = (); + x +} From 13700085768bb53aecaca9d4d96948b125db9740 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 22:15:59 -0600 Subject: [PATCH 0075/1096] Restructure aggregates and pave the way for enums. --- src/interpreter.rs | 86 ++++++++++++++++++++++++++++++++++------------ src/memory.rs | 26 ++++++++++++-- test/sums.rs | 16 ++++----- 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e807c2c2a151f..c8e73e9fb2298 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -269,6 +269,20 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_int(dest, n) } + fn assign_to_product(&mut self, dest: Pointer, dest_repr: Repr, + operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { + match dest_repr { + Repr::Product { ref fields, .. } => { + for (field, operand) in fields.iter().zip(operands) { + let src = try!(self.operand_to_ptr(operand)); + try!(self.memory.copy(src, dest.offset(field.offset), field.repr.size())); + } + } + _ => panic!("expected Repr::Product target"), + } + Ok(()) + } + fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { @@ -298,8 +312,23 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_int(dest, n) } - Aggregate(ref _kind, ref operands) => { - // TODO(tsion): Handle different `kind` variants. + Aggregate(ref kind, ref operands) => { + use rustc::mir::repr::AggregateKind::*; + match *kind { + Tuple => self.assign_to_product(dest, dest_repr, operands), + + Adt(ref adt_def, variant_idx, _) => { + use rustc::middle::ty::AdtKind::*; + match adt_def.adt_kind() { + Struct => self.assign_to_product(dest, dest_repr, operands), + + Enum => unimplemented!(), + } + } + + Vec => unimplemented!(), + Closure(..) => unimplemented!(), + } // let max_fields = adt_def.variants // .iter() @@ -312,19 +341,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // self.write_pointer(ptr.offset(i), val); // } // Value::Adt { variant: variant, data_ptr: ptr } - - match dest_repr { - Repr::Aggregate { ref fields, .. } => { - for (field, operand) in fields.iter().zip(operands) { - let src = try!(self.operand_to_ptr(operand)); - try!(self.memory.copy(src, dest.offset(field.offset), - field.repr.size())); - } - Ok(()) - } - - _ => unimplemented!(), - } } // Ref(_region, _kind, ref lvalue) => { @@ -426,7 +442,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn make_aggregate_repr(&self, iter: I) -> Repr where I: IntoIterator> { + fn make_product_repr(&self, iter: I) -> Repr where I: IntoIterator> { let mut size = 0; let fields = iter.into_iter().map(|ty| { let repr = self.ty_to_repr(ty); @@ -434,7 +450,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { size += repr.size(); FieldRepr { offset: old_size, repr: repr } }).collect(); - Repr::Aggregate { size: size, fields: fields } + Repr::Product { size: size, fields: fields } } // TODO(tsion): Cache these outputs. @@ -442,13 +458,39 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match ty.sty { ty::TyBool => Repr::Bool, ty::TyInt(_) => Repr::Int, - ty::TyTuple(ref fields) => self.make_aggregate_repr(fields.iter().cloned()), + ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), + + ty::TyEnum(adt_def, ref subst) => { + let num_variants = adt_def.variants.len(); + + let discr_size = if num_variants <= 1 { + 0 + } else if num_variants <= 1 << 8 { + 1 + } else if num_variants <= 1 << 16 { + 2 + } else if num_variants <= 1 << 32 { + 4 + } else { + 8 + }; + + let variants: Vec = adt_def.variants.iter().map(|v| { + let field_tys = v.fields.iter().map(|f| f.ty(self.tcx, subst)); + self.make_product_repr(field_tys) + }).collect(); + + Repr::Sum { + discr_size: discr_size, + max_variant_size: variants.iter().map(Repr::size).max().unwrap_or(0), + variants: variants, + } + } - ty::TyEnum(adt_def, ref subst) | ty::TyStruct(adt_def, ref subst) => { - // TODO(tsion): Support multi-variant enums. - assert!(adt_def.variants.len() == 1); + ty::TyStruct(adt_def, ref subst) => { + assert_eq!(adt_def.variants.len(), 1); let field_tys = adt_def.variants[0].fields.iter().map(|f| f.ty(self.tcx, subst)); - self.make_aggregate_repr(field_tys) + self.make_product_repr(field_tys) } ref t => panic!("can't convert type to repr: {:?}", t), diff --git a/src/memory.rs b/src/memory.rs index 93e0a6d509e16..352f19dcdf022 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -36,10 +36,31 @@ pub struct FieldRepr { pub enum Repr { Bool, Int, - Aggregate { + + /// The representation for product types including tuples, structs, and the contents of enum + /// variants. + Product { + /// Size in bytes. size: usize, fields: Vec, }, + + /// The representation for a sum type, i.e. a Rust enum. + Sum { + /// The size of the discriminant in bytes. + discr_size: usize, + + /// The size of the largest variant in bytes. + max_variant_size: usize, + + variants: Vec, + }, + + // Array { + // /// Number of elements. + // length: usize, + // elem: Repr, + // }, } impl Memory { @@ -144,7 +165,8 @@ impl Repr { match *self { Repr::Bool => 1, Repr::Int => mem::size_of::(), - Repr::Aggregate { size, .. } => size, + Repr::Product { size, .. } => size, + Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, } } } diff --git a/test/sums.rs b/test/sums.rs index 4f0b9a8eb7c2a..bb01e64ae047b 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -24,11 +24,11 @@ // } // } -// #[miri_run(expected = "Int(13)")] -// fn match_opt_some() -> i32 { -// let x = Some(13); -// match x { -// Some(data) => data, -// None => 42, -// } -// } +#[miri_run] +fn match_opt_some() -> i64 { + let x = Some(13); + match x { + Some(data) => data, + None => 42, + } +} From 7cda22f8c55c97bdfd49fef69c484325e9256fb8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 22:27:54 -0600 Subject: [PATCH 0076/1096] Add initial enum initialization support. --- src/interpreter.rs | 40 +++++++++++++++++----------------------- test/sums.rs | 42 +++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c8e73e9fb2298..6f559e97688e4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -269,9 +269,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_int(dest, n) } - fn assign_to_product(&mut self, dest: Pointer, dest_repr: Repr, + fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr, operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { - match dest_repr { + match *dest_repr { Repr::Product { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { let src = try!(self.operand_to_ptr(operand)); @@ -315,32 +315,26 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Aggregate(ref kind, ref operands) => { use rustc::mir::repr::AggregateKind::*; match *kind { - Tuple => self.assign_to_product(dest, dest_repr, operands), - - Adt(ref adt_def, variant_idx, _) => { - use rustc::middle::ty::AdtKind::*; - match adt_def.adt_kind() { - Struct => self.assign_to_product(dest, dest_repr, operands), - - Enum => unimplemented!(), + Tuple => self.assign_to_product(dest, &dest_repr, operands), + + Adt(ref adt_def, variant_idx, _) => match adt_def.adt_kind() { + ty::AdtKind::Struct => self.assign_to_product(dest, &dest_repr, operands), + + ty::AdtKind::Enum => match dest_repr { + Repr::Sum { discr_size, ref variants, .. } => + // TODO(tsion): Write the discriminant value. + self.assign_to_product( + dest.offset(discr_size), + &variants[variant_idx], + operands + ), + _ => panic!("expected Repr::Sum target"), } - } + }, Vec => unimplemented!(), Closure(..) => unimplemented!(), } - - // let max_fields = adt_def.variants - // .iter() - // .map(|v| v.fields.len()) - // .max() - // .unwrap_or(0); - // let ptr = self.allocate_aggregate(max_fields); - // for (i, operand) in operands.iter().enumerate() { - // let val = self.operand_to_ptr(operand); - // self.write_pointer(ptr.offset(i), val); - // } - // Value::Adt { variant: variant, data_ptr: ptr } } // Ref(_region, _kind, ref lvalue) => { diff --git a/test/sums.rs b/test/sums.rs index bb01e64ae047b..e6545e81c00b8 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -1,34 +1,30 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -// enum MyOption { -// Some { data: T }, -// None, -// } +#[miri_run] +fn return_none() -> Option { + None +} -// #[miri_run(expected = "Int(13)")] -// fn match_my_opt_some() -> i32 { -// let x = MyOption::Some { data: 13 }; +#[miri_run] +fn return_some() -> Option { + Some(42) +} + +// #[miri_run] +// fn match_opt_none() -> i64 { +// let x = None, // match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, +// Some(data) => data, +// None => 42, // } // } -// #[miri_run(expected = "Int(42)")] -// fn match_my_opt_none() -> i32 { -// let x = MyOption::None; +// #[miri_run] +// fn match_opt_some() -> i64 { +// let x = Some(13); // match x { -// MyOption::Some { data } => data, -// MyOption::None => 42, +// Some(data) => data, +// None => 42, // } // } - -#[miri_run] -fn match_opt_some() -> i64 { - let x = Some(13); - match x { - Some(data) => data, - None => 42, - } -} From 3f96b3a12207ce3b60edffc6d591b738526c4b55 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 22:39:09 -0600 Subject: [PATCH 0077/1096] Use i64 instead of i32 in tests. Miri's only integer representation right now is 64-bit. --- test/bools.rs | 6 +++--- test/calls.rs | 12 ++++++------ test/ints.rs | 14 +++++++------- test/loops.rs | 2 +- test/pointers.rs | 8 ++++---- test/products.rs | 6 +++--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/bools.rs b/test/bools.rs index afa71d3a7e850..416d4e5f5fee1 100755 --- a/test/bools.rs +++ b/test/bools.rs @@ -7,17 +7,17 @@ fn boolean() -> bool { } #[miri_run] -fn if_false() -> i32 { +fn if_false() -> i64 { if false { 1 } else { 0 } } #[miri_run] -fn if_true() -> i32 { +fn if_true() -> i64 { if true { 1 } else { 0 } } // #[miri_run] -// fn match_bool() -> i32 { +// fn match_bool() -> i64 { // let b = true; // match b { // true => 1, diff --git a/test/calls.rs b/test/calls.rs index 548acb49c665b..9cdba3737828e 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -2,8 +2,8 @@ #![allow(dead_code, unused_attributes)] // #[miri_run(expected = "Int(2)")] -// fn call() -> i32 { -// fn increment(x: i32) -> i32 { +// fn call() -> i64 { +// fn increment(x: i64) -> i64 { // x + 1 // } @@ -11,8 +11,8 @@ // } // #[miri_run(expected = "Int(3628800)")] -// fn factorial_recursive() -> i32 { -// fn fact(n: i32) -> i32 { +// fn factorial_recursive() -> i64 { +// fn fact(n: i64) -> i64 { // if n == 0 { // 1 // } else { @@ -25,6 +25,6 @@ // Test calling a very simple function from the standard library. // #[miri_run(expected = "Int(1)")] -// fn cross_crate_fn_call() -> i32 { -// if 1i32.is_positive() { 1 } else { 0 } +// fn cross_crate_fn_call() -> i64 { +// if 1i64.is_positive() { 1 } else { 0 } // } diff --git a/test/ints.rs b/test/ints.rs index 0c89b6e5a8330..80f842d3768dc 100644 --- a/test/ints.rs +++ b/test/ints.rs @@ -2,34 +2,34 @@ #![allow(dead_code, unused_attributes)] #[miri_run] -fn ret() -> i32 { +fn ret() -> i64 { 1 } #[miri_run] -fn neg() -> i32 { +fn neg() -> i64 { -1 } #[miri_run] -fn add() -> i32 { +fn add() -> i64 { 1 + 2 } #[miri_run] -fn indirect_add() -> i32 { +fn indirect_add() -> i64 { let x = 1; let y = 2; x + y } #[miri_run] -fn arith() -> i32 { +fn arith() -> i64 { 3*3 + 4*4 } #[miri_run] -fn match_int() -> i32 { +fn match_int() -> i64 { let n = 2; match n { 0 => 0, @@ -41,7 +41,7 @@ fn match_int() -> i32 { } // #[miri_run(expected = "Int(4)")] -// fn match_int_range() -> i32 { +// fn match_int_range() -> i64 { // let n = 42; // match n { // 0...9 => 0, diff --git a/test/loops.rs b/test/loops.rs index 3a5a66e40ac4f..c5baa414454ee 100644 --- a/test/loops.rs +++ b/test/loops.rs @@ -2,7 +2,7 @@ #![allow(dead_code, unused_attributes)] // #[miri_run(expected = "Int(3628800)")] -// fn factorial_loop() -> i32 { +// fn factorial_loop() -> i64 { // let mut product = 1; // let mut i = 1; diff --git a/test/pointers.rs b/test/pointers.rs index af825a1f2efa5..6dbd633d83f55 100644 --- a/test/pointers.rs +++ b/test/pointers.rs @@ -2,25 +2,25 @@ #![allow(dead_code, unused_attributes)] // #[miri_run(expected = "Int(1)")] -// fn one_line_ref() -> i32 { +// fn one_line_ref() -> i64 { // *&1 // } // #[miri_run(expected = "Int(1)")] -// fn basic_ref() -> i32 { +// fn basic_ref() -> i64 { // let x = &1; // *x // } // #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut() -> i32 { +// fn basic_ref_mut() -> i64 { // let x = &mut 1; // *x += 2; // *x // } // #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut_var() -> i32 { +// fn basic_ref_mut_var() -> i64 { // let mut a = 1; // { // let x = &mut a; diff --git a/test/products.rs b/test/products.rs index 287b64339aef6..afe161830929e 100644 --- a/test/products.rs +++ b/test/products.rs @@ -2,17 +2,17 @@ #![allow(dead_code, unused_attributes)] #[miri_run] -fn tuple() -> (i32,) { +fn tuple() -> (i64,) { (1,) } #[miri_run] -fn tuple_2() -> (i32, i32) { +fn tuple_2() -> (i64, i64) { (1, 2) } #[miri_run] -fn tuple_5() -> (i32, i32, i32, i32, i32) { +fn tuple_5() -> (i64, i64, i64, i64, i64) { (1, 2, 3, 4, 5) } From 397dbd909ab1a91bc2136ba32ebd9521977a42a1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 12 Mar 2016 23:15:53 -0600 Subject: [PATCH 0078/1096] Add initial support for different int sizes. --- src/interpreter.rs | 19 ++++++++++++++----- src/memory.rs | 15 ++++++++++----- test/ints.rs | 2 +- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6f559e97688e4..c33c7874fbfdc 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -5,7 +5,7 @@ use rustc::mir::repr as mir; use std::error::Error; use std::fmt; -use memory::{FieldRepr, Memory, Pointer, Repr}; +use memory::{FieldRepr, IntRepr, Memory, Pointer, Repr}; const TRACE_EXECUTION: bool = true; @@ -321,13 +321,14 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::AdtKind::Struct => self.assign_to_product(dest, &dest_repr, operands), ty::AdtKind::Enum => match dest_repr { - Repr::Sum { discr_size, ref variants, .. } => + Repr::Sum { discr_size, ref variants, .. } => { // TODO(tsion): Write the discriminant value. self.assign_to_product( dest.offset(discr_size), &variants[variant_idx], operands - ), + ) + } _ => panic!("expected Repr::Sum target"), } }, @@ -416,7 +417,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *const_val { Float(_f) => unimplemented!(), Int(n) => { - let ptr = self.memory.allocate(Repr::Int.size()); + // TODO(tsion): Check int constant type. + let ptr = self.memory.allocate(8); try!(self.memory.write_int(ptr, n)); Ok(ptr) } @@ -449,9 +451,16 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { + use syntax::ast::IntTy; match ty.sty { ty::TyBool => Repr::Bool, - ty::TyInt(_) => Repr::Int, + + ty::TyInt(IntTy::Is) => unimplemented!(), + ty::TyInt(IntTy::I8) => Repr::Int(IntRepr::I8), + ty::TyInt(IntTy::I16) => Repr::Int(IntRepr::I16), + ty::TyInt(IntTy::I32) => Repr::Int(IntRepr::I32), + ty::TyInt(IntTy::I64) => Repr::Int(IntRepr::I64), + ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), ty::TyEnum(adt_def, ref subst) => { diff --git a/src/memory.rs b/src/memory.rs index 352f19dcdf022..9b4ed9b584a03 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,6 +1,5 @@ use byteorder::{self, ByteOrder}; use std::collections::HashMap; -use std::mem; use std::ptr; use interpreter::{EvalError, EvalResult}; @@ -26,6 +25,9 @@ pub struct Pointer { pub offset: usize, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum IntRepr { I8, I16, I32, I64 } + #[derive(Clone, Debug, PartialEq, Eq)] pub struct FieldRepr { pub offset: usize, @@ -35,7 +37,7 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { Bool, - Int, + Int(IntRepr), /// The representation for product types including tuples, structs, and the contents of enum /// variants. @@ -118,11 +120,11 @@ impl Memory { } pub fn read_int(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, Repr::Int.size()).map(byteorder::NativeEndian::read_i64) + self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_i64) } pub fn write_int(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, Repr::Int.size())); + let bytes = try!(self.get_bytes_mut(ptr, 8)); byteorder::NativeEndian::write_i64(bytes, n); Ok(()) } @@ -164,7 +166,10 @@ impl Repr { pub fn size(&self) -> usize { match *self { Repr::Bool => 1, - Repr::Int => mem::size_of::(), + Repr::Int(IntRepr::I8) => 1, + Repr::Int(IntRepr::I16) => 2, + Repr::Int(IntRepr::I32) => 4, + Repr::Int(IntRepr::I64) => 8, Repr::Product { size, .. } => size, Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, } diff --git a/test/ints.rs b/test/ints.rs index 80f842d3768dc..1885ba48340dd 100644 --- a/test/ints.rs +++ b/test/ints.rs @@ -30,7 +30,7 @@ fn arith() -> i64 { #[miri_run] fn match_int() -> i64 { - let n = 2; + let n = 2i64; match n { 0 => 0, 1 => 10, From c1edb9f978261aa0c313f3b9131bdd0d09acbd6b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 01:14:20 -0600 Subject: [PATCH 0079/1096] More work for multiple int sizes. --- src/interpreter.rs | 124 ++++++++++++++++++++++++--------------------- src/memory.rs | 63 ++++++++++++++++++++--- 2 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c33c7874fbfdc..887a69f258796 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -36,16 +36,6 @@ impl fmt::Display for EvalError { } } -// #[derive(Clone, Debug, PartialEq)] -// enum Value { -// Uninit, -// Bool(bool), -// Int(i64), // FIXME(tsion): Should be bit-width aware. -// Pointer(Pointer), -// Adt { variant: usize, data_ptr: Pointer }, -// Func(def_id::DefId), -// } - /// A stack frame. struct Frame<'a, 'tcx: 'a> { /// The MIR for the fucntion called on this frame. @@ -87,6 +77,15 @@ struct Interpreter<'a, 'tcx: 'a> { stack: Vec>, } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PrimVal { + Bool(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), +} + impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { @@ -109,7 +108,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { let repr = self.ty_to_repr(arg_decl.ty); let dest = self.memory.allocate(repr.size()); - let src = try!(self.operand_to_ptr(arg_operand)); + let (src, _) = try!(self.eval_operand(arg_operand)); try!(self.memory.copy(src, dest, repr.size())); locals.push(dest); } @@ -161,22 +160,22 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Goto { target } => current_block = target, If { ref cond, targets: (then_target, else_target) } => { - let cond_ptr = try!(self.operand_to_ptr(cond)); + let (cond_ptr, _) = try!(self.eval_operand(cond)); let cond_val = try!(self.memory.read_bool(cond_ptr)); current_block = if cond_val { then_target } else { else_target }; } SwitchInt { ref discr, ref values, ref targets, .. } => { // FIXME(tsion): Handle non-integer switch types. - let discr_ptr = try!(self.lvalue_to_ptr(discr)); - let discr_val = try!(self.memory.read_int(discr_ptr)); + let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); + let discr_val = try!(self.memory.read_i64(discr_ptr)); // Branch to the `otherwise` case by default, if no match is found. current_block = targets[targets.len() - 1]; for (index, val_const) in values.iter().enumerate() { let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_int(ptr)); + let val = try!(self.memory.read_i64(ptr)); if discr_val == val { current_block = targets[index]; break; @@ -241,32 +240,35 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { right_operand: &mir::Operand<'tcx>, dest: Pointer) -> EvalResult<()> { // FIXME(tsion): Check for non-integer binary operations. - let left = try!(self.operand_to_ptr(left_operand)); - let right = try!(self.operand_to_ptr(right_operand)); - let l = try!(self.memory.read_int(left)); - let r = try!(self.memory.read_int(right)); + let (left, left_repr) = try!(self.eval_operand(left_operand)); + let (right, right_repr) = try!(self.eval_operand(right_operand)); + + let left_val = try!(self.memory.read_primval(left, &left_repr)); + let right_val = try!(self.memory.read_primval(right, &right_repr)); use rustc::mir::repr::BinOp::*; - let n = match bin_op { - Add => l + r, - Sub => l - r, - Mul => l * r, - Div => l / r, - Rem => l % r, - BitXor => l ^ r, - BitAnd => l & r, - BitOr => l | r, - Shl => l << r, - Shr => l >> r, - _ => unimplemented!(), - // Eq => Value::Bool(l == r), - // Lt => Value::Bool(l < r), - // Le => Value::Bool(l <= r), - // Ne => Value::Bool(l != r), - // Ge => Value::Bool(l >= r), - // Gt => Value::Bool(l > r), + use self::PrimVal::*; + let result_val = match (bin_op, left_val, right_val) { + (Add, I64(l), I64(r)) => I64(l + r), + (Sub, I64(l), I64(r)) => I64(l - r), + (Mul, I64(l), I64(r)) => I64(l * r), + (Div, I64(l), I64(r)) => I64(l / r), + (Rem, I64(l), I64(r)) => I64(l % r), + (BitXor, I64(l), I64(r)) => I64(l ^ r), + (BitAnd, I64(l), I64(r)) => I64(l & r), + (BitOr, I64(l), I64(r)) => I64(l | r), + (Shl, I64(l), I64(r)) => I64(l << r), + (Shr, I64(l), I64(r)) => I64(l >> r), + (Eq, I64(l), I64(r)) => Bool(l == r), + (Lt, I64(l), I64(r)) => Bool(l < r), + (Le, I64(l), I64(r)) => Bool(l <= r), + (Ne, I64(l), I64(r)) => Bool(l != r), + (Ge, I64(l), I64(r)) => Bool(l >= r), + (Gt, I64(l), I64(r)) => Bool(l > r), + _ => unimplemented!(), }; - self.memory.write_int(dest, n) + + self.memory.write_primval(dest, result_val) } fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr, @@ -274,7 +276,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *dest_repr { Repr::Product { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { - let src = try!(self.operand_to_ptr(operand)); + let (src, _) = try!(self.eval_operand(operand)); try!(self.memory.copy(src, dest.offset(field.offset), field.repr.size())); } } @@ -286,14 +288,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { - let dest = try!(self.lvalue_to_ptr(lvalue)); - let dest_ty = self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); - let dest_repr = self.ty_to_repr(dest_ty); + let (dest, dest_repr) = try!(self.eval_lvalue(lvalue)); use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { - let src = try!(self.operand_to_ptr(operand)); + let (src, _) = try!(self.eval_operand(operand)); self.memory.copy(src, dest, dest_repr.size()) } @@ -301,15 +301,19 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.eval_binary_op(bin_op, left, right, dest), UnaryOp(un_op, ref operand) => { - // FIXME(tsion): Check for non-integer operations. - let ptr = try!(self.operand_to_ptr(operand)); - let m = try!(self.memory.read_int(ptr)); + let (src, src_repr) = try!(self.eval_operand(operand)); + let src_val = try!(self.memory.read_primval(src, &src_repr)); + use rustc::mir::repr::UnOp::*; - let n = match un_op { - Not => !m, - Neg => -m, + use self::PrimVal::*; + let result_val = match (un_op, src_val) { + (Not, Bool(b)) => Bool(!b), + (Not, I64(n)) => I64(!n), + (Neg, I64(n)) => I64(-n), + _ => unimplemented!(), }; - self.memory.write_int(dest, n) + + self.memory.write_primval(dest, result_val) } Aggregate(ref kind, ref operands) => { @@ -346,22 +350,25 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<(Pointer, Repr)> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.lvalue_to_ptr(lvalue), + Consume(ref lvalue) => self.eval_lvalue(lvalue), - Constant(ref constant) => { + Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; - match constant.literal { - Value { ref value } => self.const_to_ptr(value), + match *literal { + Value { ref value } => Ok(( + try!(self.const_to_ptr(value)), + self.ty_to_repr(ty), + )), ref l => panic!("can't handle item literal: {:?}", l), } } } } - fn lvalue_to_ptr(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { + fn eval_lvalue(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<(Pointer, Repr)> { let frame = self.current_frame(); use rustc::mir::repr::Lvalue::*; @@ -374,7 +381,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ref l => panic!("can't handle lvalue: {:?}", l), }; - Ok(ptr) + let ty = self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); + Ok((ptr, self.ty_to_repr(ty))) // mir::Lvalue::Projection(ref proj) => { // let base_ptr = self.lvalue_to_ptr(&proj.base); @@ -419,7 +427,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Int(n) => { // TODO(tsion): Check int constant type. let ptr = self.memory.allocate(8); - try!(self.memory.write_int(ptr, n)); + try!(self.memory.write_i64(ptr, n)); Ok(ptr) } Uint(_u) => unimplemented!(), diff --git a/src/memory.rs b/src/memory.rs index 9b4ed9b584a03..af3911083bf65 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,7 +2,7 @@ use byteorder::{self, ByteOrder}; use std::collections::HashMap; use std::ptr; -use interpreter::{EvalError, EvalResult}; +use interpreter::{EvalError, EvalResult, PrimVal}; pub struct Memory { next_id: u64, @@ -119,14 +119,25 @@ impl Memory { Ok(()) } - pub fn read_int(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_i64) + pub fn read_primval(&self, ptr: Pointer, repr: &Repr) -> EvalResult { + match *repr { + Repr::Bool => self.read_bool(ptr).map(PrimVal::Bool), + Repr::Int(IntRepr::I8) => self.read_i8(ptr).map(PrimVal::I8), + Repr::Int(IntRepr::I16) => self.read_i16(ptr).map(PrimVal::I16), + Repr::Int(IntRepr::I32) => self.read_i32(ptr).map(PrimVal::I32), + Repr::Int(IntRepr::I64) => self.read_i64(ptr).map(PrimVal::I64), + _ => panic!("primitive read of non-primitive: {:?}", repr), + } } - pub fn write_int(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 8)); - byteorder::NativeEndian::write_i64(bytes, n); - Ok(()) + pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { + match val { + PrimVal::Bool(b) => self.write_bool(ptr, b), + PrimVal::I8(n) => self.write_i8(ptr, n), + PrimVal::I16(n) => self.write_i16(ptr, n), + PrimVal::I32(n) => self.write_i32(ptr, n), + PrimVal::I64(n) => self.write_i64(ptr, n), + } } pub fn read_bool(&self, ptr: Pointer) -> EvalResult { @@ -143,6 +154,44 @@ impl Memory { bytes[0] = b as u8; Ok(()) } + + pub fn read_i8(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 1).map(|b| b[0] as i8) + } + + pub fn write_i8(&mut self, ptr: Pointer, n: i8) -> EvalResult<()> { + self.get_bytes_mut(ptr, 1).map(|b| b[0] = n as u8) + } + + pub fn read_i16(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 2).map(byteorder::NativeEndian::read_i16) + } + + pub fn write_i16(&mut self, ptr: Pointer, n: i16) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 2)); + byteorder::NativeEndian::write_i16(bytes, n); + Ok(()) + } + + pub fn read_i32(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 4).map(byteorder::NativeEndian::read_i32) + } + + pub fn write_i32(&mut self, ptr: Pointer, n: i32) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 4)); + byteorder::NativeEndian::write_i32(bytes, n); + Ok(()) + } + + pub fn read_i64(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_i64) + } + + pub fn write_i64(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 8)); + byteorder::NativeEndian::write_i64(bytes, n); + Ok(()) + } } impl Allocation { From 6b4d2b11a625e04245b9ff9a2ff4642863a94c55 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 01:43:28 -0600 Subject: [PATCH 0080/1096] Add support for smaller signed integers. --- src/interpreter.rs | 70 ++++++++-------------------------------------- src/lib.rs | 1 + src/memory.rs | 3 +- src/primval.rs | 66 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 60 deletions(-) create mode 100644 src/primval.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 887a69f258796..a3ed7c59f12cd 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,6 +6,7 @@ use std::error::Error; use std::fmt; use memory::{FieldRepr, IntRepr, Memory, Pointer, Repr}; +use primval; const TRACE_EXECUTION: bool = true; @@ -77,15 +78,6 @@ struct Interpreter<'a, 'tcx: 'a> { stack: Vec>, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PrimVal { - Bool(bool), - I8(i8), - I16(i16), - I32(i32), - I64(i64), -} - impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { @@ -236,41 +228,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(()) } - fn eval_binary_op(&mut self, bin_op: mir::BinOp, left_operand: &mir::Operand<'tcx>, - right_operand: &mir::Operand<'tcx>, dest: Pointer) -> EvalResult<()> - { - // FIXME(tsion): Check for non-integer binary operations. - let (left, left_repr) = try!(self.eval_operand(left_operand)); - let (right, right_repr) = try!(self.eval_operand(right_operand)); - - let left_val = try!(self.memory.read_primval(left, &left_repr)); - let right_val = try!(self.memory.read_primval(right, &right_repr)); - - use rustc::mir::repr::BinOp::*; - use self::PrimVal::*; - let result_val = match (bin_op, left_val, right_val) { - (Add, I64(l), I64(r)) => I64(l + r), - (Sub, I64(l), I64(r)) => I64(l - r), - (Mul, I64(l), I64(r)) => I64(l * r), - (Div, I64(l), I64(r)) => I64(l / r), - (Rem, I64(l), I64(r)) => I64(l % r), - (BitXor, I64(l), I64(r)) => I64(l ^ r), - (BitAnd, I64(l), I64(r)) => I64(l & r), - (BitOr, I64(l), I64(r)) => I64(l | r), - (Shl, I64(l), I64(r)) => I64(l << r), - (Shr, I64(l), I64(r)) => I64(l >> r), - (Eq, I64(l), I64(r)) => Bool(l == r), - (Lt, I64(l), I64(r)) => Bool(l < r), - (Le, I64(l), I64(r)) => Bool(l <= r), - (Ne, I64(l), I64(r)) => Bool(l != r), - (Ge, I64(l), I64(r)) => Bool(l >= r), - (Gt, I64(l), I64(r)) => Bool(l > r), - _ => unimplemented!(), - }; - - self.memory.write_primval(dest, result_val) - } - fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr, operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { match *dest_repr { @@ -297,23 +254,18 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.copy(src, dest, dest_repr.size()) } - BinaryOp(bin_op, ref left, ref right) => - self.eval_binary_op(bin_op, left, right, dest), + BinaryOp(bin_op, ref left, ref right) => { + let (left_ptr, left_repr) = try!(self.eval_operand(left)); + let (right_ptr, right_repr) = try!(self.eval_operand(right)); + let left_val = try!(self.memory.read_primval(left_ptr, &left_repr)); + let right_val = try!(self.memory.read_primval(right_ptr, &right_repr)); + self.memory.write_primval(dest, primval::binary_op(bin_op, left_val, right_val)) + } UnaryOp(un_op, ref operand) => { - let (src, src_repr) = try!(self.eval_operand(operand)); - let src_val = try!(self.memory.read_primval(src, &src_repr)); - - use rustc::mir::repr::UnOp::*; - use self::PrimVal::*; - let result_val = match (un_op, src_val) { - (Not, Bool(b)) => Bool(!b), - (Not, I64(n)) => I64(!n), - (Neg, I64(n)) => I64(-n), - _ => unimplemented!(), - }; - - self.memory.write_primval(dest, result_val) + let (ptr, repr) = try!(self.eval_operand(operand)); + let val = try!(self.memory.read_primval(ptr, &repr)); + self.memory.write_primval(dest, primval::unary_op(un_op, val)) } Aggregate(ref kind, ref operands) => { diff --git a/src/lib.rs b/src/lib.rs index 036b87c2c7f69..c3b1c5a402523 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ extern crate syntax; pub mod interpreter; mod memory; +mod primval; diff --git a/src/memory.rs b/src/memory.rs index af3911083bf65..fb17b4dabed4a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,7 +2,8 @@ use byteorder::{self, ByteOrder}; use std::collections::HashMap; use std::ptr; -use interpreter::{EvalError, EvalResult, PrimVal}; +use interpreter::{EvalError, EvalResult}; +use primval::PrimVal; pub struct Memory { next_id: u64, diff --git a/src/primval.rs b/src/primval.rs new file mode 100644 index 0000000000000..9442adbaf7e7b --- /dev/null +++ b/src/primval.rs @@ -0,0 +1,66 @@ +use rustc::mir::repr as mir; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PrimVal { + Bool(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), +} + +pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { + macro_rules! int_binops { + ($v:ident, $l:ident, $r:ident) => ({ + use rustc::mir::repr::BinOp::*; + use self::PrimVal::*; + match bin_op { + Add => $v($l + $r), + Sub => $v($l - $r), + Mul => $v($l * $r), + Div => $v($l / $r), + Rem => $v($l % $r), + BitXor => $v($l ^ $r), + BitAnd => $v($l & $r), + BitOr => $v($l | $r), + + // TODO(tsion): Can have differently-typed RHS. + Shl => $v($l << $r), + Shr => $v($l >> $r), + + Eq => Bool($l == $r), + Ne => Bool($l != $r), + Lt => Bool($l < $r), + Le => Bool($l <= $r), + Gt => Bool($l > $r), + Ge => Bool($l >= $r), + } + }) + } + + use self::PrimVal::*; + match (left, right) { + (I8(l), I8(r)) => int_binops!(I8, l, r), + (I16(l), I16(r)) => int_binops!(I16, l, r), + (I32(l), I32(r)) => int_binops!(I32, l, r), + (I64(l), I64(r)) => int_binops!(I64, l, r), + _ => unimplemented!(), + } +} + +pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal { + use rustc::mir::repr::UnOp::*; + use self::PrimVal::*; + match (un_op, val) { + (Not, Bool(b)) => Bool(!b), + (Not, I8(n)) => I8(!n), + (Neg, I8(n)) => I8(-n), + (Not, I16(n)) => I16(!n), + (Neg, I16(n)) => I16(-n), + (Not, I32(n)) => I32(!n), + (Neg, I32(n)) => I32(-n), + (Not, I64(n)) => I64(!n), + (Neg, I64(n)) => I64(-n), + _ => unimplemented!(), + } +} From dad5edd4f351621fd7b609d70b611858d8d13cd1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 01:48:07 -0600 Subject: [PATCH 0081/1096] Unnest integer reprs. --- src/interpreter.rs | 10 +++++----- src/memory.rs | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a3ed7c59f12cd..bf87283dee437 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -5,7 +5,7 @@ use rustc::mir::repr as mir; use std::error::Error; use std::fmt; -use memory::{FieldRepr, IntRepr, Memory, Pointer, Repr}; +use memory::{FieldRepr, Memory, Pointer, Repr}; use primval; const TRACE_EXECUTION: bool = true; @@ -416,10 +416,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::TyBool => Repr::Bool, ty::TyInt(IntTy::Is) => unimplemented!(), - ty::TyInt(IntTy::I8) => Repr::Int(IntRepr::I8), - ty::TyInt(IntTy::I16) => Repr::Int(IntRepr::I16), - ty::TyInt(IntTy::I32) => Repr::Int(IntRepr::I32), - ty::TyInt(IntTy::I64) => Repr::Int(IntRepr::I64), + ty::TyInt(IntTy::I8) => Repr::I8, + ty::TyInt(IntTy::I16) => Repr::I16, + ty::TyInt(IntTy::I32) => Repr::I32, + ty::TyInt(IntTy::I64) => Repr::I64, ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), diff --git a/src/memory.rs b/src/memory.rs index fb17b4dabed4a..61d00d37ffc36 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -26,9 +26,6 @@ pub struct Pointer { pub offset: usize, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum IntRepr { I8, I16, I32, I64 } - #[derive(Clone, Debug, PartialEq, Eq)] pub struct FieldRepr { pub offset: usize, @@ -38,7 +35,10 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { Bool, - Int(IntRepr), + I8, + I16, + I32, + I64, /// The representation for product types including tuples, structs, and the contents of enum /// variants. @@ -123,10 +123,10 @@ impl Memory { pub fn read_primval(&self, ptr: Pointer, repr: &Repr) -> EvalResult { match *repr { Repr::Bool => self.read_bool(ptr).map(PrimVal::Bool), - Repr::Int(IntRepr::I8) => self.read_i8(ptr).map(PrimVal::I8), - Repr::Int(IntRepr::I16) => self.read_i16(ptr).map(PrimVal::I16), - Repr::Int(IntRepr::I32) => self.read_i32(ptr).map(PrimVal::I32), - Repr::Int(IntRepr::I64) => self.read_i64(ptr).map(PrimVal::I64), + Repr::I8 => self.read_i8(ptr).map(PrimVal::I8), + Repr::I16 => self.read_i16(ptr).map(PrimVal::I16), + Repr::I32 => self.read_i32(ptr).map(PrimVal::I32), + Repr::I64 => self.read_i64(ptr).map(PrimVal::I64), _ => panic!("primitive read of non-primitive: {:?}", repr), } } @@ -216,10 +216,10 @@ impl Repr { pub fn size(&self) -> usize { match *self { Repr::Bool => 1, - Repr::Int(IntRepr::I8) => 1, - Repr::Int(IntRepr::I16) => 2, - Repr::Int(IntRepr::I32) => 4, - Repr::Int(IntRepr::I64) => 8, + Repr::I8 => 1, + Repr::I16 => 2, + Repr::I32 => 4, + Repr::I64 => 8, Repr::Product { size, .. } => size, Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, } From 96c51dc8edf72b430f647afb1b67c3c77eedc5ad Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 01:55:48 -0600 Subject: [PATCH 0082/1096] Make sum repr discriminant an arbitrary repr. --- src/interpreter.rs | 18 +++++++++--------- src/memory.rs | 7 ++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bf87283dee437..e71bd8d061886 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -277,10 +277,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::AdtKind::Struct => self.assign_to_product(dest, &dest_repr, operands), ty::AdtKind::Enum => match dest_repr { - Repr::Sum { discr_size, ref variants, .. } => { + Repr::Sum { ref discr, ref variants, .. } => { // TODO(tsion): Write the discriminant value. self.assign_to_product( - dest.offset(discr_size), + dest.offset(discr.size()), &variants[variant_idx], operands ) @@ -426,16 +426,16 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::TyEnum(adt_def, ref subst) => { let num_variants = adt_def.variants.len(); - let discr_size = if num_variants <= 1 { - 0 + let discr = if num_variants <= 1 { + Repr::Product { size: 0, fields: vec![] } } else if num_variants <= 1 << 8 { - 1 + Repr::I8 } else if num_variants <= 1 << 16 { - 2 + Repr::I16 } else if num_variants <= 1 << 32 { - 4 + Repr::I32 } else { - 8 + Repr::I64 }; let variants: Vec = adt_def.variants.iter().map(|v| { @@ -444,7 +444,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }).collect(); Repr::Sum { - discr_size: discr_size, + discr: Box::new(discr), max_variant_size: variants.iter().map(Repr::size).max().unwrap_or(0), variants: variants, } diff --git a/src/memory.rs b/src/memory.rs index 61d00d37ffc36..a1681d28b81d9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -50,13 +50,10 @@ pub enum Repr { /// The representation for a sum type, i.e. a Rust enum. Sum { - /// The size of the discriminant in bytes. - discr_size: usize, - /// The size of the largest variant in bytes. max_variant_size: usize, - variants: Vec, + discr: Box, }, // Array { @@ -221,7 +218,7 @@ impl Repr { Repr::I32 => 4, Repr::I64 => 8, Repr::Product { size, .. } => size, - Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, + Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, } } } From cc8b8efd33731cf6412ad33ffa4a136f3ba400a6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 04:50:16 -0600 Subject: [PATCH 0083/1096] Allow switching on non-integer types. --- src/interpreter.rs | 5 ++--- test/bools.rs | 16 ++++++++-------- test/ints.rs | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e71bd8d061886..8db2f18f46fa8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -158,16 +158,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - // FIXME(tsion): Handle non-integer switch types. let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); - let discr_val = try!(self.memory.read_i64(discr_ptr)); + let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); // Branch to the `otherwise` case by default, if no match is found. current_block = targets[targets.len() - 1]; for (index, val_const) in values.iter().enumerate() { let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_i64(ptr)); + let val = try!(self.memory.read_primval(ptr, &discr_repr)); if discr_val == val { current_block = targets[index]; break; diff --git a/test/bools.rs b/test/bools.rs index 416d4e5f5fee1..e835d3e87e4b0 100755 --- a/test/bools.rs +++ b/test/bools.rs @@ -16,11 +16,11 @@ fn if_true() -> i64 { if true { 1 } else { 0 } } -// #[miri_run] -// fn match_bool() -> i64 { -// let b = true; -// match b { -// true => 1, -// false => 0, -// } -// } +#[miri_run] +fn match_bool() -> i16 { + let b = true; + match b { + true => 1, + _ => 0, + } +} diff --git a/test/ints.rs b/test/ints.rs index 1885ba48340dd..7270a79c32b0c 100644 --- a/test/ints.rs +++ b/test/ints.rs @@ -24,13 +24,13 @@ fn indirect_add() -> i64 { } #[miri_run] -fn arith() -> i64 { +fn arith() -> i32 { 3*3 + 4*4 } #[miri_run] -fn match_int() -> i64 { - let n = 2i64; +fn match_int() -> i16 { + let n = 2; match n { 0 => 0, 1 => 10, From 80d12601ff20454cd39de2c0d49558b46f1077a2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 06:05:48 -0600 Subject: [PATCH 0084/1096] Write enum discriminants. --- src/interpreter.rs | 7 +++++-- src/primval.rs | 15 +++++++++++++++ test/sums.rs | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8db2f18f46fa8..f1ec0c65e8a28 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,7 +6,7 @@ use std::error::Error; use std::fmt; use memory::{FieldRepr, Memory, Pointer, Repr}; -use primval; +use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = true; @@ -277,7 +277,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::AdtKind::Enum => match dest_repr { Repr::Sum { ref discr, ref variants, .. } => { - // TODO(tsion): Write the discriminant value. + if discr.size() > 0 { + let discr_val = PrimVal::from_int(variant_idx as i64, discr); + try!(self.memory.write_primval(dest, discr_val)); + } self.assign_to_product( dest.offset(discr.size()), &variants[variant_idx], diff --git a/src/primval.rs b/src/primval.rs index 9442adbaf7e7b..3fbcd6636404c 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,5 +1,7 @@ use rustc::mir::repr as mir; +use memory::Repr; + #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimVal { Bool(bool), @@ -9,6 +11,19 @@ pub enum PrimVal { I64(i64), } +impl PrimVal { + pub fn from_int(n: i64, repr: &Repr) -> Self { + // TODO(tsion): Use checked casts. + match *repr { + Repr::I8 => PrimVal::I8(n as i8), + Repr::I16 => PrimVal::I16(n as i16), + Repr::I32 => PrimVal::I32(n as i32), + Repr::I64 => PrimVal::I64(n), + _ => panic!("attempted to make integer primval from non-integer repr"), + } + } +} + pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ diff --git a/test/sums.rs b/test/sums.rs index e6545e81c00b8..d04f0b2e7cf24 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -1,6 +1,25 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] +enum Unit { Unit } + +#[miri_run] +fn return_unit() -> Unit { + Unit::Unit +} + +enum MyBool { False, True } + +#[miri_run] +fn return_true() -> MyBool { + MyBool::True +} + +#[miri_run] +fn return_false() -> MyBool { + MyBool::False +} + #[miri_run] fn return_none() -> Option { None From 6d37e7fc29e491732c729f55f1b1c31528557f37 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 06:30:28 -0600 Subject: [PATCH 0085/1096] Reimplement sum type switching. --- src/interpreter.rs | 20 ++++++++++---------- src/primval.rs | 10 ++++++++++ test/sums.rs | 34 +++++++++++++++++----------------- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f1ec0c65e8a28..339c4b3167566 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -174,6 +174,16 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + Switch { ref discr, ref targets, .. } => { + let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); + let discr_repr = match adt_repr { + Repr::Sum { ref discr, .. } => discr, + _ => panic!("attmpted to switch on non-sum type"), + }; + let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); + current_block = targets[discr_val.to_int() as usize]; + } + // Call { ref func, ref args, ref destination, .. } => { // use rustc::middle::cstore::CrateStore; // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); @@ -203,16 +213,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // } // } - // Switch { ref discr, ref targets, .. } => { - // let discr_val = self.read_lvalue(discr); - - // if let Value::Adt { variant, .. } = discr_val { - // current_block = targets[variant]; - // } else { - // panic!("Switch on non-Adt value: {:?}", discr_val); - // } - // } - Drop { target, .. } => { // TODO: Handle destructors and dynamic drop. current_block = target; diff --git a/src/primval.rs b/src/primval.rs index 3fbcd6636404c..63d5598ece07d 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -22,6 +22,16 @@ impl PrimVal { _ => panic!("attempted to make integer primval from non-integer repr"), } } + + pub fn to_int(self) -> i64 { + match self { + PrimVal::I8(n) => n as i64, + PrimVal::I16(n) => n as i64, + PrimVal::I32(n) => n as i64, + PrimVal::I64(n) => n, + _ => panic!("attempted to make integer from non-integer primval"), + } + } } pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { diff --git a/test/sums.rs b/test/sums.rs index d04f0b2e7cf24..328539acca990 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -30,20 +30,20 @@ fn return_some() -> Option { Some(42) } -// #[miri_run] -// fn match_opt_none() -> i64 { -// let x = None, -// match x { -// Some(data) => data, -// None => 42, -// } -// } - -// #[miri_run] -// fn match_opt_some() -> i64 { -// let x = Some(13); -// match x { -// Some(data) => data, -// None => 42, -// } -// } +#[miri_run] +fn match_opt_none() -> i8 { + let x = None::; + match x { + Some(_) => 10, + None => 20, + } +} + +#[miri_run] +fn match_opt_some() -> i8 { + let x = Some(13); + match x { + Some(_) => 10, + None => 20, + } +} From dd3d58f2498961cb66baa5bbaec43e8235c33235 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 06:48:04 -0600 Subject: [PATCH 0086/1096] Reimplement field lvalue projection. --- src/interpreter.rs | 15 +++++++++++++++ test/products.rs | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 339c4b3167566..5f8d46dc95259 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -332,6 +332,21 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Arg(i) => frame.arg_ptr(i), Var(i) => frame.var_ptr(i), Temp(i) => frame.temp_ptr(i), + + Projection(ref proj) => { + let (base_ptr, base_repr) = try!(self.eval_lvalue(&proj.base)); + use rustc::mir::repr::ProjectionElem::*; + match proj.elem { + Field(field, _) => match base_repr { + Repr::Product { ref fields, .. } => + base_ptr.offset(fields[field.index()].offset), + _ => panic!("field access on non-product type"), + }, + + _ => unimplemented!(), + } + } + ref l => panic!("can't handle lvalue: {:?}", l), }; diff --git a/test/products.rs b/test/products.rs index afe161830929e..4028670e6f2be 100644 --- a/test/products.rs +++ b/test/products.rs @@ -16,9 +16,16 @@ fn tuple_5() -> (i64, i64, i64, i64, i64) { (1, 2, 3, 4, 5) } -struct Pair { x: i64, y: i64 } +struct Pair { x: i8, y: i8 } #[miri_run] fn pair() -> Pair { Pair { x: 10, y: 20 } } + +#[miri_run] +fn field_access() -> (i8, i8) { + let mut p = Pair { x: 10, y: 20 }; + p.x += 5; + (p.x, p.y) +} From 9aa3a8675f115216f4e5d182128450e29a9600ce Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 07:23:48 -0600 Subject: [PATCH 0087/1096] Reimplement variant downcast projection. --- src/interpreter.rs | 33 +++++++++++++++++++++++---------- test/sums.rs | 8 ++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5f8d46dc95259..bd7a497bd7f6f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,5 +1,6 @@ use rustc::middle::const_eval; use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::subst::Substs; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use std::error::Error; @@ -340,7 +341,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Field(field, _) => match base_repr { Repr::Product { ref fields, .. } => base_ptr.offset(fields[field.index()].offset), - _ => panic!("field access on non-product type"), + _ => panic!("field access on non-product type: {:?}", base_repr), + }, + + Downcast(_, variant) => match base_repr { + Repr::Sum { ref discr, .. } => base_ptr.offset(discr.size()), + _ => panic!("variant downcast on non-sum type"), }, _ => unimplemented!(), @@ -350,8 +356,13 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ref l => panic!("can't handle lvalue: {:?}", l), }; - let ty = self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); - Ok((ptr, self.ty_to_repr(ty))) + use rustc::mir::tcx::LvalueTy; + let repr = match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { + LvalueTy::Ty { ty } => self.ty_to_repr(ty), + LvalueTy::Downcast { ref adt_def, substs, variant_index } => + self.make_variant_repr(&adt_def.variants[variant_index], substs), + }; + Ok((ptr, repr)) // mir::Lvalue::Projection(ref proj) => { // let base_ptr = self.lvalue_to_ptr(&proj.base); @@ -426,6 +437,11 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Repr::Product { size: size, fields: fields } } + fn make_variant_repr(&self, v: ty::VariantDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Repr { + let field_tys = v.fields.iter().map(|f| f.ty(self.tcx, substs)); + self.make_product_repr(field_tys) + } + // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { use syntax::ast::IntTy; @@ -440,7 +456,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), - ty::TyEnum(adt_def, ref subst) => { + ty::TyEnum(adt_def, substs) => { let num_variants = adt_def.variants.len(); let discr = if num_variants <= 1 { @@ -456,8 +472,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }; let variants: Vec = adt_def.variants.iter().map(|v| { - let field_tys = v.fields.iter().map(|f| f.ty(self.tcx, subst)); - self.make_product_repr(field_tys) + self.make_variant_repr(v, substs) }).collect(); Repr::Sum { @@ -467,17 +482,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - ty::TyStruct(adt_def, ref subst) => { + ty::TyStruct(adt_def, substs) => { assert_eq!(adt_def.variants.len(), 1); - let field_tys = adt_def.variants[0].fields.iter().map(|f| f.ty(self.tcx, subst)); - self.make_product_repr(field_tys) + self.make_variant_repr(&adt_def.variants[0], substs) } ref t => panic!("can't convert type to repr: {:?}", t), } } - fn current_frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } diff --git a/test/sums.rs b/test/sums.rs index 328539acca990..c63d3e6804501 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -32,10 +32,10 @@ fn return_some() -> Option { #[miri_run] fn match_opt_none() -> i8 { - let x = None::; + let x = None; match x { - Some(_) => 10, - None => 20, + Some(data) => data, + None => 42, } } @@ -43,7 +43,7 @@ fn match_opt_none() -> i8 { fn match_opt_some() -> i8 { let x = Some(13); match x { - Some(_) => 10, + Some(data) => data, None => 20, } } From 21059148860fbd25d9f363fff92a6f843986a336 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 07:40:52 -0600 Subject: [PATCH 0088/1096] Remove old commented-out code. --- src/interpreter.rs | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bd7a497bd7f6f..0452485d03bd6 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -362,42 +362,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { LvalueTy::Downcast { ref adt_def, substs, variant_index } => self.make_variant_repr(&adt_def.variants[variant_index], substs), }; - Ok((ptr, repr)) - // mir::Lvalue::Projection(ref proj) => { - // let base_ptr = self.lvalue_to_ptr(&proj.base); - - // match proj.elem { - // mir::ProjectionElem::Field(field, _) => { - // base_ptr.offset(field.index()) - // } - - // mir::ProjectionElem::Downcast(_, variant) => { - // let adt_val = self.read_pointer(base_ptr); - // if let Value::Adt { variant: actual_variant, data_ptr } = adt_val { - // debug_assert_eq!(variant, actual_variant); - // data_ptr - // } else { - // panic!("Downcast attempted on non-ADT: {:?}", adt_val) - // } - // } - - // mir::ProjectionElem::Deref => { - // let ptr_val = self.read_pointer(base_ptr); - // if let Value::Pointer(ptr) = ptr_val { - // ptr - // } else { - // panic!("Deref attempted on non-pointer: {:?}", ptr_val) - // } - // } - - // mir::ProjectionElem::Index(ref _operand) => unimplemented!(), - // mir::ProjectionElem::ConstantIndex { .. } => unimplemented!(), - // } - // } - - // _ => unimplemented!(), - // } + Ok((ptr, repr)) } fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { From 250e9615c5b34379de30001e6f628d2c036bc24e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 08:01:22 -0600 Subject: [PATCH 0089/1096] Remove unused variable. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 0452485d03bd6..2b26171bcb451 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -344,7 +344,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { _ => panic!("field access on non-product type: {:?}", base_repr), }, - Downcast(_, variant) => match base_repr { + Downcast(..) => match base_repr { Repr::Sum { ref discr, .. } => base_ptr.offset(discr.size()), _ => panic!("variant downcast on non-sum type"), }, From b756aecee7a7b1813fb5c12cf11d9f7d7007a546 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 08:31:29 -0600 Subject: [PATCH 0090/1096] Uncomment now-working test. --- test/loops.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/loops.rs b/test/loops.rs index c5baa414454ee..1cb8d464f79c1 100644 --- a/test/loops.rs +++ b/test/loops.rs @@ -1,15 +1,15 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_loop() -> i64 { -// let mut product = 1; -// let mut i = 1; +#[miri_run] +fn factorial_loop() -> i64 { + let mut product = 1; + let mut i = 1; -// while i <= 10 { -// product *= i; -// i += 1; -// } + while i <= 10 { + product *= i; + i += 1; + } -// product -// } + product +} From 039014fee262bd3e9587bce90a650d6b5990d349 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 08:32:30 -0600 Subject: [PATCH 0091/1096] Uncomment now-working test. --- test/ints.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/ints.rs b/test/ints.rs index 7270a79c32b0c..718fa17a74180 100644 --- a/test/ints.rs +++ b/test/ints.rs @@ -40,15 +40,15 @@ fn match_int() -> i16 { } } -// #[miri_run(expected = "Int(4)")] -// fn match_int_range() -> i64 { -// let n = 42; -// match n { -// 0...9 => 0, -// 10...19 => 1, -// 20...29 => 2, -// 30...39 => 3, -// 40...49 => 4, -// _ => 5, -// } -// } +#[miri_run] +fn match_int_range() -> i64 { + let n = 42; + match n { + 0...9 => 0, + 10...19 => 1, + 20...29 => 2, + 30...39 => 3, + 40...49 => 4, + _ => 5, + } +} From f145017319af0031c5248ee1c0cc435fba3a229b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 14:36:25 -0600 Subject: [PATCH 0092/1096] Add support for pointers. --- src/interpreter.rs | 16 +++++-- src/lib.rs | 2 +- src/memory.rs | 104 +++++++++++++++++++++++++++++++++++++++++---- test/pointers.rs | 71 ++++++++++++++++++++----------- 4 files changed, 157 insertions(+), 36 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2b26171bcb451..b2031162a79f5 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -16,6 +16,7 @@ pub enum EvalError { DanglingPointerDeref, InvalidBool, PointerOutOfBounds, + InvalidPointerAccess, } pub type EvalResult = Result; @@ -26,6 +27,8 @@ impl Error for EvalError { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidBool => "invalid boolean value read", EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", + EvalError::InvalidPointerAccess => + "a raw memory access tried to access part of a pointer value as bytes", } } @@ -297,9 +300,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - // Ref(_region, _kind, ref lvalue) => { - // Value::Pointer(self.lvalue_to_ptr(lvalue)) - // } + Ref(_, _, ref lvalue) => { + let (ptr, _) = try!(self.eval_lvalue(lvalue)); + self.memory.write_ptr(dest, ptr) + } ref r => panic!("can't handle rvalue: {:?}", r), } @@ -349,6 +353,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { _ => panic!("variant downcast on non-sum type"), }, + Deref => try!(self.memory.read_ptr(base_ptr)), + _ => unimplemented!(), } } @@ -453,6 +459,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.make_variant_repr(&adt_def.variants[0], substs) } + ty::TyRef(_, ty::TypeAndMut { ty, .. }) => { + Repr::Pointer { target: Box::new(self.ty_to_repr(ty)) } + } + ref t => panic!("can't convert type to repr: {:?}", t), } } diff --git a/src/lib.rs b/src/lib.rs index c3b1c5a402523..c7b5df9fa3d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private)] +#![feature(btree_range, collections_bound, rustc_private)] extern crate byteorder; extern crate rustc; diff --git a/src/memory.rs b/src/memory.rs index a1681d28b81d9..acc5597a4264c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,10 +1,13 @@ use byteorder::{self, ByteOrder}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; +use std::collections::Bound::{Included, Excluded}; use std::ptr; use interpreter::{EvalError, EvalResult}; use primval::PrimVal; +const POINTER_SIZE: usize = 8; + pub struct Memory { next_id: u64, alloc_map: HashMap, @@ -13,10 +16,20 @@ pub struct Memory { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); +/// A relocation represents a part of an allocation which points into another allocation. This is +/// used to represent pointers existing in the virtual memory. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Relocation { + /// The offset in the allocation where the relocation starts. + offset: usize, + /// The allocation this relocation points into. + target: AllocId, +} + #[derive(Debug)] pub struct Allocation { pub bytes: Vec, - // TODO(tsion): relocations + pub relocations: BTreeMap, // TODO(tsion): undef mask } @@ -61,6 +74,10 @@ pub enum Repr { // length: usize, // elem: Repr, // }, + + Pointer { + target: Box, + } } impl Memory { @@ -70,7 +87,7 @@ impl Memory { pub fn allocate(&mut self, size: usize) -> Pointer { let id = AllocId(self.next_id); - let alloc = Allocation { bytes: vec![0; size] }; + let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new() }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; Pointer { @@ -89,20 +106,40 @@ impl Memory { fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { let alloc = try!(self.get(ptr.alloc_id)); - try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size)); Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { let alloc = try!(self.get_mut(ptr.alloc_id)); - try!(alloc.check_bytes(ptr.offset, ptr.offset + size)); + try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size)); Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let src_bytes = try!(self.get_bytes_mut(src, size)).as_mut_ptr(); + let (src_bytes, relocations) = { + let alloc = try!(self.get_mut(src.alloc_id)); + try!(alloc.check_relocation_edges(src.offset, src.offset + size)); + let bytes = alloc.bytes[src.offset..src.offset + size].as_mut_ptr(); + + let mut relocations: Vec<(usize, AllocId)> = alloc.relocations + .range(Included(&src.offset), Excluded(&(src.offset + size))) + .map(|(&k, &v)| (k, v)) + .collect(); + + for &mut (ref mut offset, _) in &mut relocations { + alloc.relocations.remove(offset); + *offset += dest.offset - src.offset; + } + + (bytes, relocations) + }; + let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); + // TODO(tsion): Clear the destination range's existing relocations. + try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); + // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and // `dest` could possibly overlap. @@ -117,6 +154,29 @@ impl Memory { Ok(()) } + pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { + let alloc = try!(self.get(ptr.alloc_id)); + try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + POINTER_SIZE)); + let bytes = &alloc.bytes[ptr.offset..ptr.offset + POINTER_SIZE]; + let offset = byteorder::NativeEndian::read_u64(bytes) as usize; + + // TODO(tsion): Return an EvalError here instead of panicking. + let alloc_id = *alloc.relocations.get(&ptr.offset).unwrap(); + + Ok(Pointer { alloc_id: alloc_id, offset: offset }) + } + + // TODO(tsion): Detect invalid writes here and elsewhere. + pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> { + { + let bytes = try!(self.get_bytes_mut(dest, POINTER_SIZE)); + byteorder::NativeEndian::write_u64(bytes, ptr_val.offset as u64); + } + let alloc = try!(self.get_mut(dest.alloc_id)); + alloc.relocations.insert(dest.offset, ptr_val.alloc_id); + Ok(()) + } + pub fn read_primval(&self, ptr: Pointer, repr: &Repr) -> EvalResult { match *repr { Repr::Bool => self.read_bool(ptr).map(PrimVal::Bool), @@ -193,18 +253,45 @@ impl Memory { } impl Allocation { - fn check_bytes(&self, start: usize, end: usize) -> EvalResult<()> { + fn check_bounds(&self, start: usize, end: usize) -> EvalResult<()> { if start <= self.bytes.len() && end <= self.bytes.len() { Ok(()) } else { Err(EvalError::PointerOutOfBounds) } } + + fn count_overlapping_relocations(&self, start: usize, end: usize) -> usize { + self.relocations.range( + Included(&start.saturating_sub(POINTER_SIZE - 1)), + Excluded(&end) + ).count() + } + + fn check_relocation_edges(&self, start: usize, end: usize) -> EvalResult<()> { + try!(self.check_bounds(start, end)); + let n = + self.count_overlapping_relocations(start, start) + + self.count_overlapping_relocations(end, end); + if n == 0 { + Ok(()) + } else { + Err(EvalError::InvalidPointerAccess) + } + } + + fn check_no_relocations(&self, start: usize, end: usize) -> EvalResult<()> { + try!(self.check_bounds(start, end)); + if self.count_overlapping_relocations(start, end) == 0 { + Ok(()) + } else { + Err(EvalError::InvalidPointerAccess) + } + } } impl Pointer { pub fn offset(self, i: usize) -> Self { - // TODO(tsion): Check for offset out of bounds. Pointer { offset: self.offset + i, ..self } } } @@ -219,6 +306,7 @@ impl Repr { Repr::I64 => 8, Repr::Product { size, .. } => size, Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, + Repr::Pointer { .. } => POINTER_SIZE, } } } diff --git a/test/pointers.rs b/test/pointers.rs index 6dbd633d83f55..494adf243acb1 100644 --- a/test/pointers.rs +++ b/test/pointers.rs @@ -1,30 +1,53 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -// #[miri_run(expected = "Int(1)")] -// fn one_line_ref() -> i64 { -// *&1 -// } +#[miri_run] +fn one_line_ref() -> i16 { + *&1 +} -// #[miri_run(expected = "Int(1)")] -// fn basic_ref() -> i64 { -// let x = &1; -// *x -// } +#[miri_run] +fn basic_ref() -> i16 { + let x = &1; + *x +} -// #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut() -> i64 { -// let x = &mut 1; -// *x += 2; -// *x -// } +#[miri_run] +fn basic_ref_mut() -> i16 { + let x = &mut 1; + *x += 2; + *x +} -// #[miri_run(expected = "Int(3)")] -// fn basic_ref_mut_var() -> i64 { -// let mut a = 1; -// { -// let x = &mut a; -// *x += 2; -// } -// a -// } +#[miri_run] +fn basic_ref_mut_var() -> i16 { + let mut a = 1; + { + let x = &mut a; + *x += 2; + } + a +} + +#[miri_run] +fn tuple_ref_mut() -> (i8, i8) { + let mut t = (10, 20); + { + let x = &mut t.1; + *x += 2; + } + t +} + +#[miri_run] +fn match_ref_mut() -> i8 { + let mut t = (20, 22); + { + let mut opt = Some(&mut t); + match opt { + Some(&mut (ref mut x, ref mut y)) => *x += *y, + None => {}, + } + } + t.0 +} From 373f8a7a87c069f18b623cd5cb2eca8f83eb865a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 14:46:24 -0600 Subject: [PATCH 0093/1096] Fix typo. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b2031162a79f5..e5cebc0a72b74 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -43,7 +43,7 @@ impl fmt::Display for EvalError { /// A stack frame. struct Frame<'a, 'tcx: 'a> { - /// The MIR for the fucntion called on this frame. + /// The MIR for the function called on this frame. mir: &'a mir::Mir<'tcx>, /// A pointer for writing the return value of the current call, if it's not a diverging call. From 7740268dd5c5c0d6b5deef0a5fdacb567fa7a946 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 16:08:23 -0600 Subject: [PATCH 0094/1096] Reimplement crate-local function calls. --- src/interpreter.rs | 64 ++++++++++++++++++++++++---------------------- test/calls.rs | 38 +++++++++++++-------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e5cebc0a72b74..b364e7286971f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -92,7 +92,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn push_stack_frame(&mut self, mir: &'a mir::Mir<'tcx>, args: &[&mir::Operand<'tcx>], + fn push_stack_frame(&mut self, mir: &'a mir::Mir<'tcx>, args: &[mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); @@ -132,7 +132,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn call(&mut self, mir: &'a mir::Mir<'tcx>, args: &[&mir::Operand<'tcx>], + fn call(&mut self, mir: &'a mir::Mir<'tcx>, args: &[mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { try!(self.push_stack_frame(mir, args, return_ptr)); let mut current_block = mir::START_BLOCK; @@ -188,34 +188,37 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { current_block = targets[discr_val.to_int() as usize]; } - // Call { ref func, ref args, ref destination, .. } => { - // use rustc::middle::cstore::CrateStore; - // let ptr = destination.as_ref().map(|&(ref lv, _)| self.lvalue_to_ptr(lv)); - // let func_val = self.operand_to_ptr(func); - - // if let Value::Func(def_id) = func_val { - // let mir_data; - // let mir = match self.tcx.map.as_local_node_id(def_id) { - // Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), - // None => { - // let cstore = &self.tcx.sess.cstore; - // mir_data = cstore.maybe_get_item_mir(self.tcx, def_id).unwrap(); - // &mir_data - // } - // }; - - // let arg_vals: Vec = - // args.iter().map(|arg| self.operand_to_ptr(arg)).collect(); - - // self.call(mir, &arg_vals, ptr); - - // if let Some((_, target)) = *destination { - // current_block = target; - // } - // } else { - // panic!("tried to call a non-function value: {:?}", func_val); - // } - // } + Call { ref func, ref args, ref destination, .. } => { + let ptr = match *destination { + Some((ref lv, _)) => Some(try!(self.eval_lvalue(lv)).0), + None => None, + }; + let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); + + match func_ty.sty { + ty::TyFnDef(def_id, _, _) => { + // let mir_data; + let mir = match self.tcx.map.as_local_node_id(def_id) { + Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), + None => { + unimplemented!() + // use rustc::middle::cstore::CrateStore; + // let cs = &self.tcx.sess.cstore; + // mir_data = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); + // &mir_data + } + }; + + try!(self.call(mir, args, ptr)); + } + + _ => panic!("can't handle callee of type {:?}", func_ty), + } + + if let Some((_, target)) = *destination { + current_block = target; + } + } Drop { target, .. } => { // TODO: Handle destructors and dynamic drop. @@ -223,7 +226,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } Resume => unimplemented!(), - _ => unimplemented!(), } } diff --git a/test/calls.rs b/test/calls.rs index 9cdba3737828e..d56349ddf73b3 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -1,27 +1,25 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -// #[miri_run(expected = "Int(2)")] -// fn call() -> i64 { -// fn increment(x: i64) -> i64 { -// x + 1 -// } +#[miri_run] +fn call() -> i32 { + fn increment(x: i32) -> i32 { + x + 1 + } + increment(1) +} -// increment(1) -// } - -// #[miri_run(expected = "Int(3628800)")] -// fn factorial_recursive() -> i64 { -// fn fact(n: i64) -> i64 { -// if n == 0 { -// 1 -// } else { -// n * fact(n - 1) -// } -// } - -// fact(10) -// } +#[miri_run] +fn factorial_recursive() -> i64 { + fn fact(n: i64) -> i64 { + if n == 0 { + 1 + } else { + n * fact(n - 1) + } + } + fact(10) +} // Test calling a very simple function from the standard library. // #[miri_run(expected = "Int(1)")] From 1a27734a7b7657e28baad2505aab39a962028108 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 17:19:42 -0600 Subject: [PATCH 0095/1096] Reimplement cross-crate function calls. --- src/interpreter.rs | 67 +++++++++++++++++++++++++++++++++------------- test/calls.rs | 8 +++--- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b364e7286971f..e51e6fd5d17be 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,10 +1,15 @@ use rustc::middle::const_eval; +use rustc::middle::def_id::DefId; use rustc::middle::ty::{self, TyCtxt}; use rustc::middle::subst::Substs; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; +use rustc::util::nodemap::DefIdMap; +use std::cell::RefCell; use std::error::Error; use std::fmt; +use std::ops::Deref; +use std::rc::Rc; use memory::{FieldRepr, Memory, Pointer, Repr}; use primval::{self, PrimVal}; @@ -41,10 +46,26 @@ impl fmt::Display for EvalError { } } +#[derive(Clone)] +pub enum CachedMir<'mir, 'tcx: 'mir> { + Ref(&'mir mir::Mir<'tcx>), + Owned(Rc>) +} + +impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { + type Target = mir::Mir<'tcx>; + fn deref(&self) -> &mir::Mir<'tcx> { + match *self { + CachedMir::Ref(r) => r, + CachedMir::Owned(ref rc) => &rc, + } + } +} + /// A stack frame. struct Frame<'a, 'tcx: 'a> { /// The MIR for the function called on this frame. - mir: &'a mir::Mir<'tcx>, + mir: CachedMir<'a, 'tcx>, /// A pointer for writing the return value of the current call, if it's not a diverging call. return_ptr: Option, @@ -78,6 +99,7 @@ impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { struct Interpreter<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, + mir_cache: RefCell>>>, memory: Memory, stack: Vec>, } @@ -87,12 +109,13 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Interpreter { tcx: tcx, mir_map: mir_map, + mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(), stack: Vec::new(), } } - fn push_stack_frame(&mut self, mir: &'a mir::Mir<'tcx>, args: &[mir::Operand<'tcx>], + fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); @@ -117,7 +140,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { })); self.stack.push(Frame { - mir: mir, + mir: mir.clone(), return_ptr: return_ptr, locals: locals, var_offset: num_args, @@ -132,9 +155,28 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn call(&mut self, mir: &'a mir::Mir<'tcx>, args: &[mir::Operand<'tcx>], + fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + match self.tcx.map.as_local_node_id(def_id) { + Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), + None => { + let mut mir_cache = self.mir_cache.borrow_mut(); + if let Some(mir) = mir_cache.get(&def_id) { + return CachedMir::Owned(mir.clone()); + } + + use rustc::middle::cstore::CrateStore; + let cs = &self.tcx.sess.cstore; + let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); + let cached = Rc::new(mir); + mir_cache.insert(def_id, cached.clone()); + CachedMir::Owned(cached) + } + } + } + + fn call(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { - try!(self.push_stack_frame(mir, args, return_ptr)); + try!(self.push_stack_frame(mir.clone(), args, return_ptr)); let mut current_block = mir::START_BLOCK; loop { @@ -197,18 +239,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match func_ty.sty { ty::TyFnDef(def_id, _, _) => { - // let mir_data; - let mir = match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => self.mir_map.map.get(&node_id).unwrap(), - None => { - unimplemented!() - // use rustc::middle::cstore::CrateStore; - // let cs = &self.tcx.sess.cstore; - // mir_data = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); - // &mir_data - } - }; - + let mir = self.load_mir(def_id); try!(self.call(mir, args, ptr)); } @@ -491,7 +522,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } ty::FnDiverging => None, }; - miri.call(mir, &[], return_ptr).unwrap(); + miri.call(CachedMir::Ref(mir), &[], return_ptr).unwrap(); if let Some(ret) = return_ptr { println!("Returned: {:?}\n", miri.memory.get(ret.alloc_id).unwrap()); diff --git a/test/calls.rs b/test/calls.rs index d56349ddf73b3..1b562b1b729ac 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -22,7 +22,7 @@ fn factorial_recursive() -> i64 { } // Test calling a very simple function from the standard library. -// #[miri_run(expected = "Int(1)")] -// fn cross_crate_fn_call() -> i64 { -// if 1i64.is_positive() { 1 } else { 0 } -// } +#[miri_run] +fn cross_crate_fn_call() -> i64 { + if 1i32.is_positive() { 1 } else { 0 } +} From 66eb109070a824057e61571bc2b2714cad6a65d0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 13 Mar 2016 18:33:26 -0600 Subject: [PATCH 0096/1096] Properly handle generic fn calls. --- src/interpreter.rs | 12 +++++++++--- test/calls.rs | 6 ++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e51e6fd5d17be..2b5c4ffa2fdf4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,7 +1,7 @@ use rustc::middle::const_eval; use rustc::middle::def_id::DefId; use rustc::middle::ty::{self, TyCtxt}; -use rustc::middle::subst::Substs; +use rustc::middle::subst::{Subst, Substs}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::util::nodemap::DefIdMap; @@ -102,6 +102,7 @@ struct Interpreter<'a, 'tcx: 'a> { mir_cache: RefCell>>>, memory: Memory, stack: Vec>, + substs_stack: Vec<&'tcx Substs<'tcx>>, } impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { @@ -112,6 +113,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(), stack: Vec::new(), + substs_stack: vec![tcx.mk_substs(Substs::empty())], } } @@ -238,7 +240,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); match func_ty.sty { - ty::TyFnDef(def_id, _, _) => { + ty::TyFnDef(def_id, substs, _) => { + self.substs_stack.push(substs); let mir = self.load_mir(def_id); try!(self.call(mir, args, ptr)); } @@ -261,6 +264,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } self.pop_stack_frame(); + self.substs_stack.pop(); Ok(()) } @@ -450,7 +454,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { use syntax::ast::IntTy; - match ty.sty { + let substs = self.substs_stack.last().unwrap(); + + match ty.subst(self.tcx, substs).sty { ty::TyBool => Repr::Bool, ty::TyInt(IntTy::Is) => unimplemented!(), diff --git a/test/calls.rs b/test/calls.rs index 1b562b1b729ac..56f5237113dbf 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -21,6 +21,12 @@ fn factorial_recursive() -> i64 { fact(10) } +#[miri_run] +fn call_generic() -> (i16, bool) { + fn id(t: T) -> T { t } + (id(42), id(true)) +} + // Test calling a very simple function from the standard library. #[miri_run] fn cross_crate_fn_call() -> i64 { From 7bac5963b9d5f50b5652c071432445c0740ddef4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 20:39:51 -0600 Subject: [PATCH 0097/1096] Handle recursive calls without recursing in miri. --- src/interpreter.rs | 147 ++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2b5c4ffa2fdf4..b602857e911f3 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -67,6 +67,9 @@ struct Frame<'a, 'tcx: 'a> { /// The MIR for the function called on this frame. mir: CachedMir<'a, 'tcx>, + /// The block in the MIR this frame will execute once a fn call returns back to this frame. + next_block: mir::BasicBlock, + /// A pointer for writing the return value of the current call, if it's not a diverging call. return_ptr: Option, @@ -113,7 +116,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(), stack: Vec::new(), - substs_stack: vec![tcx.mk_substs(Substs::empty())], + substs_stack: Vec::new(), } } @@ -143,6 +146,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.stack.push(Frame { mir: mir.clone(), + next_block: mir::START_BLOCK, return_ptr: return_ptr, locals: locals, var_offset: num_args, @@ -176,95 +180,96 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn call(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>], - return_ptr: Option) -> EvalResult<()> { - try!(self.push_stack_frame(mir.clone(), args, return_ptr)); - let mut current_block = mir::START_BLOCK; + fn run(&mut self) -> EvalResult<()> { + 'outer: while !self.stack.is_empty() { + let mut current_block = self.current_frame().next_block; - loop { - if TRACE_EXECUTION { println!("Entering block: {:?}", current_block); } - let block_data = mir.basic_block_data(current_block); + loop { + if TRACE_EXECUTION { println!("Entering block: {:?}", current_block); } + let current_mir = self.current_frame().mir.clone(); // Cloning a reference. + let block_data = current_mir.basic_block_data(current_block); - for stmt in &block_data.statements { - if TRACE_EXECUTION { println!("{:?}", stmt); } - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - try!(self.eval_assignment(lvalue, rvalue)); - } + for stmt in &block_data.statements { + if TRACE_EXECUTION { println!("{:?}", stmt); } + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + try!(self.eval_assignment(lvalue, rvalue)); + } - if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } + if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } - use rustc::mir::repr::Terminator::*; - match *block_data.terminator() { - Return => break, + use rustc::mir::repr::Terminator::*; + match *block_data.terminator() { + Return => break, - Goto { target } => current_block = target, + Goto { target } => current_block = target, - If { ref cond, targets: (then_target, else_target) } => { - let (cond_ptr, _) = try!(self.eval_operand(cond)); - let cond_val = try!(self.memory.read_bool(cond_ptr)); - current_block = if cond_val { then_target } else { else_target }; - } + If { ref cond, targets: (then_target, else_target) } => { + let (cond_ptr, _) = try!(self.eval_operand(cond)); + let cond_val = try!(self.memory.read_bool(cond_ptr)); + current_block = if cond_val { then_target } else { else_target }; + } - SwitchInt { ref discr, ref values, ref targets, .. } => { - let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); - let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); + SwitchInt { ref discr, ref values, ref targets, .. } => { + let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); + let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); - // Branch to the `otherwise` case by default, if no match is found. - current_block = targets[targets.len() - 1]; + // Branch to the `otherwise` case by default, if no match is found. + current_block = targets[targets.len() - 1]; - for (index, val_const) in values.iter().enumerate() { - let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_primval(ptr, &discr_repr)); - if discr_val == val { - current_block = targets[index]; - break; + for (index, val_const) in values.iter().enumerate() { + let ptr = try!(self.const_to_ptr(val_const)); + let val = try!(self.memory.read_primval(ptr, &discr_repr)); + if discr_val == val { + current_block = targets[index]; + break; + } } } - } - Switch { ref discr, ref targets, .. } => { - let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); - let discr_repr = match adt_repr { - Repr::Sum { ref discr, .. } => discr, - _ => panic!("attmpted to switch on non-sum type"), - }; - let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); - current_block = targets[discr_val.to_int() as usize]; - } + Switch { ref discr, ref targets, .. } => { + let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); + let discr_repr = match adt_repr { + Repr::Sum { ref discr, .. } => discr, + _ => panic!("attmpted to switch on non-sum type"), + }; + let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); + current_block = targets[discr_val.to_int() as usize]; + } - Call { ref func, ref args, ref destination, .. } => { - let ptr = match *destination { - Some((ref lv, _)) => Some(try!(self.eval_lvalue(lv)).0), - None => None, - }; - let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); - - match func_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - self.substs_stack.push(substs); - let mir = self.load_mir(def_id); - try!(self.call(mir, args, ptr)); + Call { ref func, ref args, ref destination, .. } => { + let mut return_ptr = None; + if let Some((ref lv, target)) = *destination { + self.current_frame_mut().next_block = target; + return_ptr = Some(try!(self.eval_lvalue(lv)).0) } - _ => panic!("can't handle callee of type {:?}", func_ty), + let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); + + match func_ty.sty { + ty::TyFnDef(def_id, substs, _) => { + let mir = self.load_mir(def_id); + self.substs_stack.push(substs); + try!(self.push_stack_frame(mir, args, return_ptr)); + continue 'outer; + } + + _ => panic!("can't handle callee of type {:?}", func_ty), + } } - if let Some((_, target)) = *destination { + Drop { target, .. } => { + // TODO: Handle destructors and dynamic drop. current_block = target; } - } - Drop { target, .. } => { - // TODO: Handle destructors and dynamic drop. - current_block = target; + Resume => unimplemented!(), } - - Resume => unimplemented!(), } + + self.pop_stack_frame(); + self.substs_stack.pop(); } - self.pop_stack_frame(); - self.substs_stack.pop(); Ok(()) } @@ -454,7 +459,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { use syntax::ast::IntTy; - let substs = self.substs_stack.last().unwrap(); + let substs = self.substs_stack.last().map(|&s| s) + .unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())); match ty.subst(self.tcx, substs).sty { ty::TyBool => Repr::Bool, @@ -509,6 +515,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn current_frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } + + fn current_frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { + self.stack.last_mut().expect("no call frames exist") + } } pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { @@ -528,7 +538,8 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } ty::FnDiverging => None, }; - miri.call(CachedMir::Ref(mir), &[], return_ptr).unwrap(); + miri.push_stack_frame(CachedMir::Ref(mir), &[], return_ptr).unwrap(); + miri.run().unwrap(); if let Some(ret) = return_ptr { println!("Returned: {:?}\n", miri.memory.get(ret.alloc_id).unwrap()); From 4a22283d8bc872a8e3bd486744c261aca906f7f0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 21:18:39 -0600 Subject: [PATCH 0098/1096] Extract Terminator evaluation out of the main loop. --- src/interpreter.rs | 193 +++++++++++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 78 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b602857e911f3..09e24c90f38d4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -62,15 +62,37 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { } } +struct Interpreter<'a, 'tcx: 'a> { + /// The results of the type checker, from rustc. + tcx: &'a TyCtxt<'tcx>, + + /// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items. + mir_map: &'a MirMap<'tcx>, + + /// A local cache from DefIds to Mir for non-crate-local items. + mir_cache: RefCell>>>, + + /// The virtual memory system. + memory: Memory, + + /// The virtual call stack. + stack: Vec>, + + /// Another stack containing the type substitutions for the current function invocation. Exists + /// separately from `stack` because it must contain the `Substs` for a function while + /// *creating* the `Frame` for that same function. + substs_stack: Vec<&'tcx Substs<'tcx>>, +} + /// A stack frame. struct Frame<'a, 'tcx: 'a> { /// The MIR for the function called on this frame. mir: CachedMir<'a, 'tcx>, - /// The block in the MIR this frame will execute once a fn call returns back to this frame. + /// The block this frame will execute when a function call returns back to this frame. next_block: mir::BasicBlock, - /// A pointer for writing the return value of the current call, if it's not a diverging call. + /// A pointer for writing the return value of the current call if it's not a diverging call. return_ptr: Option, /// The list of locals for the current function, stored in order as @@ -85,27 +107,16 @@ struct Frame<'a, 'tcx: 'a> { temp_offset: usize, } -impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { - fn arg_ptr(&self, i: u32) -> Pointer { - self.locals[i as usize] - } - - fn var_ptr(&self, i: u32) -> Pointer { - self.locals[self.var_offset + i as usize] - } +/// Represents the action to be taken in the main loop as a result of executing a terminator. +enum TerminatorTarget { + /// Make a local jump to the given block. + Block(mir::BasicBlock), - fn temp_ptr(&self, i: u32) -> Pointer { - self.locals[self.temp_offset + i as usize] - } -} + /// Start executing from the new current frame. (For function calls.) + Call, -struct Interpreter<'a, 'tcx: 'a> { - tcx: &'a TyCtxt<'tcx>, - mir_map: &'a MirMap<'tcx>, - mir_cache: RefCell>>>, - memory: Memory, - stack: Vec>, - substs_stack: Vec<&'tcx Substs<'tcx>>, + /// Stop executing the current frame and resume the previous frame. + Return, } impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { @@ -195,82 +206,94 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { try!(self.eval_assignment(lvalue, rvalue)); } - if TRACE_EXECUTION { println!("{:?}", block_data.terminator()); } + let terminator = block_data.terminator(); + if TRACE_EXECUTION { println!("{:?}", terminator); } + match try!(self.eval_terminator(terminator)) { + TerminatorTarget::Block(block) => current_block = block, + TerminatorTarget::Return => break, + TerminatorTarget::Call => continue 'outer, + } + } + + self.pop_stack_frame(); + self.substs_stack.pop(); + } - use rustc::mir::repr::Terminator::*; - match *block_data.terminator() { - Return => break, + Ok(()) + } - Goto { target } => current_block = target, + fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult { + use rustc::mir::repr::Terminator::*; + let target = match *terminator { + Return => TerminatorTarget::Return, - If { ref cond, targets: (then_target, else_target) } => { - let (cond_ptr, _) = try!(self.eval_operand(cond)); - let cond_val = try!(self.memory.read_bool(cond_ptr)); - current_block = if cond_val { then_target } else { else_target }; - } + Goto { target } => TerminatorTarget::Block(target), - SwitchInt { ref discr, ref values, ref targets, .. } => { - let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); - let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); + If { ref cond, targets: (then_target, else_target) } => { + let (cond_ptr, _) = try!(self.eval_operand(cond)); + let cond_val = try!(self.memory.read_bool(cond_ptr)); + TerminatorTarget::Block(if cond_val { then_target } else { else_target }) + } - // Branch to the `otherwise` case by default, if no match is found. - current_block = targets[targets.len() - 1]; + SwitchInt { ref discr, ref values, ref targets, .. } => { + let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); + let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); - for (index, val_const) in values.iter().enumerate() { - let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_primval(ptr, &discr_repr)); - if discr_val == val { - current_block = targets[index]; - break; - } - } - } + // Branch to the `otherwise` case by default, if no match is found. + let mut target_block = targets[targets.len() - 1]; - Switch { ref discr, ref targets, .. } => { - let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); - let discr_repr = match adt_repr { - Repr::Sum { ref discr, .. } => discr, - _ => panic!("attmpted to switch on non-sum type"), - }; - let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); - current_block = targets[discr_val.to_int() as usize]; + for (index, val_const) in values.iter().enumerate() { + let ptr = try!(self.const_to_ptr(val_const)); + let val = try!(self.memory.read_primval(ptr, &discr_repr)); + if discr_val == val { + target_block = targets[index]; + break; } + } - Call { ref func, ref args, ref destination, .. } => { - let mut return_ptr = None; - if let Some((ref lv, target)) = *destination { - self.current_frame_mut().next_block = target; - return_ptr = Some(try!(self.eval_lvalue(lv)).0) - } + TerminatorTarget::Block(target_block) + } - let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); + Switch { ref discr, ref targets, .. } => { + let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); + let discr_repr = match adt_repr { + Repr::Sum { ref discr, .. } => discr, + _ => panic!("attmpted to switch on non-sum type"), + }; + let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); + TerminatorTarget::Block(targets[discr_val.to_int() as usize]) + } - match func_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let mir = self.load_mir(def_id); - self.substs_stack.push(substs); - try!(self.push_stack_frame(mir, args, return_ptr)); - continue 'outer; - } + Call { ref func, ref args, ref destination, .. } => { + let mut return_ptr = None; + if let Some((ref lv, target)) = *destination { + self.current_frame_mut().next_block = target; + return_ptr = Some(try!(self.eval_lvalue(lv)).0) + } - _ => panic!("can't handle callee of type {:?}", func_ty), - } - } + let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); - Drop { target, .. } => { - // TODO: Handle destructors and dynamic drop. - current_block = target; + match func_ty.sty { + ty::TyFnDef(def_id, substs, _) => { + let mir = self.load_mir(def_id); + self.substs_stack.push(substs); + try!(self.push_stack_frame(mir, args, return_ptr)); + TerminatorTarget::Call } - Resume => unimplemented!(), + _ => panic!("can't handle callee of type {:?}", func_ty), } } - self.pop_stack_frame(); - self.substs_stack.pop(); - } + Drop { target, .. } => { + // TODO: Handle destructors and dynamic drop. + TerminatorTarget::Block(target) + } - Ok(()) + Resume => unimplemented!(), + }; + + Ok(target) } fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr, @@ -521,6 +544,20 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } +impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { + fn arg_ptr(&self, i: u32) -> Pointer { + self.locals[i as usize] + } + + fn var_ptr(&self, i: u32) -> Pointer { + self.locals[self.var_offset + i as usize] + } + + fn temp_ptr(&self, i: u32) -> Pointer { + self.locals[self.temp_offset + i as usize] + } +} + pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { From f74d1dc7f12e4b8996a2bbdf0b48410ce99bee31 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 21:29:18 -0600 Subject: [PATCH 0099/1096] Improve execution trace logging. --- src/interpreter.rs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 09e24c90f38d4..e9d4cc1deb833 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -192,31 +192,49 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } fn run(&mut self) -> EvalResult<()> { + fn print_indent(n: usize) { + for _ in 0..n { + print!(" "); + } + } + 'outer: while !self.stack.is_empty() { let mut current_block = self.current_frame().next_block; loop { - if TRACE_EXECUTION { println!("Entering block: {:?}", current_block); } + if TRACE_EXECUTION { + print_indent(self.stack.len()); + println!("{:?}:", current_block); + } + let current_mir = self.current_frame().mir.clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { - if TRACE_EXECUTION { println!("{:?}", stmt); } + if TRACE_EXECUTION { + print_indent(self.stack.len() + 1); + println!("{:?}", stmt); + } let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; try!(self.eval_assignment(lvalue, rvalue)); } let terminator = block_data.terminator(); - if TRACE_EXECUTION { println!("{:?}", terminator); } + if TRACE_EXECUTION { + print_indent(self.stack.len() + 1); + println!("{:?}", terminator); + } + match try!(self.eval_terminator(terminator)) { TerminatorTarget::Block(block) => current_block = block, - TerminatorTarget::Return => break, + TerminatorTarget::Return => { + self.pop_stack_frame(); + self.substs_stack.pop(); + continue 'outer; + } TerminatorTarget::Call => continue 'outer, } } - - self.pop_stack_frame(); - self.substs_stack.pop(); } Ok(()) From a7c7764c931b59f4bf56ad8bba1a31bb6ac70d4d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 21:48:00 -0600 Subject: [PATCH 0100/1096] Reorganize and simplify. --- src/error.rs | 32 +++++++ src/interpreter.rs | 208 +++++++++++++++++---------------------------- src/lib.rs | 1 + src/memory.rs | 2 +- 4 files changed, 111 insertions(+), 132 deletions(-) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000..a05250e3b5482 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::error::Error; +use std::fmt; + +#[derive(Clone, Debug)] +pub enum EvalError { + DanglingPointerDeref, + InvalidBool, + PointerOutOfBounds, + InvalidPointerAccess, +} + +pub type EvalResult = Result; + +impl Error for EvalError { + fn description(&self) -> &str { + match *self { + EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", + EvalError::InvalidBool => "invalid boolean value read", + EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", + EvalError::InvalidPointerAccess => + "a raw memory access tried to access part of a pointer value as bytes", + } + } + + fn cause(&self) -> Option<&Error> { None } +} + +impl fmt::Display for EvalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} diff --git a/src/interpreter.rs b/src/interpreter.rs index e9d4cc1deb833..145d3d30808d9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,67 +1,20 @@ use rustc::middle::const_eval; use rustc::middle::def_id::DefId; -use rustc::middle::ty::{self, TyCtxt}; use rustc::middle::subst::{Subst, Substs}; +use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; -use std::error::Error; -use std::fmt; use std::ops::Deref; use std::rc::Rc; +use error::EvalResult; use memory::{FieldRepr, Memory, Pointer, Repr}; use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = true; -#[derive(Clone, Debug)] -pub enum EvalError { - DanglingPointerDeref, - InvalidBool, - PointerOutOfBounds, - InvalidPointerAccess, -} - -pub type EvalResult = Result; - -impl Error for EvalError { - fn description(&self) -> &str { - match *self { - EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", - EvalError::InvalidBool => "invalid boolean value read", - EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", - EvalError::InvalidPointerAccess => - "a raw memory access tried to access part of a pointer value as bytes", - } - } - - fn cause(&self) -> Option<&Error> { None } -} - -impl fmt::Display for EvalError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -#[derive(Clone)] -pub enum CachedMir<'mir, 'tcx: 'mir> { - Ref(&'mir mir::Mir<'tcx>), - Owned(Rc>) -} - -impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { - type Target = mir::Mir<'tcx>; - fn deref(&self) -> &mir::Mir<'tcx> { - match *self { - CachedMir::Ref(r) => r, - CachedMir::Owned(ref rc) => &rc, - } - } -} - struct Interpreter<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: &'a TyCtxt<'tcx>, @@ -78,8 +31,8 @@ struct Interpreter<'a, 'tcx: 'a> { /// The virtual call stack. stack: Vec>, - /// Another stack containing the type substitutions for the current function invocation. Exists - /// separately from `stack` because it must contain the `Substs` for a function while + /// Another stack containing the type substitutions for the current function invocation. It + /// exists separately from `stack` because it must contain the `Substs` for a function while /// *creating* the `Frame` for that same function. substs_stack: Vec<&'tcx Substs<'tcx>>, } @@ -107,6 +60,12 @@ struct Frame<'a, 'tcx: 'a> { temp_offset: usize, } +#[derive(Clone)] +enum CachedMir<'mir, 'tcx: 'mir> { + Ref(&'mir mir::Mir<'tcx>), + Owned(Rc>) +} + /// Represents the action to be taken in the main loop as a result of executing a terminator. enum TerminatorTarget { /// Make a local jump to the given block. @@ -131,6 +90,46 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + fn run(&mut self) -> EvalResult<()> { + use std::fmt::Debug; + fn print_trace(t: &T, suffix: &'static str, indent: usize) { + if !TRACE_EXECUTION { return; } + for _ in 0..indent { print!(" "); } + println!("{:?}{}", t, suffix); + } + + 'outer: while !self.stack.is_empty() { + let mut current_block = self.current_frame().next_block; + + loop { + print_trace(¤t_block, ":", self.stack.len()); + let current_mir = self.current_frame().mir.clone(); // Cloning a reference. + let block_data = current_mir.basic_block_data(current_block); + + for stmt in &block_data.statements { + print_trace(stmt, "", self.stack.len() + 1); + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + try!(self.eval_assignment(lvalue, rvalue)); + } + + let terminator = block_data.terminator(); + print_trace(terminator, "", self.stack.len() + 1); + + match try!(self.eval_terminator(terminator)) { + TerminatorTarget::Block(block) => current_block = block, + TerminatorTarget::Return => { + self.pop_stack_frame(); + self.substs_stack.pop(); + continue 'outer; + } + TerminatorTarget::Call => continue 'outer, + } + } + } + + Ok(()) + } + fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>], return_ptr: Option) -> EvalResult<()> { let num_args = mir.arg_decls.len(); @@ -172,74 +171,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { - match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), - None => { - let mut mir_cache = self.mir_cache.borrow_mut(); - if let Some(mir) = mir_cache.get(&def_id) { - return CachedMir::Owned(mir.clone()); - } - - use rustc::middle::cstore::CrateStore; - let cs = &self.tcx.sess.cstore; - let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); - let cached = Rc::new(mir); - mir_cache.insert(def_id, cached.clone()); - CachedMir::Owned(cached) - } - } - } - - fn run(&mut self) -> EvalResult<()> { - fn print_indent(n: usize) { - for _ in 0..n { - print!(" "); - } - } - - 'outer: while !self.stack.is_empty() { - let mut current_block = self.current_frame().next_block; - - loop { - if TRACE_EXECUTION { - print_indent(self.stack.len()); - println!("{:?}:", current_block); - } - - let current_mir = self.current_frame().mir.clone(); // Cloning a reference. - let block_data = current_mir.basic_block_data(current_block); - - for stmt in &block_data.statements { - if TRACE_EXECUTION { - print_indent(self.stack.len() + 1); - println!("{:?}", stmt); - } - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - try!(self.eval_assignment(lvalue, rvalue)); - } - - let terminator = block_data.terminator(); - if TRACE_EXECUTION { - print_indent(self.stack.len() + 1); - println!("{:?}", terminator); - } - - match try!(self.eval_terminator(terminator)) { - TerminatorTarget::Block(block) => current_block = block, - TerminatorTarget::Return => { - self.pop_stack_frame(); - self.substs_stack.pop(); - continue 'outer; - } - TerminatorTarget::Call => continue 'outer, - } - } - } - - Ok(()) - } - fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult { use rustc::mir::repr::Terminator::*; let target = match *terminator { @@ -417,9 +348,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let ptr = match *lvalue { ReturnPointer => frame.return_ptr.expect("ReturnPointer used in a function with no return value"), - Arg(i) => frame.arg_ptr(i), - Var(i) => frame.var_ptr(i), - Temp(i) => frame.temp_ptr(i), + Arg(i) => frame.locals[i as usize], + Var(i) => frame.locals[frame.var_offset + i as usize], + Temp(i) => frame.locals[frame.temp_offset + i as usize], Projection(ref proj) => { let (base_ptr, base_repr) = try!(self.eval_lvalue(&proj.base)); @@ -560,19 +491,34 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn current_frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } -} -impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { - fn arg_ptr(&self, i: u32) -> Pointer { - self.locals[i as usize] - } + fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + match self.tcx.map.as_local_node_id(def_id) { + Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), + None => { + let mut mir_cache = self.mir_cache.borrow_mut(); + if let Some(mir) = mir_cache.get(&def_id) { + return CachedMir::Owned(mir.clone()); + } - fn var_ptr(&self, i: u32) -> Pointer { - self.locals[self.var_offset + i as usize] + use rustc::middle::cstore::CrateStore; + let cs = &self.tcx.sess.cstore; + let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); + let cached = Rc::new(mir); + mir_cache.insert(def_id, cached.clone()); + CachedMir::Owned(cached) + } + } } +} - fn temp_ptr(&self, i: u32) -> Pointer { - self.locals[self.temp_offset + i as usize] +impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { + type Target = mir::Mir<'tcx>; + fn deref(&self) -> &mir::Mir<'tcx> { + match *self { + CachedMir::Ref(r) => r, + CachedMir::Owned(ref rc) => &rc, + } } } diff --git a/src/lib.rs b/src/lib.rs index c7b5df9fa3d17..9b938d5147225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate rustc; extern crate rustc_mir; extern crate syntax; +mod error; pub mod interpreter; mod memory; mod primval; diff --git a/src/memory.rs b/src/memory.rs index acc5597a4264c..8a962d804b7b0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; use std::ptr; -use interpreter::{EvalError, EvalResult}; +use error::{EvalError, EvalResult}; use primval::PrimVal; const POINTER_SIZE: usize = 8; From b1475e5cd4a1fc3075e193dc97dbc8f52f2f49ed Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 22:05:50 -0600 Subject: [PATCH 0101/1096] Implement Rvalue::Box allocations. --- src/interpreter.rs | 22 +++++++++++++++++++--- src/memory.rs | 10 ---------- test/heap.rs | 7 +++++++ 3 files changed, 26 insertions(+), 13 deletions(-) create mode 100755 test/heap.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 145d3d30808d9..5e45e717dfebc 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -10,7 +10,7 @@ use std::ops::Deref; use std::rc::Rc; use error::EvalResult; -use memory::{FieldRepr, Memory, Pointer, Repr}; +use memory::{self, FieldRepr, Memory, Pointer, Repr}; use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = true; @@ -319,6 +319,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_ptr(dest, ptr) } + Box(ty) => { + let repr = self.ty_to_repr(ty); + let ptr = self.memory.allocate(repr.size()); + self.memory.write_ptr(dest, ptr) + } + ref r => panic!("can't handle rvalue: {:?}", r), } } @@ -476,7 +482,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.make_variant_repr(&adt_def.variants[0], substs) } - ty::TyRef(_, ty::TypeAndMut { ty, .. }) => { + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { Repr::Pointer { target: Box::new(self.ty_to_repr(ty)) } } @@ -523,6 +529,15 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { } pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { + /// Print the given allocation and all allocations it depends on. + fn print_allocation_tree(memory: &Memory, alloc_id: memory::AllocId) { + let alloc = memory.get(alloc_id).unwrap(); + println!(" {:?}", alloc); + for &target_alloc in alloc.relocations.values() { + print_allocation_tree(memory, target_alloc); + } + } + for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { use syntax::attr::AttrMetaMethods; @@ -543,7 +558,8 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) miri.run().unwrap(); if let Some(ret) = return_ptr { - println!("Returned: {:?}\n", miri.memory.get(ret.alloc_id).unwrap()); + println!("Result:"); + print_allocation_tree(&miri.memory, ret.alloc_id); } } } diff --git a/src/memory.rs b/src/memory.rs index 8a962d804b7b0..3675476e2ee1d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -16,16 +16,6 @@ pub struct Memory { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); -/// A relocation represents a part of an allocation which points into another allocation. This is -/// used to represent pointers existing in the virtual memory. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Relocation { - /// The offset in the allocation where the relocation starts. - offset: usize, - /// The allocation this relocation points into. - target: AllocId, -} - #[derive(Debug)] pub struct Allocation { pub bytes: Vec, diff --git a/test/heap.rs b/test/heap.rs new file mode 100755 index 0000000000000..c97633c656981 --- /dev/null +++ b/test/heap.rs @@ -0,0 +1,7 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn make_box() -> Box { + Box::new(42) +} From fbb9dd260d5bef6d35e5fb5ab927851fbcfdd3c9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 22:08:38 -0600 Subject: [PATCH 0102/1096] Clarify output allocations. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5e45e717dfebc..2d81b1b265fd7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -532,7 +532,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) /// Print the given allocation and all allocations it depends on. fn print_allocation_tree(memory: &Memory, alloc_id: memory::AllocId) { let alloc = memory.get(alloc_id).unwrap(); - println!(" {:?}", alloc); + println!(" {:?}: {:?}", alloc_id, alloc); for &target_alloc in alloc.relocations.values() { print_allocation_tree(memory, target_alloc); } From 9e1bb9841ee885be82fce24752a8e3de17ab485b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 22:26:39 -0600 Subject: [PATCH 0103/1096] Fix substs in nested generic function calls. --- src/interpreter.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2d81b1b265fd7..14e4323a5f195 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -225,6 +225,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match func_ty.sty { ty::TyFnDef(def_id, substs, _) => { let mir = self.load_mir(def_id); + let substs = self.tcx.mk_substs( + substs.subst(self.tcx, self.current_substs())); self.substs_stack.push(substs); try!(self.push_stack_frame(mir, args, return_ptr)); TerminatorTarget::Call @@ -436,11 +438,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { - use syntax::ast::IntTy; - let substs = self.substs_stack.last().map(|&s| s) - .unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())); - - match ty.subst(self.tcx, substs).sty { + use syntax::ast::{IntTy, UintTy}; + match ty.subst(self.tcx, self.current_substs()).sty { ty::TyBool => Repr::Bool, ty::TyInt(IntTy::Is) => unimplemented!(), @@ -498,6 +497,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } + fn current_substs(&self) -> &'tcx Substs<'tcx> { + self.substs_stack.last().cloned().unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())) + } + fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), @@ -560,6 +563,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) if let Some(ret) = return_ptr { println!("Result:"); print_allocation_tree(&miri.memory, ret.alloc_id); + println!(""); } } } From 9d1d96ce07dabcbef38d4a0ddaa42d90919cba8e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 23:03:31 -0600 Subject: [PATCH 0104/1096] Add unsigned integers. --- src/interpreter.rs | 22 +++++++---- src/memory.rs | 95 ++++++++++++++++++++++++++++++++++++++-------- src/primval.rs | 44 +++++++++++---------- 3 files changed, 118 insertions(+), 43 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 14e4323a5f195..17ab9a52bbac7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { _ => panic!("attmpted to switch on non-sum type"), }; let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); - TerminatorTarget::Block(targets[discr_val.to_int() as usize]) + TerminatorTarget::Block(targets[discr_val.to_usize()]) } Call { ref func, ref args, ref destination, .. } => { @@ -298,7 +298,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::AdtKind::Enum => match dest_repr { Repr::Sum { ref discr, ref variants, .. } => { if discr.size() > 0 { - let discr_val = PrimVal::from_int(variant_idx as i64, discr); + let discr_val = PrimVal::from_usize(variant_idx, discr); try!(self.memory.write_primval(dest, discr_val)); } self.assign_to_product( @@ -442,12 +442,18 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match ty.subst(self.tcx, self.current_substs()).sty { ty::TyBool => Repr::Bool, - ty::TyInt(IntTy::Is) => unimplemented!(), - ty::TyInt(IntTy::I8) => Repr::I8, + ty::TyInt(IntTy::Is) => Repr::isize(), + ty::TyInt(IntTy::I8) => Repr::I8, ty::TyInt(IntTy::I16) => Repr::I16, ty::TyInt(IntTy::I32) => Repr::I32, ty::TyInt(IntTy::I64) => Repr::I64, + ty::TyUint(UintTy::Us) => Repr::usize(), + ty::TyUint(UintTy::U8) => Repr::U8, + ty::TyUint(UintTy::U16) => Repr::U16, + ty::TyUint(UintTy::U32) => Repr::U32, + ty::TyUint(UintTy::U64) => Repr::U64, + ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), ty::TyEnum(adt_def, substs) => { @@ -456,13 +462,13 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let discr = if num_variants <= 1 { Repr::Product { size: 0, fields: vec![] } } else if num_variants <= 1 << 8 { - Repr::I8 + Repr::U8 } else if num_variants <= 1 << 16 { - Repr::I16 + Repr::U16 } else if num_variants <= 1 << 32 { - Repr::I32 + Repr::U32 } else { - Repr::I64 + Repr::U64 }; let variants: Vec = adt_def.variants.iter().map(|v| { diff --git a/src/memory.rs b/src/memory.rs index 3675476e2ee1d..5ab8872e85e38 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,6 +1,7 @@ use byteorder::{self, ByteOrder}; use std::collections::{BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; +use std::mem; use std::ptr; use error::{EvalError, EvalResult}; @@ -38,10 +39,8 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { Bool, - I8, - I16, - I32, - I64, + I8, I16, I32, I64, + U8, U16, U32, U64, /// The representation for product types including tuples, structs, and the contents of enum /// variants. @@ -170,10 +169,14 @@ impl Memory { pub fn read_primval(&self, ptr: Pointer, repr: &Repr) -> EvalResult { match *repr { Repr::Bool => self.read_bool(ptr).map(PrimVal::Bool), - Repr::I8 => self.read_i8(ptr).map(PrimVal::I8), - Repr::I16 => self.read_i16(ptr).map(PrimVal::I16), - Repr::I32 => self.read_i32(ptr).map(PrimVal::I32), - Repr::I64 => self.read_i64(ptr).map(PrimVal::I64), + Repr::I8 => self.read_i8(ptr).map(PrimVal::I8), + Repr::I16 => self.read_i16(ptr).map(PrimVal::I16), + Repr::I32 => self.read_i32(ptr).map(PrimVal::I32), + Repr::I64 => self.read_i64(ptr).map(PrimVal::I64), + Repr::U8 => self.read_u8(ptr).map(PrimVal::U8), + Repr::U16 => self.read_u16(ptr).map(PrimVal::U16), + Repr::U32 => self.read_u32(ptr).map(PrimVal::U32), + Repr::U64 => self.read_u64(ptr).map(PrimVal::U64), _ => panic!("primitive read of non-primitive: {:?}", repr), } } @@ -181,10 +184,14 @@ impl Memory { pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { match val { PrimVal::Bool(b) => self.write_bool(ptr, b), - PrimVal::I8(n) => self.write_i8(ptr, n), - PrimVal::I16(n) => self.write_i16(ptr, n), - PrimVal::I32(n) => self.write_i32(ptr, n), - PrimVal::I64(n) => self.write_i64(ptr, n), + PrimVal::I8(n) => self.write_i8(ptr, n), + PrimVal::I16(n) => self.write_i16(ptr, n), + PrimVal::I32(n) => self.write_i32(ptr, n), + PrimVal::I64(n) => self.write_i64(ptr, n), + PrimVal::U8(n) => self.write_u8(ptr, n), + PrimVal::U16(n) => self.write_u16(ptr, n), + PrimVal::U32(n) => self.write_u32(ptr, n), + PrimVal::U64(n) => self.write_u64(ptr, n), } } @@ -240,6 +247,44 @@ impl Memory { byteorder::NativeEndian::write_i64(bytes, n); Ok(()) } + + pub fn read_u8(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 1).map(|b| b[0] as u8) + } + + pub fn write_u8(&mut self, ptr: Pointer, n: u8) -> EvalResult<()> { + self.get_bytes_mut(ptr, 1).map(|b| b[0] = n as u8) + } + + pub fn read_u16(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 2).map(byteorder::NativeEndian::read_u16) + } + + pub fn write_u16(&mut self, ptr: Pointer, n: u16) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 2)); + byteorder::NativeEndian::write_u16(bytes, n); + Ok(()) + } + + pub fn read_u32(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 4).map(byteorder::NativeEndian::read_u32) + } + + pub fn write_u32(&mut self, ptr: Pointer, n: u32) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 4)); + byteorder::NativeEndian::write_u32(bytes, n); + Ok(()) + } + + pub fn read_u64(&self, ptr: Pointer) -> EvalResult { + self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_u64) + } + + pub fn write_u64(&mut self, ptr: Pointer, n: u64) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, 8)); + byteorder::NativeEndian::write_u64(bytes, n); + Ok(()) + } } impl Allocation { @@ -287,13 +332,31 @@ impl Pointer { } impl Repr { + // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? + pub fn isize() -> Self { + match mem::size_of::() { + 4 => Repr::I32, + 8 => Repr::I64, + _ => unimplemented!(), + } + } + + // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? + pub fn usize() -> Self { + match mem::size_of::() { + 4 => Repr::U32, + 8 => Repr::U64, + _ => unimplemented!(), + } + } + pub fn size(&self) -> usize { match *self { Repr::Bool => 1, - Repr::I8 => 1, - Repr::I16 => 2, - Repr::I32 => 4, - Repr::I64 => 8, + Repr::I8 | Repr::U8 => 1, + Repr::I16 | Repr::U16 => 2, + Repr::I32 | Repr::U32 => 4, + Repr::I64 | Repr::U64 => 8, Repr::Product { size, .. } => size, Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, Repr::Pointer { .. } => POINTER_SIZE, diff --git a/src/primval.rs b/src/primval.rs index 63d5598ece07d..88bd88b17461a 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -5,31 +5,29 @@ use memory::Repr; #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimVal { Bool(bool), - I8(i8), - I16(i16), - I32(i32), - I64(i64), + I8(i8), I16(i16), I32(i32), I64(i64), + U8(u8), U16(u16), U32(u32), U64(u64), } impl PrimVal { - pub fn from_int(n: i64, repr: &Repr) -> Self { + pub fn from_usize(n: usize, repr: &Repr) -> Self { // TODO(tsion): Use checked casts. match *repr { - Repr::I8 => PrimVal::I8(n as i8), - Repr::I16 => PrimVal::I16(n as i16), - Repr::I32 => PrimVal::I32(n as i32), - Repr::I64 => PrimVal::I64(n), - _ => panic!("attempted to make integer primval from non-integer repr"), + Repr::U8 => PrimVal::U8(n as u8), + Repr::U16 => PrimVal::U16(n as u16), + Repr::U32 => PrimVal::U32(n as u32), + Repr::U64 => PrimVal::U64(n as u64), + _ => panic!("attempted to make usize primval from non-uint repr"), } } - pub fn to_int(self) -> i64 { + pub fn to_usize(self) -> usize { match self { - PrimVal::I8(n) => n as i64, - PrimVal::I16(n) => n as i64, - PrimVal::I32(n) => n as i64, - PrimVal::I64(n) => n, - _ => panic!("attempted to make integer from non-integer primval"), + PrimVal::U8(n) => n as usize, + PrimVal::U16(n) => n as usize, + PrimVal::U32(n) => n as usize, + PrimVal::U64(n) => n as usize, + _ => panic!("attempted to make usize from non-uint primval"), } } } @@ -65,10 +63,14 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { use self::PrimVal::*; match (left, right) { - (I8(l), I8(r)) => int_binops!(I8, l, r), + (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), (I32(l), I32(r)) => int_binops!(I32, l, r), (I64(l), I64(r)) => int_binops!(I64, l, r), + (U8(l), U8(r)) => int_binops!(U8, l, r), + (U16(l), U16(r)) => int_binops!(U16, l, r), + (U32(l), U32(r)) => int_binops!(U32, l, r), + (U64(l), U64(r)) => int_binops!(U64, l, r), _ => unimplemented!(), } } @@ -78,14 +80,18 @@ pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal { use self::PrimVal::*; match (un_op, val) { (Not, Bool(b)) => Bool(!b), - (Not, I8(n)) => I8(!n), - (Neg, I8(n)) => I8(-n), + (Not, I8(n)) => I8(!n), + (Neg, I8(n)) => I8(-n), (Not, I16(n)) => I16(!n), (Neg, I16(n)) => I16(-n), (Not, I32(n)) => I32(!n), (Neg, I32(n)) => I32(-n), (Not, I64(n)) => I64(!n), (Neg, I64(n)) => I64(-n), + (Not, U8(n)) => U8(!n), + (Not, U16(n)) => U16(!n), + (Not, U32(n)) => U32(!n), + (Not, U64(n)) => U64(!n), _ => unimplemented!(), } } From 7eddb4e92a71e3842cf8f5ca5edacd02e22bd1b1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 14 Mar 2016 23:25:13 -0600 Subject: [PATCH 0105/1096] Test the unstable box syntax. --- test/heap.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/heap.rs b/test/heap.rs index c97633c656981..05efc56f0f33e 100755 --- a/test/heap.rs +++ b/test/heap.rs @@ -1,7 +1,12 @@ -#![feature(custom_attribute)] +#![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] #[miri_run] -fn make_box() -> Box { - Box::new(42) +fn make_box() -> Box<(i16, i16)> { + Box::new((1, 2)) +} + +#[miri_run] +fn make_box_syntax() -> Box<(i16, i16)> { + box (1, 2) } From 40462d64ef367b22a27ff67e1cf170798d7e29a3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 15 Mar 2016 00:45:25 -0600 Subject: [PATCH 0106/1096] Implement the size_of intrinsic. --- src/interpreter.rs | 54 +++++++++++++++++++++++++++++++++++----------- test/calls.rs | 6 ++++++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 17ab9a52bbac7..21be40f696a17 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,6 @@ use rustc::middle::const_eval; use rustc::middle::def_id::DefId; -use rustc::middle::subst::{Subst, Substs}; +use rustc::middle::subst::{self, Subst, Substs}; use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; @@ -223,13 +223,38 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); match func_ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let mir = self.load_mir(def_id); - let substs = self.tcx.mk_substs( - substs.subst(self.tcx, self.current_substs())); - self.substs_stack.push(substs); - try!(self.push_stack_frame(mir, args, return_ptr)); - TerminatorTarget::Call + ty::TyFnDef(def_id, substs, bare_fn_ty) => { + use syntax::abi::Abi; + match bare_fn_ty.abi { + Abi::RustIntrinsic => match &self.tcx.item_name(def_id).as_str()[..] { + "size_of" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let (dest, dest_repr) = + try!(self.eval_lvalue(&mir::Lvalue::ReturnPointer)); + let size = PrimVal::from_usize(self.ty_to_repr(ty).size(), + &dest_repr); + try!(self.memory.write_primval(dest, size)); + + // Since we pushed no stack frame, the main loop will act as if + // the call just completed and it's returning to the current + // frame. + TerminatorTarget::Call + }, + + name => panic!("can't handle intrinsic named {}", name), + }, + + Abi::Rust => { + let mir = self.load_mir(def_id); + let substs = self.tcx.mk_substs( + substs.subst(self.tcx, self.current_substs())); + self.substs_stack.push(substs); + try!(self.push_stack_frame(mir, args, return_ptr)); + TerminatorTarget::Call + } + + abi => panic!("can't handle function with ABI {:?}", abi), + } } _ => panic!("can't handle callee of type {:?}", func_ty), @@ -404,14 +429,19 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { try!(self.memory.write_i64(ptr, n)); Ok(ptr) } - Uint(_u) => unimplemented!(), - Str(ref _s) => unimplemented!(), - ByteStr(ref _bs) => unimplemented!(), + Uint(n) => { + // TODO(tsion): Check int constant type. + let ptr = self.memory.allocate(8); + try!(self.memory.write_u64(ptr, n)); + Ok(ptr) + } + Str(ref _s) => unimplemented!(), + ByteStr(ref _bs) => unimplemented!(), Bool(b) => { let ptr = self.memory.allocate(Repr::Bool.size()); try!(self.memory.write_bool(ptr, b)); Ok(ptr) - }, + } Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), diff --git a/test/calls.rs b/test/calls.rs index 56f5237113dbf..2cda1c3bddf1b 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -32,3 +32,9 @@ fn call_generic() -> (i16, bool) { fn cross_crate_fn_call() -> i64 { if 1i32.is_positive() { 1 } else { 0 } } + +// Test one of the simplest intrinsics. +#[miri_run] +fn test_size_of() -> usize { + ::std::mem::size_of::>() +} From 8f84d3abc690ab905501a87a3d693b0f5094fb4b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 15 Mar 2016 05:50:53 -0600 Subject: [PATCH 0107/1096] Implement fixed-sized arrays. --- src/interpreter.rs | 26 ++++++++++++++++++++++---- src/memory.rs | 12 +++++++----- test/arrays.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 9 deletions(-) create mode 100755 test/arrays.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 21be40f696a17..1abc50ce55bc1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -336,7 +336,20 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } }, - Vec => unimplemented!(), + Vec => match dest_repr { + Repr::Array { ref elem, length } => { + assert_eq!(length, operands.len()); + let elem_size = elem.size(); + for (i, operand) in operands.iter().enumerate() { + let (src, _) = try!(self.eval_operand(operand)); + let offset = i * elem_size; + try!(self.memory.copy(src, dest.offset(offset), elem_size)); + } + Ok(()) + } + _ => panic!("expected Repr::Array target"), + }, + Closure(..) => unimplemented!(), } } @@ -517,9 +530,14 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.make_variant_repr(&adt_def.variants[0], substs) } - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - Repr::Pointer { target: Box::new(self.ty_to_repr(ty)) } - } + ty::TyArray(ref elem_ty, length) => Repr::Array { + elem: Box::new(self.ty_to_repr(elem_ty)), + length: length, + }, + + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => Repr::Pointer { + target: Box::new(self.ty_to_repr(ty)) + }, ref t => panic!("can't convert type to repr: {:?}", t), } diff --git a/src/memory.rs b/src/memory.rs index 5ab8872e85e38..379c95ec05014 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -58,11 +58,12 @@ pub enum Repr { discr: Box, }, - // Array { - // /// Number of elements. - // length: usize, - // elem: Repr, - // }, + Array { + elem: Box, + + /// Number of elements. + length: usize, + }, Pointer { target: Box, @@ -359,6 +360,7 @@ impl Repr { Repr::I64 | Repr::U64 => 8, Repr::Product { size, .. } => size, Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, + Repr::Array { ref elem, length } => elem.size() * length, Repr::Pointer { .. } => POINTER_SIZE, } } diff --git a/test/arrays.rs b/test/arrays.rs new file mode 100755 index 0000000000000..ed9e501600d19 --- /dev/null +++ b/test/arrays.rs @@ -0,0 +1,27 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn empty_array() -> [u16; 0] { + [] +} + +#[miri_run] +fn singular_array() -> [u16; 1] { + [42] +} + +#[miri_run] +fn deuce_array() -> [u16; 2] { + [42, 53] +} + +#[miri_run] +fn big_array() -> [u16; 5] { + [5, 4, 3, 2, 1] +} + +#[miri_run] +fn array_array() -> [[u8; 2]; 3] { + [[5, 4], [3, 2], [1, 0]] +} From c18e7a68fbfc28f7e4bbd0fbb715e216e17273dc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 15 Mar 2016 07:13:31 -0600 Subject: [PATCH 0108/1096] Split Repr creation out of eval_lvalue. --- src/interpreter.rs | 43 +++++++++++++++++++++++++------------------ src/memory.rs | 1 + 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1abc50ce55bc1..e725f15df83b9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -185,7 +185,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let (discr_ptr, discr_repr) = try!(self.eval_lvalue(discr)); + let discr_ptr = try!(self.eval_lvalue(discr)); + let discr_repr = self.lvalue_repr(discr); let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); // Branch to the `otherwise` case by default, if no match is found. @@ -204,7 +205,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } Switch { ref discr, ref targets, .. } => { - let (adt_ptr, adt_repr) = try!(self.eval_lvalue(discr)); + let adt_ptr = try!(self.eval_lvalue(discr)); + let adt_repr = self.lvalue_repr(discr); let discr_repr = match adt_repr { Repr::Sum { ref discr, .. } => discr, _ => panic!("attmpted to switch on non-sum type"), @@ -217,7 +219,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { self.current_frame_mut().next_block = target; - return_ptr = Some(try!(self.eval_lvalue(lv)).0) + return_ptr = Some(try!(self.eval_lvalue(lv))); } let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); @@ -229,8 +231,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Abi::RustIntrinsic => match &self.tcx.item_name(def_id).as_str()[..] { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let (dest, dest_repr) = - try!(self.eval_lvalue(&mir::Lvalue::ReturnPointer)); + let ret_ptr = &mir::Lvalue::ReturnPointer; + let dest = try!(self.eval_lvalue(ret_ptr)); + let dest_repr = self.lvalue_repr(ret_ptr); let size = PrimVal::from_usize(self.ty_to_repr(ty).size(), &dest_repr); try!(self.memory.write_primval(dest, size)); @@ -289,7 +292,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { - let (dest, dest_repr) = try!(self.eval_lvalue(lvalue)); + let dest = try!(self.eval_lvalue(lvalue)); + let dest_repr = self.lvalue_repr(lvalue); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -355,7 +359,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } Ref(_, _, ref lvalue) => { - let (ptr, _) = try!(self.eval_lvalue(lvalue)); + let ptr = try!(self.eval_lvalue(lvalue)); self.memory.write_ptr(dest, ptr) } @@ -372,7 +376,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<(Pointer, Repr)> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.eval_lvalue(lvalue), + Consume(ref lvalue) => Ok((try!(self.eval_lvalue(lvalue)), self.lvalue_repr(lvalue))), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; @@ -387,7 +391,16 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn eval_lvalue(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<(Pointer, Repr)> { + fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> Repr { + use rustc::mir::tcx::LvalueTy; + match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { + LvalueTy::Ty { ty } => self.ty_to_repr(ty), + LvalueTy::Downcast { ref adt_def, substs, variant_index } => + self.make_variant_repr(&adt_def.variants[variant_index], substs), + } + } + + fn eval_lvalue(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { let frame = self.current_frame(); use rustc::mir::repr::Lvalue::*; @@ -399,7 +412,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Temp(i) => frame.locals[frame.temp_offset + i as usize], Projection(ref proj) => { - let (base_ptr, base_repr) = try!(self.eval_lvalue(&proj.base)); + let base_ptr = try!(self.eval_lvalue(&proj.base)); + let base_repr = self.lvalue_repr(&proj.base); use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, _) => match base_repr { @@ -422,14 +436,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ref l => panic!("can't handle lvalue: {:?}", l), }; - use rustc::mir::tcx::LvalueTy; - let repr = match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { - LvalueTy::Ty { ty } => self.ty_to_repr(ty), - LvalueTy::Downcast { ref adt_def, substs, variant_index } => - self.make_variant_repr(&adt_def.variants[variant_index], substs), - }; - - Ok((ptr, repr)) + Ok(ptr) } fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { diff --git a/src/memory.rs b/src/memory.rs index 379c95ec05014..5ab3fa79ad7f6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,6 +7,7 @@ use std::ptr; use error::{EvalError, EvalResult}; use primval::PrimVal; +// TODO(tsion): How should this get set? Host or target pointer size? const POINTER_SIZE: usize = 8; pub struct Memory { From 3ab619c5c301e4357e92a819461705b8ffcb1225 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 15 Mar 2016 16:09:08 -0600 Subject: [PATCH 0109/1096] Update for changes in rustc master. --- src/interpreter.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e725f15df83b9..7877fbaa4842b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -443,16 +443,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { use rustc::middle::const_eval::ConstVal::*; match *const_val { Float(_f) => unimplemented!(), - Int(n) => { + Integral(int) => { // TODO(tsion): Check int constant type. let ptr = self.memory.allocate(8); - try!(self.memory.write_i64(ptr, n)); - Ok(ptr) - } - Uint(n) => { - // TODO(tsion): Check int constant type. - let ptr = self.memory.allocate(8); - try!(self.memory.write_u64(ptr, n)); + try!(self.memory.write_u64(ptr, int.to_u64_unchecked())); Ok(ptr) } Str(ref _s) => unimplemented!(), @@ -462,11 +456,13 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { try!(self.memory.write_bool(ptr, b)); Ok(ptr) } + Char(_c) => unimplemented!(), Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), Array(_, _) => unimplemented!(), Repeat(_, _) => unimplemented!(), + Dummy => unimplemented!(), } } From 59cf49baf4c4c3a0e558fa720056f27ceb0e067f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 16 Mar 2016 23:28:09 -0600 Subject: [PATCH 0110/1096] Create a pointer-only version of eval_operand. Fix pointer offset. --- src/interpreter.rs | 38 ++++++++++++++++++++++---------------- src/memory.rs | 4 ++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 7877fbaa4842b..1dadc96c4f56c 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -142,7 +142,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { let repr = self.ty_to_repr(arg_decl.ty); let dest = self.memory.allocate(repr.size()); - let (src, _) = try!(self.eval_operand(arg_operand)); + let src = try!(self.eval_operand(arg_operand)); try!(self.memory.copy(src, dest, repr.size())); locals.push(dest); } @@ -171,7 +171,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // TODO(tsion): Deallocate local variables. } - fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult { + fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) + -> EvalResult { use rustc::mir::repr::Terminator::*; let target = match *terminator { Return => TerminatorTarget::Return, @@ -179,7 +180,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Goto { target } => TerminatorTarget::Block(target), If { ref cond, targets: (then_target, else_target) } => { - let (cond_ptr, _) = try!(self.eval_operand(cond)); + let cond_ptr = try!(self.eval_operand(cond)); let cond_val = try!(self.memory.read_bool(cond_ptr)); TerminatorTarget::Block(if cond_val { then_target } else { else_target }) } @@ -280,8 +281,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *dest_repr { Repr::Product { ref fields, .. } => { for (field, operand) in fields.iter().zip(operands) { - let (src, _) = try!(self.eval_operand(operand)); - try!(self.memory.copy(src, dest.offset(field.offset), field.repr.size())); + let src = try!(self.eval_operand(operand)); + let field_dest = dest.offset(field.offset as isize); + try!(self.memory.copy(src, field_dest, field.repr.size())); } } _ => panic!("expected Repr::Product target"), @@ -298,20 +300,20 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { - let (src, _) = try!(self.eval_operand(operand)); + let src = try!(self.eval_operand(operand)); self.memory.copy(src, dest, dest_repr.size()) } BinaryOp(bin_op, ref left, ref right) => { - let (left_ptr, left_repr) = try!(self.eval_operand(left)); - let (right_ptr, right_repr) = try!(self.eval_operand(right)); + let (left_ptr, left_repr) = try!(self.eval_operand_and_repr(left)); + let (right_ptr, right_repr) = try!(self.eval_operand_and_repr(right)); let left_val = try!(self.memory.read_primval(left_ptr, &left_repr)); let right_val = try!(self.memory.read_primval(right_ptr, &right_repr)); self.memory.write_primval(dest, primval::binary_op(bin_op, left_val, right_val)) } UnaryOp(un_op, ref operand) => { - let (ptr, repr) = try!(self.eval_operand(operand)); + let (ptr, repr) = try!(self.eval_operand_and_repr(operand)); let val = try!(self.memory.read_primval(ptr, &repr)); self.memory.write_primval(dest, primval::unary_op(un_op, val)) } @@ -331,7 +333,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { try!(self.memory.write_primval(dest, discr_val)); } self.assign_to_product( - dest.offset(discr.size()), + dest.offset(discr.size() as isize), &variants[variant_idx], operands ) @@ -345,9 +347,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { assert_eq!(length, operands.len()); let elem_size = elem.size(); for (i, operand) in operands.iter().enumerate() { - let (src, _) = try!(self.eval_operand(operand)); + let src = try!(self.eval_operand(operand)); let offset = i * elem_size; - try!(self.memory.copy(src, dest.offset(offset), elem_size)); + let elem_dest = dest.offset(offset as isize); + try!(self.memory.copy(src, elem_dest, elem_size)); } Ok(()) } @@ -373,11 +376,14 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<(Pointer, Repr)> { + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { + self.eval_operand_and_repr(op).map(|(p, _)| p) + } + + fn eval_operand_and_repr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<(Pointer, Repr)> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => Ok((try!(self.eval_lvalue(lvalue)), self.lvalue_repr(lvalue))), - Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { @@ -418,12 +424,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match proj.elem { Field(field, _) => match base_repr { Repr::Product { ref fields, .. } => - base_ptr.offset(fields[field.index()].offset), + base_ptr.offset(fields[field.index()].offset as isize), _ => panic!("field access on non-product type: {:?}", base_repr), }, Downcast(..) => match base_repr { - Repr::Sum { ref discr, .. } => base_ptr.offset(discr.size()), + Repr::Sum { ref discr, .. } => base_ptr.offset(discr.size() as isize), _ => panic!("variant downcast on non-sum type"), }, diff --git a/src/memory.rs b/src/memory.rs index 5ab3fa79ad7f6..6020ec12330d8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -328,8 +328,8 @@ impl Allocation { } impl Pointer { - pub fn offset(self, i: usize) -> Self { - Pointer { offset: self.offset + i, ..self } + pub fn offset(self, i: isize) -> Self { + Pointer { offset: (self.offset as isize + i) as usize, ..self } } } From d3b47c418f75fbc4316074d6baa16a8194a3ddc8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 16 Mar 2016 23:28:49 -0600 Subject: [PATCH 0111/1096] WIP: Support array indexing including a `get_unchecked` test. Required supporting: * Trait method lookup * The `offset` intrinsic * Fat pointers * Unsizing coercions and some raw pointer and integer casts --- src/interpreter.rs | 217 +++++++++++++++++++++++++++++++++++++++------ src/memory.rs | 10 +-- test/arrays.rs | 16 ++-- test/heap.rs | 5 ++ 4 files changed, 208 insertions(+), 40 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1dadc96c4f56c..de49d6e1564a4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,7 @@ use rustc::middle::const_eval; use rustc::middle::def_id::DefId; use rustc::middle::subst::{self, Subst, Substs}; +use rustc::middle::traits; use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; @@ -8,6 +9,7 @@ use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; +use syntax::codemap::DUMMY_SP; use error::EvalResult; use memory::{self, FieldRepr, Memory, Pointer, Repr}; @@ -226,32 +228,57 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); match func_ty.sty { - ty::TyFnDef(def_id, substs, bare_fn_ty) => { + ty::TyFnDef(def_id, substs, fn_ty) => { + let substs = self.tcx.mk_substs( + substs.subst(self.tcx, self.current_substs())); + use syntax::abi::Abi; - match bare_fn_ty.abi { - Abi::RustIntrinsic => match &self.tcx.item_name(def_id).as_str()[..] { - "size_of" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let ret_ptr = &mir::Lvalue::ReturnPointer; - let dest = try!(self.eval_lvalue(ret_ptr)); - let dest_repr = self.lvalue_repr(ret_ptr); - let size = PrimVal::from_usize(self.ty_to_repr(ty).size(), - &dest_repr); - try!(self.memory.write_primval(dest, size)); - - // Since we pushed no stack frame, the main loop will act as if - // the call just completed and it's returning to the current - // frame. - TerminatorTarget::Call - }, - - name => panic!("can't handle intrinsic named {}", name), - }, + match fn_ty.abi { + Abi::RustIntrinsic => { + let ret_ptr = &mir::Lvalue::ReturnPointer; + let dest = try!(self.eval_lvalue(ret_ptr)); + let dest_repr = self.lvalue_repr(ret_ptr); + + match &self.tcx.item_name(def_id).as_str()[..] { + "size_of" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = PrimVal::from_usize( + self.ty_to_repr(ty).size(), + &dest_repr + ); + try!(self.memory.write_primval(dest, size)); + } + + "offset" => { + let pointee_ty = *substs.types.get(subst::FnSpace, 0); + let pointee_size = self.ty_to_repr(pointee_ty).size() as isize; + let ptr_arg = try!(self.eval_operand(&args[0])); + let offset_arg = try!(self.eval_operand(&args[1])); + let ptr = try!(self.memory.read_ptr(ptr_arg)); + // TODO(tsion): read_isize + let offset = try!(self.memory.read_i64(offset_arg)); + let result_ptr = ptr.offset(offset as isize * pointee_size); + try!(self.memory.write_ptr(dest, result_ptr)); + } + + name => panic!("can't handle intrinsic named {}", name), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + TerminatorTarget::Call + } Abi::Rust => { + // Only trait methods can have a Self parameter. + let (def_id, substs) = if substs.self_ty().is_some() { + self.trait_method(def_id, substs) + } else { + (def_id, substs) + }; + let mir = self.load_mir(def_id); - let substs = self.tcx.mk_substs( - substs.subst(self.tcx, self.current_substs())); self.substs_stack.push(substs); try!(self.push_stack_frame(mir, args, return_ptr)); TerminatorTarget::Call @@ -372,6 +399,51 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.memory.write_ptr(dest, ptr) } + Cast(kind, ref operand, dest_ty) => { + fn pointee_type<'tcx>(ptr_ty: ty::Ty<'tcx>) -> Option> { + match ptr_ty.sty { + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyBox(ty) => { + Some(ty) + } + + _ => None, + } + } + + let src = try!(self.eval_operand(operand)); + let src_ty = self.current_frame().mir.operand_ty(self.tcx, operand); + + use rustc::mir::repr::CastKind::*; + match kind { + Unsize => { + try!(self.memory.copy(src, dest, 8)); + let src_pointee_ty = pointee_type(src_ty).unwrap(); + let dest_pointee_ty = pointee_type(dest_ty).unwrap(); + + match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { + (&ty::TyArray(_, length), &ty::TySlice(_)) => + // TODO(tsion): Add write_usize? (Host/target issues.) + self.memory.write_u64(dest.offset(8), length as u64), + + _ => panic!("can't handle cast: {:?}", rvalue), + } + } + + Misc => { + if pointee_type(src_ty).is_some() && pointee_type(dest_ty).is_some() { + self.memory.copy(src, dest, 8) + } else { + self.memory.copy(src, dest, 8) + // panic!("can't handle cast: {:?}", rvalue); + } + } + + _ => panic!("can't handle cast: {:?}", rvalue), + } + } + ref r => panic!("can't handle rvalue: {:?}", r), } } @@ -544,9 +616,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { length: length, }, - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => Repr::Pointer { - target: Box::new(self.ty_to_repr(ty)) - }, + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyBox(ty) => { + if ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) { + Repr::Pointer + } else { + Repr::FatPointer + } + } ref t => panic!("can't convert type to repr: {:?}", t), } @@ -582,6 +660,95 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } } + + fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + use rustc::middle::infer; + use syntax::ast; + + // Do the initial selection for the obligation. This yields the shallow result we are + // looking for -- that is, what specific impl. + let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables); + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // Currently, we use a fulfillment context to completely resolve all nested obligations. + // This is because they can inform the inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infer::drain_fulfillment_cx_or_panic( + DUMMY_SP, &infcx, &mut fulfill_cx, &vtable + ); + + vtable + } + + /// Trait method, which has to be resolved to an impl method. + pub fn trait_method(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) + -> (DefId, &'tcx Substs<'tcx>) { + let method_item = self.tcx.impl_or_trait_item(def_id); + let trait_id = method_item.container().id(); + let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); + match self.fulfill_obligation(trait_ref) { + traits::VtableImpl(vtable_impl) => { + let impl_did = vtable_impl.impl_def_id; + let mname = self.tcx.item_name(def_id); + // Create a concatenated set of substitutions which includes those from the + // impl and those from the method: + let impl_substs = vtable_impl.substs.with_method_from(&substs); + let substs = self.tcx.mk_substs(impl_substs); + let mth = self.tcx.get_impl_method(impl_did, substs, mname); + + println!("{:?} {:?}", mth.method.def_id, mth.substs); + (mth.method.def_id, mth.substs) + } + traits::VtableClosure(vtable_closure) => { + // The substitutions should have no type parameters remaining after passing + // through fulfill_obligation + let trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + unimplemented!() + // vtable_closure.closure_def_id + // vtable_closure.substs + // trait_closure_kind + + // let method_ty = def_ty(tcx, def_id, substs); + // let fn_ptr_ty = match method_ty.sty { + // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + // _ => unreachable!("expected fn item type, found {}", + // method_ty) + // }; + // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + } + traits::VtableFnPointer(fn_ty) => { + let trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + unimplemented!() + // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); + + // let method_ty = def_ty(tcx, def_id, substs); + // let fn_ptr_ty = match method_ty.sty { + // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + // _ => unreachable!("expected fn item type, found {}", + // method_ty) + // }; + // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + } + traits::VtableObject(ref data) => { + unimplemented!() + // Callee { + // data: Virtual(traits::get_vtable_index_of_object_method( + // tcx, data, def_id)), + // ty: def_ty(tcx, def_id, substs) + // } + } + vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), + } + } } impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { diff --git a/src/memory.rs b/src/memory.rs index 6020ec12330d8..a7da9e19dfd9b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -43,6 +43,9 @@ pub enum Repr { I8, I16, I32, I64, U8, U16, U32, U64, + Pointer, + FatPointer, + /// The representation for product types including tuples, structs, and the contents of enum /// variants. Product { @@ -65,10 +68,6 @@ pub enum Repr { /// Number of elements. length: usize, }, - - Pointer { - target: Box, - } } impl Memory { @@ -362,7 +361,8 @@ impl Repr { Repr::Product { size, .. } => size, Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, Repr::Array { ref elem, length } => elem.size() * length, - Repr::Pointer { .. } => POINTER_SIZE, + Repr::Pointer => POINTER_SIZE, + Repr::FatPointer => POINTER_SIZE * 2, } } } diff --git a/test/arrays.rs b/test/arrays.rs index ed9e501600d19..1add02561e38e 100755 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -6,16 +6,6 @@ fn empty_array() -> [u16; 0] { [] } -#[miri_run] -fn singular_array() -> [u16; 1] { - [42] -} - -#[miri_run] -fn deuce_array() -> [u16; 2] { - [42, 53] -} - #[miri_run] fn big_array() -> [u16; 5] { [5, 4, 3, 2, 1] @@ -25,3 +15,9 @@ fn big_array() -> [u16; 5] { fn array_array() -> [[u8; 2]; 3] { [[5, 4], [3, 2], [1, 0]] } + +#[miri_run] +fn indexing() -> i32 { + let a = [0, 10, 20, 30]; + unsafe { *a.get_unchecked(2) } +} diff --git a/test/heap.rs b/test/heap.rs index 05efc56f0f33e..c40165909df02 100755 --- a/test/heap.rs +++ b/test/heap.rs @@ -10,3 +10,8 @@ fn make_box() -> Box<(i16, i16)> { fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } + +// #[miri_run] +// fn make_vec() -> Vec { +// Vec::new() +// } From aa791a4085dce76a6b9100e12a894a47745c3b2a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 01:11:06 -0600 Subject: [PATCH 0112/1096] Add fixmes to horrible casting code. --- src/interpreter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index de49d6e1564a4..1ce055b494ac9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -433,8 +433,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Misc => { if pointee_type(src_ty).is_some() && pointee_type(dest_ty).is_some() { + // FIXME(tsion): Wrong for fat pointers. self.memory.copy(src, dest, 8) } else { + // FIXME(tsion): Wrong for almost everything. self.memory.copy(src, dest, 8) // panic!("can't handle cast: {:?}", rvalue); } From a1fc284559aaee127c93a2d6179dcda4e034d229 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 02:53:03 -0600 Subject: [PATCH 0113/1096] Simplify some Repr code. --- src/interpreter.rs | 85 ++++++++++++++++++++++++---------------------- src/memory.rs | 44 ++++++++++++++++-------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1ce055b494ac9..2988bc38811cc 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -142,18 +142,18 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let mut locals = Vec::with_capacity(num_args + num_vars + num_temps); for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { - let repr = self.ty_to_repr(arg_decl.ty); - let dest = self.memory.allocate(repr.size()); + let size = self.ty_to_repr(arg_decl.ty).size(); + let dest = self.memory.allocate(size); let src = try!(self.eval_operand(arg_operand)); - try!(self.memory.copy(src, dest, repr.size())); + try!(self.memory.copy(src, dest, size)); locals.push(dest); } let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); locals.extend(var_tys.chain(temp_tys).map(|ty| { - let repr = self.ty_to_repr(ty).size(); - self.memory.allocate(repr) + let size = self.ty_to_repr(ty).size(); + self.memory.allocate(size) })); self.stack.push(Frame { @@ -189,15 +189,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = try!(self.eval_lvalue(discr)); - let discr_repr = self.lvalue_repr(discr); - let discr_val = try!(self.memory.read_primval(discr_ptr, &discr_repr)); + let discr_size = self.lvalue_repr(discr).size(); + let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; for (index, val_const) in values.iter().enumerate() { let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_primval(ptr, &discr_repr)); + let val = try!(self.memory.read_uint(ptr, discr_size)); if discr_val == val { target_block = targets[index]; break; @@ -210,12 +210,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Switch { ref discr, ref targets, .. } => { let adt_ptr = try!(self.eval_lvalue(discr)); let adt_repr = self.lvalue_repr(discr); - let discr_repr = match adt_repr { - Repr::Sum { ref discr, .. } => discr, + let discr_size = match adt_repr { + Repr::Sum { discr_size, .. } => discr_size, _ => panic!("attmpted to switch on non-sum type"), }; - let discr_val = try!(self.memory.read_primval(adt_ptr, &discr_repr)); - TerminatorTarget::Block(targets[discr_val.to_usize()]) + let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size)); + TerminatorTarget::Block(targets[discr_val as usize]) } Call { ref func, ref args, ref destination, .. } => { @@ -332,16 +332,21 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } BinaryOp(bin_op, ref left, ref right) => { - let (left_ptr, left_repr) = try!(self.eval_operand_and_repr(left)); - let (right_ptr, right_repr) = try!(self.eval_operand_and_repr(right)); - let left_val = try!(self.memory.read_primval(left_ptr, &left_repr)); - let right_val = try!(self.memory.read_primval(right_ptr, &right_repr)); + let left_ptr = try!(self.eval_operand(left)); + let left_ty = self.operand_ty(left); + let left_val = try!(self.memory.read_primval(left_ptr, left_ty)); + + let right_ptr = try!(self.eval_operand(right)); + let right_ty = self.operand_ty(right); + let right_val = try!(self.memory.read_primval(right_ptr, right_ty)); + self.memory.write_primval(dest, primval::binary_op(bin_op, left_val, right_val)) } UnaryOp(un_op, ref operand) => { - let (ptr, repr) = try!(self.eval_operand_and_repr(operand)); - let val = try!(self.memory.read_primval(ptr, &repr)); + let ptr = try!(self.eval_operand(operand)); + let ty = self.operand_ty(operand); + let val = try!(self.memory.read_primval(ptr, ty)); self.memory.write_primval(dest, primval::unary_op(un_op, val)) } @@ -354,13 +359,13 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::AdtKind::Struct => self.assign_to_product(dest, &dest_repr, operands), ty::AdtKind::Enum => match dest_repr { - Repr::Sum { ref discr, ref variants, .. } => { - if discr.size() > 0 { - let discr_val = PrimVal::from_usize(variant_idx, discr); - try!(self.memory.write_primval(dest, discr_val)); + Repr::Sum { discr_size, ref variants, .. } => { + if discr_size > 0 { + let discr = variant_idx as u64; + try!(self.memory.write_uint(dest, discr, discr_size)); } self.assign_to_product( - dest.offset(discr.size() as isize), + dest.offset(discr_size as isize), &variants[variant_idx], operands ) @@ -450,6 +455,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { + self.current_frame().mir.operand_ty(self.tcx, operand) + } + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { self.eval_operand_and_repr(op).map(|(p, _)| p) } @@ -503,7 +512,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }, Downcast(..) => match base_repr { - Repr::Sum { ref discr, .. } => base_ptr.offset(discr.size() as isize), + Repr::Sum { discr_size, .. } => base_ptr.offset(discr_size as isize), _ => panic!("variant downcast on non-sum type"), }, @@ -585,16 +594,12 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::TyEnum(adt_def, substs) => { let num_variants = adt_def.variants.len(); - let discr = if num_variants <= 1 { - Repr::Product { size: 0, fields: vec![] } - } else if num_variants <= 1 << 8 { - Repr::U8 - } else if num_variants <= 1 << 16 { - Repr::U16 - } else if num_variants <= 1 << 32 { - Repr::U32 - } else { - Repr::U64 + let discr_size = match num_variants { + n if n <= 1 => 0, + n if n <= 1 << 8 => 1, + n if n <= 1 << 16 => 2, + n if n <= 1 << 32 => 4, + _ => 8, }; let variants: Vec = adt_def.variants.iter().map(|v| { @@ -602,7 +607,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }).collect(); Repr::Sum { - discr: Box::new(discr), + discr_size: discr_size, max_variant_size: variants.iter().map(Repr::size).max().unwrap_or(0), variants: variants, } @@ -710,10 +715,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { println!("{:?} {:?}", mth.method.def_id, mth.substs); (mth.method.def_id, mth.substs) } - traits::VtableClosure(vtable_closure) => { + traits::VtableClosure(_vtable_closure) => { // The substitutions should have no type parameters remaining after passing // through fulfill_obligation - let trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); unimplemented!() // vtable_closure.closure_def_id // vtable_closure.substs @@ -727,8 +732,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // }; // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } - traits::VtableFnPointer(fn_ty) => { - let trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + traits::VtableFnPointer(_fn_ty) => { + let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); unimplemented!() // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); @@ -740,7 +745,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { // }; // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } - traits::VtableObject(ref data) => { + traits::VtableObject(ref _data) => { unimplemented!() // Callee { // data: Virtual(traits::get_vtable_index_of_object_method( diff --git a/src/memory.rs b/src/memory.rs index a7da9e19dfd9b..a135f267910b8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,5 @@ -use byteorder::{self, ByteOrder}; +use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; +use rustc::middle::ty; use std::collections::{BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; use std::mem; @@ -56,10 +57,14 @@ pub enum Repr { /// The representation for a sum type, i.e. a Rust enum. Sum { + /// The size of the discriminant (an integer). Should be between 0 and 8. + discr_size: usize, + /// The size of the largest variant in bytes. max_variant_size: usize, + + /// The represenations of the contents of each variant. variants: Vec, - discr: Box, }, Array { @@ -167,18 +172,19 @@ impl Memory { Ok(()) } - pub fn read_primval(&self, ptr: Pointer, repr: &Repr) -> EvalResult { - match *repr { - Repr::Bool => self.read_bool(ptr).map(PrimVal::Bool), - Repr::I8 => self.read_i8(ptr).map(PrimVal::I8), - Repr::I16 => self.read_i16(ptr).map(PrimVal::I16), - Repr::I32 => self.read_i32(ptr).map(PrimVal::I32), - Repr::I64 => self.read_i64(ptr).map(PrimVal::I64), - Repr::U8 => self.read_u8(ptr).map(PrimVal::U8), - Repr::U16 => self.read_u16(ptr).map(PrimVal::U16), - Repr::U32 => self.read_u32(ptr).map(PrimVal::U32), - Repr::U64 => self.read_u64(ptr).map(PrimVal::U64), - _ => panic!("primitive read of non-primitive: {:?}", repr), + pub fn read_primval(&self, ptr: Pointer, ty: ty::Ty) -> EvalResult { + use syntax::ast::{IntTy, UintTy}; + match ty.sty { + ty::TyBool => self.read_bool(ptr).map(PrimVal::Bool), + ty::TyInt(IntTy::I8) => self.read_i8(ptr).map(PrimVal::I8), + ty::TyInt(IntTy::I16) => self.read_i16(ptr).map(PrimVal::I16), + ty::TyInt(IntTy::I32) => self.read_i32(ptr).map(PrimVal::I32), + ty::TyInt(IntTy::I64) => self.read_i64(ptr).map(PrimVal::I64), + ty::TyUint(UintTy::U8) => self.read_u8(ptr).map(PrimVal::U8), + ty::TyUint(UintTy::U16) => self.read_u16(ptr).map(PrimVal::U16), + ty::TyUint(UintTy::U32) => self.read_u32(ptr).map(PrimVal::U32), + ty::TyUint(UintTy::U64) => self.read_u64(ptr).map(PrimVal::U64), + _ => panic!("primitive read of non-primitive type: {:?}", ty), } } @@ -286,6 +292,14 @@ impl Memory { byteorder::NativeEndian::write_u64(bytes, n); Ok(()) } + + pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult { + self.get_bytes(ptr, size).map(|mut b| b.read_uint::(size).unwrap()) + } + + pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<()> { + self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::(n, size).unwrap()) + } } impl Allocation { @@ -359,7 +373,7 @@ impl Repr { Repr::I32 | Repr::U32 => 4, Repr::I64 | Repr::U64 => 8, Repr::Product { size, .. } => size, - Repr::Sum { ref discr, max_variant_size, .. } => discr.size() + max_variant_size, + Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, Repr::Array { ref elem, length } => elem.size() * length, Repr::Pointer => POINTER_SIZE, Repr::FatPointer => POINTER_SIZE * 2, From abd235837ac9340b41251da26ef91aaf07628880 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 03:11:40 -0600 Subject: [PATCH 0114/1096] Simplify FieldRepr sizes. --- src/interpreter.rs | 8 ++++---- src/memory.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2988bc38811cc..e9c581a568184 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -310,7 +310,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { for (field, operand) in fields.iter().zip(operands) { let src = try!(self.eval_operand(operand)); let field_dest = dest.offset(field.offset as isize); - try!(self.memory.copy(src, field_dest, field.repr.size())); + try!(self.memory.copy(src, field_dest, field.size)); } } _ => panic!("expected Repr::Product target"), @@ -558,10 +558,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn make_product_repr(&self, iter: I) -> Repr where I: IntoIterator> { let mut size = 0; let fields = iter.into_iter().map(|ty| { - let repr = self.ty_to_repr(ty); + let field_size = self.ty_to_repr(ty).size(); let old_size = size; - size += repr.size(); - FieldRepr { offset: old_size, repr: repr } + size += field_size; + FieldRepr { offset: old_size, size: field_size } }).collect(); Repr::Product { size: size, fields: fields } } diff --git a/src/memory.rs b/src/memory.rs index a135f267910b8..73bdc4ea49add 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -35,7 +35,7 @@ pub struct Pointer { #[derive(Clone, Debug, PartialEq, Eq)] pub struct FieldRepr { pub offset: usize, - pub repr: Repr, + pub size: usize, } #[derive(Clone, Debug, PartialEq, Eq)] From 4704bdca8d1eefe1efe32a40ce3f6798b207129d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 03:12:15 -0600 Subject: [PATCH 0115/1096] Simplify PrimVals. --- src/interpreter.rs | 9 +++------ src/memory.rs | 12 ++++++++++++ src/primval.rs | 25 ------------------------- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e9c581a568184..8741beb8735a9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -13,7 +13,7 @@ use syntax::codemap::DUMMY_SP; use error::EvalResult; use memory::{self, FieldRepr, Memory, Pointer, Repr}; -use primval::{self, PrimVal}; +use primval; const TRACE_EXECUTION: bool = true; @@ -242,11 +242,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match &self.tcx.item_name(def_id).as_str()[..] { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = PrimVal::from_usize( - self.ty_to_repr(ty).size(), - &dest_repr - ); - try!(self.memory.write_primval(dest, size)); + let size = self.ty_to_repr(ty).size() as u64; + try!(self.memory.write_uint(dest, size, dest_repr.size())); } "offset" => { diff --git a/src/memory.rs b/src/memory.rs index 73bdc4ea49add..17e9439de6d8c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -184,6 +184,10 @@ impl Memory { ty::TyUint(UintTy::U16) => self.read_u16(ptr).map(PrimVal::U16), ty::TyUint(UintTy::U32) => self.read_u32(ptr).map(PrimVal::U32), ty::TyUint(UintTy::U64) => self.read_u64(ptr).map(PrimVal::U64), + + // TODO(tsion): Pick the PrimVal dynamically. + ty::TyInt(IntTy::Is) => self.read_int(ptr, POINTER_SIZE).map(PrimVal::I64), + ty::TyUint(UintTy::Us) => self.read_uint(ptr, POINTER_SIZE).map(PrimVal::U64), _ => panic!("primitive read of non-primitive type: {:?}", ty), } } @@ -255,6 +259,14 @@ impl Memory { Ok(()) } + pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult { + self.get_bytes(ptr, size).map(|mut b| b.read_int::(size).unwrap()) + } + + pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<()> { + self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::(n, size).unwrap()) + } + pub fn read_u8(&self, ptr: Pointer) -> EvalResult { self.get_bytes(ptr, 1).map(|b| b[0] as u8) } diff --git a/src/primval.rs b/src/primval.rs index 88bd88b17461a..a34da06d59717 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,7 +1,5 @@ use rustc::mir::repr as mir; -use memory::Repr; - #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimVal { Bool(bool), @@ -9,29 +7,6 @@ pub enum PrimVal { U8(u8), U16(u16), U32(u32), U64(u64), } -impl PrimVal { - pub fn from_usize(n: usize, repr: &Repr) -> Self { - // TODO(tsion): Use checked casts. - match *repr { - Repr::U8 => PrimVal::U8(n as u8), - Repr::U16 => PrimVal::U16(n as u16), - Repr::U32 => PrimVal::U32(n as u32), - Repr::U64 => PrimVal::U64(n as u64), - _ => panic!("attempted to make usize primval from non-uint repr"), - } - } - - pub fn to_usize(self) -> usize { - match self { - PrimVal::U8(n) => n as usize, - PrimVal::U16(n) => n as usize, - PrimVal::U32(n) => n as usize, - PrimVal::U64(n) => n as usize, - _ => panic!("attempted to make usize from non-uint primval"), - } - } -} - pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ From 7698a85d0100d3c6b152169813eacf9385cc50b4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 03:19:13 -0600 Subject: [PATCH 0116/1096] Simplify Repr::Array's elem size. --- src/interpreter.rs | 15 +++++++-------- src/memory.rs | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8741beb8735a9..9c5e505671afc 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -372,9 +372,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }, Vec => match dest_repr { - Repr::Array { ref elem, length } => { + Repr::Array { elem_size, length } => { assert_eq!(length, operands.len()); - let elem_size = elem.size(); for (i, operand) in operands.iter().enumerate() { let src = try!(self.eval_operand(operand)); let offset = i * elem_size; @@ -396,8 +395,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } Box(ty) => { - let repr = self.ty_to_repr(ty); - let ptr = self.memory.allocate(repr.size()); + let size = self.ty_to_repr(ty).size(); + let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr) } @@ -538,7 +537,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Str(ref _s) => unimplemented!(), ByteStr(ref _bs) => unimplemented!(), Bool(b) => { - let ptr = self.memory.allocate(Repr::Bool.size()); + let ptr = self.memory.allocate(1); try!(self.memory.write_bool(ptr, b)); Ok(ptr) } @@ -616,7 +615,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } ty::TyArray(ref elem_ty, length) => Repr::Array { - elem: Box::new(self.ty_to_repr(elem_ty)), + elem_size: self.ty_to_repr(elem_ty).size(), length: length, }, @@ -786,8 +785,8 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { - let repr = miri.ty_to_repr(ty).size(); - Some(miri.memory.allocate(repr)) + let size = miri.ty_to_repr(ty).size(); + Some(miri.memory.allocate(size)) } ty::FnDiverging => None, }; diff --git a/src/memory.rs b/src/memory.rs index 17e9439de6d8c..a42d504381ae7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -68,7 +68,7 @@ pub enum Repr { }, Array { - elem: Box, + elem_size: usize, /// Number of elements. length: usize, @@ -386,7 +386,7 @@ impl Repr { Repr::I64 | Repr::U64 => 8, Repr::Product { size, .. } => size, Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, - Repr::Array { ref elem, length } => elem.size() * length, + Repr::Array { elem_size, length } => elem_size * length, Repr::Pointer => POINTER_SIZE, Repr::FatPointer => POINTER_SIZE * 2, } From c55d4b07fd2b896e303efe4d4ad4401a19ce97aa Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 03:20:49 -0600 Subject: [PATCH 0117/1096] Fix typo. --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index a42d504381ae7..deea5e29deca7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -63,7 +63,7 @@ pub enum Repr { /// The size of the largest variant in bytes. max_variant_size: usize, - /// The represenations of the contents of each variant. + /// The representations of the contents of each variant. variants: Vec, }, From 71ed95246502875331e97a4be98431b032247d83 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 03:36:06 -0600 Subject: [PATCH 0118/1096] Simplify primitive type reprs. --- src/interpreter.rs | 19 +++++++++---------- src/memory.rs | 25 +++++++------------------ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 9c5e505671afc..ca0a96a1767e3 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -571,19 +571,18 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { use syntax::ast::{IntTy, UintTy}; match ty.subst(self.tcx, self.current_substs()).sty { - ty::TyBool => Repr::Bool, - + ty::TyBool => Repr::Primitive { size: 1 }, ty::TyInt(IntTy::Is) => Repr::isize(), - ty::TyInt(IntTy::I8) => Repr::I8, - ty::TyInt(IntTy::I16) => Repr::I16, - ty::TyInt(IntTy::I32) => Repr::I32, - ty::TyInt(IntTy::I64) => Repr::I64, + ty::TyInt(IntTy::I8) => Repr::Primitive { size: 1 }, + ty::TyInt(IntTy::I16) => Repr::Primitive { size: 2 }, + ty::TyInt(IntTy::I32) => Repr::Primitive { size: 4 }, + ty::TyInt(IntTy::I64) => Repr::Primitive { size: 8 }, ty::TyUint(UintTy::Us) => Repr::usize(), - ty::TyUint(UintTy::U8) => Repr::U8, - ty::TyUint(UintTy::U16) => Repr::U16, - ty::TyUint(UintTy::U32) => Repr::U32, - ty::TyUint(UintTy::U64) => Repr::U64, + ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, + ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, + ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, + ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), diff --git a/src/memory.rs b/src/memory.rs index deea5e29deca7..612f8e4938e61 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -40,9 +40,10 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { - Bool, - I8, I16, I32, I64, - U8, U16, U32, U64, + /// Representation for a primitive type such as a boolean, integer, or character. + Primitive { + size: usize + }, Pointer, FatPointer, @@ -361,29 +362,17 @@ impl Pointer { impl Repr { // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? pub fn isize() -> Self { - match mem::size_of::() { - 4 => Repr::I32, - 8 => Repr::I64, - _ => unimplemented!(), - } + Repr::Primitive { size: mem::size_of::() } } // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? pub fn usize() -> Self { - match mem::size_of::() { - 4 => Repr::U32, - 8 => Repr::U64, - _ => unimplemented!(), - } + Repr::Primitive { size: mem::size_of::() } } pub fn size(&self) -> usize { match *self { - Repr::Bool => 1, - Repr::I8 | Repr::U8 => 1, - Repr::I16 | Repr::U16 => 2, - Repr::I32 | Repr::U32 => 4, - Repr::I64 | Repr::U64 => 8, + Repr::Primitive { size } => size, Repr::Product { size, .. } => size, Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, Repr::Array { elem_size, length } => elem_size * length, From 3ebf5063a4c0e790469425cb3cfcbf313f7fb2cc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 04:38:46 -0600 Subject: [PATCH 0119/1096] Merge sum and product representations. --- src/interpreter.rs | 143 +++++++++++++++++++++++---------------------- src/memory.rs | 20 ++----- 2 files changed, 78 insertions(+), 85 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index ca0a96a1767e3..da0eb95708acc 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -7,6 +7,7 @@ use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; +use std::iter; use std::ops::Deref; use std::rc::Rc; use syntax::codemap::DUMMY_SP; @@ -211,8 +212,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let adt_ptr = try!(self.eval_lvalue(discr)); let adt_repr = self.lvalue_repr(discr); let discr_size = match adt_repr { - Repr::Sum { discr_size, .. } => discr_size, - _ => panic!("attmpted to switch on non-sum type"), + Repr::Aggregate { discr_size, .. } => discr_size, + _ => panic!("attmpted to switch on non-aggregate type"), }; let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size)); TerminatorTarget::Block(targets[discr_val as usize]) @@ -300,17 +301,22 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(target) } - fn assign_to_product(&mut self, dest: Pointer, dest_repr: &Repr, + fn assign_to_aggregate(&mut self, dest: Pointer, dest_repr: &Repr, variant: usize, operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { match *dest_repr { - Repr::Product { ref fields, .. } => { - for (field, operand) in fields.iter().zip(operands) { + Repr::Aggregate { discr_size, ref variants, .. } => { + if discr_size > 0 { + let discr = variant as u64; + try!(self.memory.write_uint(dest, discr, discr_size)); + } + let after_discr = dest.offset(discr_size as isize); + for (field, operand) in variants[variant].iter().zip(operands) { let src = try!(self.eval_operand(operand)); - let field_dest = dest.offset(field.offset as isize); + let field_dest = after_discr.offset(field.offset as isize); try!(self.memory.copy(src, field_dest, field.size)); } } - _ => panic!("expected Repr::Product target"), + _ => panic!("expected Repr::Aggregate target"), } Ok(()) } @@ -350,26 +356,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Aggregate(ref kind, ref operands) => { use rustc::mir::repr::AggregateKind::*; match *kind { - Tuple => self.assign_to_product(dest, &dest_repr, operands), + Tuple => self.assign_to_aggregate(dest, &dest_repr, 0, operands), - Adt(ref adt_def, variant_idx, _) => match adt_def.adt_kind() { - ty::AdtKind::Struct => self.assign_to_product(dest, &dest_repr, operands), - - ty::AdtKind::Enum => match dest_repr { - Repr::Sum { discr_size, ref variants, .. } => { - if discr_size > 0 { - let discr = variant_idx as u64; - try!(self.memory.write_uint(dest, discr, discr_size)); - } - self.assign_to_product( - dest.offset(discr_size as isize), - &variants[variant_idx], - operands - ) - } - _ => panic!("expected Repr::Sum target"), - } - }, + Adt(_, variant_idx, _) => + self.assign_to_aggregate(dest, &dest_repr, variant_idx, operands), Vec => match dest_repr { Repr::Array { elem_size, length } => { @@ -480,8 +470,11 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { use rustc::mir::tcx::LvalueTy; match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { LvalueTy::Ty { ty } => self.ty_to_repr(ty), - LvalueTy::Downcast { ref adt_def, substs, variant_index } => - self.make_variant_repr(&adt_def.variants[variant_index], substs), + LvalueTy::Downcast { ref adt_def, substs, variant_index } => { + let field_tys = adt_def.variants[variant_index].fields.iter() + .map(|f| f.ty(self.tcx, substs)); + self.make_aggregate_repr(iter::once(field_tys)) + } } } @@ -502,14 +495,16 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, _) => match base_repr { - Repr::Product { ref fields, .. } => - base_ptr.offset(fields[field.index()].offset as isize), + Repr::Aggregate { discr_size: 0, ref variants, .. } => { + let fields = &variants[0]; + base_ptr.offset(fields[field.index()].offset as isize) + } _ => panic!("field access on non-product type: {:?}", base_repr), }, Downcast(..) => match base_repr { - Repr::Sum { discr_size, .. } => base_ptr.offset(discr_size as isize), - _ => panic!("variant downcast on non-sum type"), + Repr::Aggregate { discr_size, .. } => base_ptr.offset(discr_size as isize), + _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), }, Deref => try!(self.memory.read_ptr(base_ptr)), @@ -551,20 +546,45 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn make_product_repr(&self, iter: I) -> Repr where I: IntoIterator> { - let mut size = 0; - let fields = iter.into_iter().map(|ty| { - let field_size = self.ty_to_repr(ty).size(); - let old_size = size; - size += field_size; - FieldRepr { offset: old_size, size: field_size } - }).collect(); - Repr::Product { size: size, fields: fields } - } + fn make_aggregate_repr(&self, variant_fields: V) -> Repr + where V: IntoIterator, F: IntoIterator> + { + let mut variants = Vec::new(); + let mut max_variant_size = 0; + + for field_tys in variant_fields { + let mut fields = Vec::new(); + let mut size = 0; + + for ty in field_tys { + let field_size = self.ty_to_repr(ty).size(); + let offest = size; + size += field_size; + fields.push(FieldRepr { offset: offest, size: field_size }); + } + + if size > max_variant_size { + max_variant_size = size; + } + variants.push(fields); + } + + let num_variants = variants.len(); + + let discr_size = match num_variants { + n if n <= 1 => 0, + n if n <= 1 << 8 => 1, + n if n <= 1 << 16 => 2, + n if n <= 1 << 32 => 4, + _ => 8, + }; + + Repr::Aggregate { + discr_size: discr_size, + max_variant_size: max_variant_size, + variants: variants, + } - fn make_variant_repr(&self, v: ty::VariantDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Repr { - let field_tys = v.fields.iter().map(|f| f.ty(self.tcx, substs)); - self.make_product_repr(field_tys) } // TODO(tsion): Cache these outputs. @@ -584,33 +604,14 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, - ty::TyTuple(ref fields) => self.make_product_repr(fields.iter().cloned()), - - ty::TyEnum(adt_def, substs) => { - let num_variants = adt_def.variants.len(); - - let discr_size = match num_variants { - n if n <= 1 => 0, - n if n <= 1 << 8 => 1, - n if n <= 1 << 16 => 2, - n if n <= 1 << 32 => 4, - _ => 8, - }; - - let variants: Vec = adt_def.variants.iter().map(|v| { - self.make_variant_repr(v, substs) - }).collect(); - - Repr::Sum { - discr_size: discr_size, - max_variant_size: variants.iter().map(Repr::size).max().unwrap_or(0), - variants: variants, - } - } + ty::TyTuple(ref fields) => + self.make_aggregate_repr(iter::once(fields.iter().cloned())), - ty::TyStruct(adt_def, substs) => { - assert_eq!(adt_def.variants.len(), 1); - self.make_variant_repr(&adt_def.variants[0], substs) + ty::TyEnum(adt_def, substs) | ty::TyStruct(adt_def, substs) => { + let variants = adt_def.variants.iter().map(|v| { + v.fields.iter().map(|f| f.ty(self.tcx, substs)) + }); + self.make_aggregate_repr(variants) } ty::TyArray(ref elem_ty, length) => Repr::Array { diff --git a/src/memory.rs b/src/memory.rs index 612f8e4938e61..a2632007d802d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -48,24 +48,17 @@ pub enum Repr { Pointer, FatPointer, - /// The representation for product types including tuples, structs, and the contents of enum - /// variants. - Product { - /// Size in bytes. - size: usize, - fields: Vec, - }, - - /// The representation for a sum type, i.e. a Rust enum. - Sum { - /// The size of the discriminant (an integer). Should be between 0 and 8. + /// The representation for aggregate types including structs, enums, and tuples. + Aggregate { + /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for + /// structs and tuples. discr_size: usize, /// The size of the largest variant in bytes. max_variant_size: usize, /// The representations of the contents of each variant. - variants: Vec, + variants: Vec>, }, Array { @@ -373,8 +366,7 @@ impl Repr { pub fn size(&self) -> usize { match *self { Repr::Primitive { size } => size, - Repr::Product { size, .. } => size, - Repr::Sum { discr_size, max_variant_size, .. } => discr_size + max_variant_size, + Repr::Aggregate { discr_size, max_variant_size, .. } => discr_size + max_variant_size, Repr::Array { elem_size, length } => elem_size * length, Repr::Pointer => POINTER_SIZE, Repr::FatPointer => POINTER_SIZE * 2, From 573c11cef5744d58b37abdfdb4c175d33e9b6344 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 04:54:04 -0600 Subject: [PATCH 0120/1096] Simplify make_aggregate_repr. --- src/interpreter.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index da0eb95708acc..ff54f18f755f2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -546,8 +546,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn make_aggregate_repr(&self, variant_fields: V) -> Repr - where V: IntoIterator, F: IntoIterator> + fn make_aggregate_repr(&self, variant_fields: V) -> Repr + where V: IntoIterator, V::Item: IntoIterator> { let mut variants = Vec::new(); let mut max_variant_size = 0; @@ -563,22 +563,17 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { fields.push(FieldRepr { offset: offest, size: field_size }); } - if size > max_variant_size { - max_variant_size = size; - } + if size > max_variant_size { max_variant_size = size; } variants.push(fields); } - let num_variants = variants.len(); - - let discr_size = match num_variants { + let discr_size = match variants.len() { n if n <= 1 => 0, n if n <= 1 << 8 => 1, n if n <= 1 << 16 => 2, n if n <= 1 << 32 => 4, _ => 8, }; - Repr::Aggregate { discr_size: discr_size, max_variant_size: max_variant_size, From 432619ea5eda876a510c39699a362f87a42b38b5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 04:57:50 -0600 Subject: [PATCH 0121/1096] Use total size instead of max variant size in aggregates. --- src/interpreter.rs | 2 +- src/memory.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index ff54f18f755f2..2ccd5adeecbf5 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -576,7 +576,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }; Repr::Aggregate { discr_size: discr_size, - max_variant_size: max_variant_size, + size: max_variant_size + discr_size, variants: variants, } diff --git a/src/memory.rs b/src/memory.rs index a2632007d802d..667f28e987c2c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -54,8 +54,8 @@ pub enum Repr { /// structs and tuples. discr_size: usize, - /// The size of the largest variant in bytes. - max_variant_size: usize, + /// The size of the entire aggregate, including the discriminant. + size: usize, /// The representations of the contents of each variant. variants: Vec>, @@ -366,7 +366,7 @@ impl Repr { pub fn size(&self) -> usize { match *self { Repr::Primitive { size } => size, - Repr::Aggregate { discr_size, max_variant_size, .. } => discr_size + max_variant_size, + Repr::Aggregate { size, .. } => size, Repr::Array { elem_size, length } => elem_size * length, Repr::Pointer => POINTER_SIZE, Repr::FatPointer => POINTER_SIZE * 2, From 19bf6eec6b82665d8d59e4c91eacaf105831d26e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 05:32:00 -0600 Subject: [PATCH 0122/1096] Arena allocate and cache type representations. --- src/interpreter.rs | 54 ++++++++++++++++++++++++++++++++++------------ src/lib.rs | 7 +++++- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2ccd5adeecbf5..f9cc4e3aea86d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,3 +1,4 @@ +use arena::TypedArena; use rustc::middle::const_eval; use rustc::middle::def_id::DefId; use rustc::middle::subst::{self, Subst, Substs}; @@ -6,6 +7,7 @@ use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::util::nodemap::DefIdMap; +use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; use std::iter; use std::ops::Deref; @@ -18,7 +20,7 @@ use primval; const TRACE_EXECUTION: bool = true; -struct Interpreter<'a, 'tcx: 'a> { +struct Interpreter<'a, 'tcx: 'a, 'arena> { /// The results of the type checker, from rustc. tcx: &'a TyCtxt<'tcx>, @@ -28,6 +30,12 @@ struct Interpreter<'a, 'tcx: 'a> { /// A local cache from DefIds to Mir for non-crate-local items. mir_cache: RefCell>>>, + /// An arena allocator for type representations. + repr_arena: &'arena TypedArena, + + /// A cache for in-memory representations of types. + repr_cache: RefCell, &'arena Repr>>, + /// The virtual memory system. memory: Memory, @@ -81,12 +89,16 @@ enum TerminatorTarget { Return, } -impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { - fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { +impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { + fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, repr_arena: &'arena TypedArena) + -> Self + { Interpreter { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), + repr_arena: repr_arena, + repr_cache: RefCell::new(FnvHashMap()), memory: Memory::new(), stack: Vec::new(), substs_stack: Vec::new(), @@ -211,7 +223,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Switch { ref discr, ref targets, .. } => { let adt_ptr = try!(self.eval_lvalue(discr)); let adt_repr = self.lvalue_repr(discr); - let discr_size = match adt_repr { + let discr_size = match *adt_repr { Repr::Aggregate { discr_size, .. } => discr_size, _ => panic!("attmpted to switch on non-aggregate type"), }; @@ -361,7 +373,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Adt(_, variant_idx, _) => self.assign_to_aggregate(dest, &dest_repr, variant_idx, operands), - Vec => match dest_repr { + Vec => match *dest_repr { Repr::Array { elem_size, length } => { assert_eq!(length, operands.len()); for (i, operand) in operands.iter().enumerate() { @@ -449,7 +461,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { self.eval_operand_and_repr(op).map(|(p, _)| p) } - fn eval_operand_and_repr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<(Pointer, Repr)> { + fn eval_operand_and_repr(&mut self, op: &mir::Operand<'tcx>) + -> EvalResult<(Pointer, &'arena Repr)> + { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => Ok((try!(self.eval_lvalue(lvalue)), self.lvalue_repr(lvalue))), @@ -466,14 +480,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> Repr { + // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueRepr). + fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> &'arena Repr { use rustc::mir::tcx::LvalueTy; match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { LvalueTy::Ty { ty } => self.ty_to_repr(ty), LvalueTy::Downcast { ref adt_def, substs, variant_index } => { let field_tys = adt_def.variants[variant_index].fields.iter() .map(|f| f.ty(self.tcx, substs)); - self.make_aggregate_repr(iter::once(field_tys)) + self.repr_arena.alloc(self.make_aggregate_repr(iter::once(field_tys))) } } } @@ -494,7 +509,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let base_repr = self.lvalue_repr(&proj.base); use rustc::mir::repr::ProjectionElem::*; match proj.elem { - Field(field, _) => match base_repr { + Field(field, _) => match *base_repr { Repr::Aggregate { discr_size: 0, ref variants, .. } => { let fields = &variants[0]; base_ptr.offset(fields[field.index()].offset as isize) @@ -502,7 +517,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { _ => panic!("field access on non-product type: {:?}", base_repr), }, - Downcast(..) => match base_repr { + Downcast(..) => match *base_repr { Repr::Aggregate { discr_size, .. } => base_ptr.offset(discr_size as isize), _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), }, @@ -583,9 +598,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } // TODO(tsion): Cache these outputs. - fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> Repr { + fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { + let ty = ty.subst(self.tcx, self.current_substs()); + + if let Some(repr) = self.repr_cache.borrow().get(ty) { + return repr; + } + use syntax::ast::{IntTy, UintTy}; - match ty.subst(self.tcx, self.current_substs()).sty { + let repr = match ty.sty { ty::TyBool => Repr::Primitive { size: 1 }, ty::TyInt(IntTy::Is) => Repr::isize(), ty::TyInt(IntTy::I8) => Repr::Primitive { size: 1 }, @@ -625,7 +646,11 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } ref t => panic!("can't convert type to repr: {:?}", t), - } + }; + + let repr_ref = self.repr_arena.alloc(repr); + self.repr_cache.borrow_mut().insert(ty, repr_ref); + repr_ref } fn current_frame(&self) -> &Frame<'a, 'tcx> { @@ -777,7 +802,8 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) println!("Interpreting: {}", item.name); - let mut miri = Interpreter::new(tcx, mir_map); + let repr_arena = TypedArena::new(); + let mut miri = Interpreter::new(tcx, mir_map, &repr_arena); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { let size = miri.ty_to_repr(ty).size(); diff --git a/src/lib.rs b/src/lib.rs index 9b938d5147225..8646db3c222fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,15 @@ #![feature(btree_range, collections_bound, rustc_private)] -extern crate byteorder; +// From rustc. +extern crate arena; extern crate rustc; +extern crate rustc_data_structures; extern crate rustc_mir; extern crate syntax; +// From crates.io. +extern crate byteorder; + mod error; pub mod interpreter; mod memory; From 20da5cacc6a4d6e4da402a9b5e8af531c9c2c116 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 05:40:27 -0600 Subject: [PATCH 0123/1096] Cached those outputs. --- src/interpreter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f9cc4e3aea86d..23187e0980ad4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -597,7 +597,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } - // TODO(tsion): Cache these outputs. fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { let ty = ty.subst(self.tcx, self.current_substs()); From 49a26b93ca07f32d22713387c0f379862eacee30 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 05:40:56 -0600 Subject: [PATCH 0124/1096] whitespace. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 23187e0980ad4..1b64af66f007a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -600,7 +600,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { let ty = ty.subst(self.tcx, self.current_substs()); - if let Some(repr) = self.repr_cache.borrow().get(ty) { + if let Some(repr) = self.repr_cache.borrow().get(ty) { return repr; } From e057a156f286328430c6a75fc9909d5102f164e9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 05:49:12 -0600 Subject: [PATCH 0125/1096] Shorten ty_to_repr(ty).size() to ty_size(ty). --- src/interpreter.rs | 86 ++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1b64af66f007a..cde232e419e44 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut locals = Vec::with_capacity(num_args + num_vars + num_temps); for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { - let size = self.ty_to_repr(arg_decl.ty).size(); + let size = self.ty_size(arg_decl.ty); let dest = self.memory.allocate(size); let src = try!(self.eval_operand(arg_operand)); try!(self.memory.copy(src, dest, size)); @@ -165,7 +165,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); locals.extend(var_tys.chain(temp_tys).map(|ty| { - let size = self.ty_to_repr(ty).size(); + let size = self.ty_size(ty); self.memory.allocate(size) })); @@ -255,13 +255,13 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match &self.tcx.item_name(def_id).as_str()[..] { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_to_repr(ty).size() as u64; + let size = self.ty_size(ty) as u64; try!(self.memory.write_uint(dest, size, dest_repr.size())); } "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.ty_to_repr(pointee_ty).size() as isize; + let pointee_size = self.ty_size(pointee_ty) as isize; let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); let ptr = try!(self.memory.read_ptr(ptr_arg)); @@ -397,7 +397,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Box(ty) => { - let size = self.ty_to_repr(ty).size(); + let size = self.ty_size(ty); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr) } @@ -561,40 +561,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - fn make_aggregate_repr(&self, variant_fields: V) -> Repr - where V: IntoIterator, V::Item: IntoIterator> - { - let mut variants = Vec::new(); - let mut max_variant_size = 0; - - for field_tys in variant_fields { - let mut fields = Vec::new(); - let mut size = 0; - - for ty in field_tys { - let field_size = self.ty_to_repr(ty).size(); - let offest = size; - size += field_size; - fields.push(FieldRepr { offset: offest, size: field_size }); - } - - if size > max_variant_size { max_variant_size = size; } - variants.push(fields); - } - - let discr_size = match variants.len() { - n if n <= 1 => 0, - n if n <= 1 << 8 => 1, - n if n <= 1 << 16 => 2, - n if n <= 1 << 32 => 4, - _ => 8, - }; - Repr::Aggregate { - discr_size: discr_size, - size: max_variant_size + discr_size, - variants: variants, - } - + fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize { + self.ty_to_repr(ty).size() } fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { @@ -630,7 +598,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } ty::TyArray(ref elem_ty, length) => Repr::Array { - elem_size: self.ty_to_repr(elem_ty).size(), + elem_size: self.ty_size(elem_ty), length: length, }, @@ -652,6 +620,42 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { repr_ref } + fn make_aggregate_repr(&self, variant_fields: V) -> Repr + where V: IntoIterator, V::Item: IntoIterator> + { + let mut variants = Vec::new(); + let mut max_variant_size = 0; + + for field_tys in variant_fields { + let mut fields = Vec::new(); + let mut size = 0; + + for ty in field_tys { + let field_size = self.ty_size(ty); + let offest = size; + size += field_size; + fields.push(FieldRepr { offset: offest, size: field_size }); + } + + if size > max_variant_size { max_variant_size = size; } + variants.push(fields); + } + + let discr_size = match variants.len() { + n if n <= 1 => 0, + n if n <= 1 << 8 => 1, + n if n <= 1 << 16 => 2, + n if n <= 1 << 32 => 4, + _ => 8, + }; + Repr::Aggregate { + discr_size: discr_size, + size: max_variant_size + discr_size, + variants: variants, + } + + } + fn current_frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } @@ -805,7 +809,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map, &repr_arena); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { - let size = miri.ty_to_repr(ty).size(); + let size = miri.ty_size(ty); Some(miri.memory.allocate(size)) } ty::FnDiverging => None, From 36dfde50f48d0a2bfcb02d8512372f7c2ca37821 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 06:00:27 -0600 Subject: [PATCH 0126/1096] Extract intrinsic function handling. --- src/interpreter.rs | 74 ++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index cde232e419e44..2b8734d3d8c32 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -247,38 +247,13 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use syntax::abi::Abi; match fn_ty.abi { - Abi::RustIntrinsic => { - let ret_ptr = &mir::Lvalue::ReturnPointer; - let dest = try!(self.eval_lvalue(ret_ptr)); - let dest_repr = self.lvalue_repr(ret_ptr); - - match &self.tcx.item_name(def_id).as_str()[..] { - "size_of" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty) as u64; - try!(self.memory.write_uint(dest, size, dest_repr.size())); - } - - "offset" => { - let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.ty_size(pointee_ty) as isize; - let ptr_arg = try!(self.eval_operand(&args[0])); - let offset_arg = try!(self.eval_operand(&args[1])); - let ptr = try!(self.memory.read_ptr(ptr_arg)); - // TODO(tsion): read_isize - let offset = try!(self.memory.read_i64(offset_arg)); - let result_ptr = ptr.offset(offset as isize * pointee_size); - try!(self.memory.write_ptr(dest, result_ptr)); - } - - name => panic!("can't handle intrinsic named {}", name), - } - - // Since we pushed no stack frame, the main loop will act - // as if the call just completed and it's returning to the - // current frame. - TerminatorTarget::Call - } + Abi::RustIntrinsic => + try!(self.call_intrinsic( + &self.tcx.item_name(def_id).as_str(), + fn_ty, + substs, + args, + )), Abi::Rust => { // Only trait methods can have a Self parameter. @@ -313,6 +288,41 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(target) } + fn call_intrinsic(&mut self, name: &str, _fn_ty: &'tcx ty::BareFnTy<'tcx>, + substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>]) -> EvalResult + { + let ret_ptr = &mir::Lvalue::ReturnPointer; + let dest = try!(self.eval_lvalue(ret_ptr)); + let dest_size = self.lvalue_repr(ret_ptr).size(); + + match name { + "size_of" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty) as u64; + try!(self.memory.write_uint(dest, size, dest_size)); + } + + "offset" => { + let pointee_ty = *substs.types.get(subst::FnSpace, 0); + let pointee_size = self.ty_size(pointee_ty) as isize; + let ptr_arg = try!(self.eval_operand(&args[0])); + let offset_arg = try!(self.eval_operand(&args[1])); + let ptr = try!(self.memory.read_ptr(ptr_arg)); + // TODO(tsion): read_isize + let offset = try!(self.memory.read_i64(offset_arg)); + let result_ptr = ptr.offset(offset as isize * pointee_size); + try!(self.memory.write_ptr(dest, result_ptr)); + } + + name => panic!("can't handle intrinsic: {}", name), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(TerminatorTarget::Call) + } + fn assign_to_aggregate(&mut self, dest: Pointer, dest_repr: &Repr, variant: usize, operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { match *dest_repr { From 961137c018bf82b52fced7de8f03cc3bc66b049e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 06:04:24 -0600 Subject: [PATCH 0127/1096] Remove fn_ty argument from call_intrinsic for now. --- src/interpreter.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2b8734d3d8c32..42af6655ab1e9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -247,13 +247,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use syntax::abi::Abi; match fn_ty.abi { - Abi::RustIntrinsic => - try!(self.call_intrinsic( - &self.tcx.item_name(def_id).as_str(), - fn_ty, - substs, - args, - )), + Abi::RustIntrinsic => { + let name = self.tcx.item_name(def_id).as_str(); + try!(self.call_intrinsic(&name, substs, args)) + } Abi::Rust => { // Only trait methods can have a Self parameter. @@ -288,8 +285,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(target) } - fn call_intrinsic(&mut self, name: &str, _fn_ty: &'tcx ty::BareFnTy<'tcx>, - substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>]) -> EvalResult + fn call_intrinsic(&mut self, name: &str, substs: &'tcx Substs<'tcx>, + args: &[mir::Operand<'tcx>]) -> EvalResult { let ret_ptr = &mir::Lvalue::ReturnPointer; let dest = try!(self.eval_lvalue(ret_ptr)); From 0a8491b9853b63cc7f28b288e6261d430f6e64e3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 06:26:37 -0600 Subject: [PATCH 0128/1096] Simplify integer reading/writing. --- src/interpreter.rs | 6 +-- src/memory.rs | 108 +++++++-------------------------------------- 2 files changed, 19 insertions(+), 95 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 42af6655ab1e9..afd6f78a0dca4 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -306,7 +306,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let offset_arg = try!(self.eval_operand(&args[1])); let ptr = try!(self.memory.read_ptr(ptr_arg)); // TODO(tsion): read_isize - let offset = try!(self.memory.read_i64(offset_arg)); + let offset = try!(self.memory.read_int(offset_arg, 8)); let result_ptr = ptr.offset(offset as isize * pointee_size); try!(self.memory.write_ptr(dest, result_ptr)); } @@ -435,7 +435,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => // TODO(tsion): Add write_usize? (Host/target issues.) - self.memory.write_u64(dest.offset(8), length as u64), + self.memory.write_uint(dest.offset(8), length as u64, 8), _ => panic!("can't handle cast: {:?}", rvalue), } @@ -548,7 +548,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Integral(int) => { // TODO(tsion): Check int constant type. let ptr = self.memory.allocate(8); - try!(self.memory.write_u64(ptr, int.to_u64_unchecked())); + try!(self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)); Ok(ptr) } Str(ref _s) => unimplemented!(), diff --git a/src/memory.rs b/src/memory.rs index 667f28e987c2c..86b00452bc00a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -170,14 +170,14 @@ impl Memory { use syntax::ast::{IntTy, UintTy}; match ty.sty { ty::TyBool => self.read_bool(ptr).map(PrimVal::Bool), - ty::TyInt(IntTy::I8) => self.read_i8(ptr).map(PrimVal::I8), - ty::TyInt(IntTy::I16) => self.read_i16(ptr).map(PrimVal::I16), - ty::TyInt(IntTy::I32) => self.read_i32(ptr).map(PrimVal::I32), - ty::TyInt(IntTy::I64) => self.read_i64(ptr).map(PrimVal::I64), - ty::TyUint(UintTy::U8) => self.read_u8(ptr).map(PrimVal::U8), - ty::TyUint(UintTy::U16) => self.read_u16(ptr).map(PrimVal::U16), - ty::TyUint(UintTy::U32) => self.read_u32(ptr).map(PrimVal::U32), - ty::TyUint(UintTy::U64) => self.read_u64(ptr).map(PrimVal::U64), + ty::TyInt(IntTy::I8) => self.read_int(ptr, 1).map(|n| PrimVal::I8(n as i8)), + ty::TyInt(IntTy::I16) => self.read_int(ptr, 2).map(|n| PrimVal::I16(n as i16)), + ty::TyInt(IntTy::I32) => self.read_int(ptr, 4).map(|n| PrimVal::I32(n as i32)), + ty::TyInt(IntTy::I64) => self.read_int(ptr, 8).map(|n| PrimVal::I64(n as i64)), + ty::TyUint(UintTy::U8) => self.read_uint(ptr, 1).map(|n| PrimVal::U8(n as u8)), + ty::TyUint(UintTy::U16) => self.read_uint(ptr, 2).map(|n| PrimVal::U16(n as u16)), + ty::TyUint(UintTy::U32) => self.read_uint(ptr, 4).map(|n| PrimVal::U32(n as u32)), + ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)), // TODO(tsion): Pick the PrimVal dynamically. ty::TyInt(IntTy::Is) => self.read_int(ptr, POINTER_SIZE).map(PrimVal::I64), @@ -189,14 +189,14 @@ impl Memory { pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { match val { PrimVal::Bool(b) => self.write_bool(ptr, b), - PrimVal::I8(n) => self.write_i8(ptr, n), - PrimVal::I16(n) => self.write_i16(ptr, n), - PrimVal::I32(n) => self.write_i32(ptr, n), - PrimVal::I64(n) => self.write_i64(ptr, n), - PrimVal::U8(n) => self.write_u8(ptr, n), - PrimVal::U16(n) => self.write_u16(ptr, n), - PrimVal::U32(n) => self.write_u32(ptr, n), - PrimVal::U64(n) => self.write_u64(ptr, n), + PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), + PrimVal::I16(n) => self.write_int(ptr, n as i64, 2), + PrimVal::I32(n) => self.write_int(ptr, n as i64, 4), + PrimVal::I64(n) => self.write_int(ptr, n as i64, 8), + PrimVal::U8(n) => self.write_uint(ptr, n as u64, 1), + PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), + PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), + PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), } } @@ -215,44 +215,6 @@ impl Memory { Ok(()) } - pub fn read_i8(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 1).map(|b| b[0] as i8) - } - - pub fn write_i8(&mut self, ptr: Pointer, n: i8) -> EvalResult<()> { - self.get_bytes_mut(ptr, 1).map(|b| b[0] = n as u8) - } - - pub fn read_i16(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 2).map(byteorder::NativeEndian::read_i16) - } - - pub fn write_i16(&mut self, ptr: Pointer, n: i16) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 2)); - byteorder::NativeEndian::write_i16(bytes, n); - Ok(()) - } - - pub fn read_i32(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 4).map(byteorder::NativeEndian::read_i32) - } - - pub fn write_i32(&mut self, ptr: Pointer, n: i32) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 4)); - byteorder::NativeEndian::write_i32(bytes, n); - Ok(()) - } - - pub fn read_i64(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_i64) - } - - pub fn write_i64(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 8)); - byteorder::NativeEndian::write_i64(bytes, n); - Ok(()) - } - pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult { self.get_bytes(ptr, size).map(|mut b| b.read_int::(size).unwrap()) } @@ -261,44 +223,6 @@ impl Memory { self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::(n, size).unwrap()) } - pub fn read_u8(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 1).map(|b| b[0] as u8) - } - - pub fn write_u8(&mut self, ptr: Pointer, n: u8) -> EvalResult<()> { - self.get_bytes_mut(ptr, 1).map(|b| b[0] = n as u8) - } - - pub fn read_u16(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 2).map(byteorder::NativeEndian::read_u16) - } - - pub fn write_u16(&mut self, ptr: Pointer, n: u16) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 2)); - byteorder::NativeEndian::write_u16(bytes, n); - Ok(()) - } - - pub fn read_u32(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 4).map(byteorder::NativeEndian::read_u32) - } - - pub fn write_u32(&mut self, ptr: Pointer, n: u32) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 4)); - byteorder::NativeEndian::write_u32(bytes, n); - Ok(()) - } - - pub fn read_u64(&self, ptr: Pointer) -> EvalResult { - self.get_bytes(ptr, 8).map(byteorder::NativeEndian::read_u64) - } - - pub fn write_u64(&mut self, ptr: Pointer, n: u64) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 8)); - byteorder::NativeEndian::write_u64(bytes, n); - Ok(()) - } - pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult { self.get_bytes(ptr, size).map(|mut b| b.read_uint::(size).unwrap()) } From 6477a5c6949a66631958a2be2d98ef74777ffbc9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 06:39:29 -0600 Subject: [PATCH 0129/1096] Fix boolean tests and clean up code. --- src/memory.rs | 4 +--- test/bools.rs | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 86b00452bc00a..ae927865b4dd8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -210,9 +210,7 @@ impl Memory { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, 1)); - bytes[0] = b as u8; - Ok(()) + self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult { diff --git a/test/bools.rs b/test/bools.rs index e835d3e87e4b0..f8e6c2d89d22a 100755 --- a/test/bools.rs +++ b/test/bools.rs @@ -8,12 +8,14 @@ fn boolean() -> bool { #[miri_run] fn if_false() -> i64 { - if false { 1 } else { 0 } + let c = false; + if c { 1 } else { 0 } } #[miri_run] fn if_true() -> i64 { - if true { 1 } else { 0 } + let c = true; + if c { 1 } else { 0 } } #[miri_run] From 0b37be71c29cc35173c60a643cf44af11ef2f769 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 07:24:10 -0600 Subject: [PATCH 0130/1096] Change invalid pointer read panic into Err. --- src/error.rs | 3 +++ src/memory.rs | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index a05250e3b5482..9d8830f0d5347 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,7 @@ pub enum EvalError { InvalidBool, PointerOutOfBounds, InvalidPointerAccess, + ReadBytesAsPointer, } pub type EvalResult = Result; @@ -19,6 +20,8 @@ impl Error for EvalError { EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", EvalError::InvalidPointerAccess => "a raw memory access tried to access part of a pointer value as bytes", + EvalError::ReadBytesAsPointer => + "attempted to read some raw bytes as a pointer address", } } diff --git a/src/memory.rs b/src/memory.rs index ae927865b4dd8..d9d1a60111f41 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -149,10 +149,10 @@ impl Memory { let bytes = &alloc.bytes[ptr.offset..ptr.offset + POINTER_SIZE]; let offset = byteorder::NativeEndian::read_u64(bytes) as usize; - // TODO(tsion): Return an EvalError here instead of panicking. - let alloc_id = *alloc.relocations.get(&ptr.offset).unwrap(); - - Ok(Pointer { alloc_id: alloc_id, offset: offset }) + match alloc.relocations.get(&ptr.offset) { + Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), + None => Err(EvalError::ReadBytesAsPointer), + } } // TODO(tsion): Detect invalid writes here and elsewhere. From 12457607c3df8ff86f7c27348b60bbfe5643a33d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 07:53:26 -0600 Subject: [PATCH 0131/1096] Choose pointer size dynamically. --- src/interpreter.rs | 8 ++++---- src/memory.rs | 45 ++++++++++++++++++--------------------------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index afd6f78a0dca4..6c1144b6b9c16 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -582,13 +582,13 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use syntax::ast::{IntTy, UintTy}; let repr = match ty.sty { ty::TyBool => Repr::Primitive { size: 1 }, - ty::TyInt(IntTy::Is) => Repr::isize(), + ty::TyInt(IntTy::Is) => Repr::Primitive { size: self.memory.pointer_size }, ty::TyInt(IntTy::I8) => Repr::Primitive { size: 1 }, ty::TyInt(IntTy::I16) => Repr::Primitive { size: 2 }, ty::TyInt(IntTy::I32) => Repr::Primitive { size: 4 }, ty::TyInt(IntTy::I64) => Repr::Primitive { size: 8 }, - ty::TyUint(UintTy::Us) => Repr::usize(), + ty::TyUint(UintTy::Us) => Repr::Primitive { size: self.memory.pointer_size }, ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, @@ -613,9 +613,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { if ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) { - Repr::Pointer + Repr::Primitive { size: self.memory.pointer_size } } else { - Repr::FatPointer + Repr::Primitive { size: self.memory.pointer_size * 2 } } } diff --git a/src/memory.rs b/src/memory.rs index d9d1a60111f41..c563e84a90c5d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -8,12 +8,10 @@ use std::ptr; use error::{EvalError, EvalResult}; use primval::PrimVal; -// TODO(tsion): How should this get set? Host or target pointer size? -const POINTER_SIZE: usize = 8; - pub struct Memory { - next_id: u64, alloc_map: HashMap, + next_id: u64, + pub pointer_size: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -40,14 +38,11 @@ pub struct FieldRepr { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Repr { - /// Representation for a primitive type such as a boolean, integer, or character. + /// Representation for a non-aggregate type such as a boolean, integer, character or pointer. Primitive { size: usize }, - Pointer, - FatPointer, - /// The representation for aggregate types including structs, enums, and tuples. Aggregate { /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for @@ -71,7 +66,13 @@ pub enum Repr { impl Memory { pub fn new() -> Self { - Memory { next_id: 0, alloc_map: HashMap::new() } + Memory { + alloc_map: HashMap::new(), + next_id: 0, + + // TODO(tsion): Should this be host's or target's usize? + pointer_size: mem::size_of::(), + } } pub fn allocate(&mut self, size: usize) -> Pointer { @@ -145,8 +146,8 @@ impl Memory { pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { let alloc = try!(self.get(ptr.alloc_id)); - try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + POINTER_SIZE)); - let bytes = &alloc.bytes[ptr.offset..ptr.offset + POINTER_SIZE]; + try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + self.pointer_size)); + let bytes = &alloc.bytes[ptr.offset..ptr.offset + self.pointer_size]; let offset = byteorder::NativeEndian::read_u64(bytes) as usize; match alloc.relocations.get(&ptr.offset) { @@ -158,7 +159,8 @@ impl Memory { // TODO(tsion): Detect invalid writes here and elsewhere. pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> { { - let bytes = try!(self.get_bytes_mut(dest, POINTER_SIZE)); + let size = self.pointer_size; + let bytes = try!(self.get_bytes_mut(dest, size)); byteorder::NativeEndian::write_u64(bytes, ptr_val.offset as u64); } let alloc = try!(self.get_mut(dest.alloc_id)); @@ -180,8 +182,8 @@ impl Memory { ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)), // TODO(tsion): Pick the PrimVal dynamically. - ty::TyInt(IntTy::Is) => self.read_int(ptr, POINTER_SIZE).map(PrimVal::I64), - ty::TyUint(UintTy::Us) => self.read_uint(ptr, POINTER_SIZE).map(PrimVal::U64), + ty::TyInt(IntTy::Is) => self.read_int(ptr, self.pointer_size).map(PrimVal::I64), + ty::TyUint(UintTy::Us) => self.read_uint(ptr, self.pointer_size).map(PrimVal::U64), _ => panic!("primitive read of non-primitive type: {:?}", ty), } } @@ -241,7 +243,8 @@ impl Allocation { fn count_overlapping_relocations(&self, start: usize, end: usize) -> usize { self.relocations.range( - Included(&start.saturating_sub(POINTER_SIZE - 1)), + // FIXME(tsion): Assuming pointer size is 8. Move this method to Memory. + Included(&start.saturating_sub(8 - 1)), Excluded(&end) ).count() } @@ -275,23 +278,11 @@ impl Pointer { } impl Repr { - // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? - pub fn isize() -> Self { - Repr::Primitive { size: mem::size_of::() } - } - - // TODO(tsion): Choice is based on host machine's type size. Should this be how miri works? - pub fn usize() -> Self { - Repr::Primitive { size: mem::size_of::() } - } - pub fn size(&self) -> usize { match *self { Repr::Primitive { size } => size, Repr::Aggregate { size, .. } => size, Repr::Array { elem_size, length } => elem_size * length, - Repr::Pointer => POINTER_SIZE, - Repr::FatPointer => POINTER_SIZE * 2, } } } From f8f31ea5491ee282711b59b6bcbffc1140052a3f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 17 Mar 2016 08:01:34 -0600 Subject: [PATCH 0132/1096] Remove some TODOs. --- src/interpreter.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 6c1144b6b9c16..25f5e54191471 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -305,8 +305,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); let ptr = try!(self.memory.read_ptr(ptr_arg)); - // TODO(tsion): read_isize - let offset = try!(self.memory.read_int(offset_arg, 8)); + let offset = try!(self.memory.read_int(offset_arg, self.memory.pointer_size)); let result_ptr = ptr.offset(offset as isize * pointee_size); try!(self.memory.write_ptr(dest, result_ptr)); } @@ -433,9 +432,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let dest_pointee_ty = pointee_type(dest_ty).unwrap(); match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { - (&ty::TyArray(_, length), &ty::TySlice(_)) => - // TODO(tsion): Add write_usize? (Host/target issues.) - self.memory.write_uint(dest.offset(8), length as u64, 8), + (&ty::TyArray(_, length), &ty::TySlice(_)) => { + let size = self.memory.pointer_size; + self.memory.write_uint( + dest.offset(size as isize), + length as u64, + size, + ) + } _ => panic!("can't handle cast: {:?}", rvalue), } From 27ff9ab914c43a4e32774ed12e6710d05c28703b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 10:48:31 -0600 Subject: [PATCH 0133/1096] Add initial support for closures. --- src/interpreter.rs | 112 +++++++++++++++++++++++++-------------------- test/closures.rs | 38 +++++++++++++++ 2 files changed, 100 insertions(+), 50 deletions(-) create mode 100644 test/closures.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 25f5e54191471..3e1444ad51640 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -145,29 +145,20 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(()) } - fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>], - return_ptr: Option) -> EvalResult<()> { - let num_args = mir.arg_decls.len(); - let num_vars = mir.var_decls.len(); - let num_temps = mir.temp_decls.len(); - assert_eq!(args.len(), num_args); - - let mut locals = Vec::with_capacity(num_args + num_vars + num_temps); - - for (arg_decl, arg_operand) in mir.arg_decls.iter().zip(args) { - let size = self.ty_size(arg_decl.ty); - let dest = self.memory.allocate(size); - let src = try!(self.eval_operand(arg_operand)); - try!(self.memory.copy(src, dest, size)); - locals.push(dest); - } - + fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, return_ptr: Option) + -> EvalResult<()> + { + let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); - locals.extend(var_tys.chain(temp_tys).map(|ty| { + + let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { let size = self.ty_size(ty); self.memory.allocate(size) - })); + }).collect(); + + let num_args = mir.arg_decls.len(); + let num_vars = mir.var_decls.len(); self.stack.push(Frame { mir: mir.clone(), @@ -238,13 +229,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { return_ptr = Some(try!(self.eval_lvalue(lv))); } - let func_ty = self.current_frame().mir.operand_ty(self.tcx, func); - + let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnDef(def_id, substs, fn_ty) => { - let substs = self.tcx.mk_substs( - substs.subst(self.tcx, self.current_substs())); - use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { @@ -252,7 +239,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.call_intrinsic(&name, substs, args)) } - Abi::Rust => { + Abi::Rust | Abi::RustCall => { + // TODO(tsion): Adjust the first argument when calling a Fn or + // FnMut closure via FnOnce::call_once. + // Only trait methods can have a Self parameter. let (def_id, substs) = if substs.self_ty().is_some() { self.trait_method(def_id, substs) @@ -260,9 +250,39 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { (def_id, substs) }; + let mut arg_srcs = Vec::new(); + for arg in args { + let (src, repr) = try!(self.eval_operand_and_repr(arg)); + arg_srcs.push((src, repr.size())); + } + + if fn_ty.abi == Abi::RustCall && !args.is_empty() { + arg_srcs.pop(); + let last_arg = args.last().unwrap(); + let (last_src, last_repr) = + try!(self.eval_operand_and_repr(last_arg)); + match *last_repr { + Repr::Aggregate { discr_size: 0, ref variants, .. } => { + assert_eq!(variants.len(), 1); + for field in &variants[0] { + let src = last_src.offset(field.offset as isize); + arg_srcs.push((src, field.size)); + } + } + + _ => panic!("expected tuple as last argument in function with 'rust-call' ABI"), + } + } + let mir = self.load_mir(def_id); self.substs_stack.push(substs); - try!(self.push_stack_frame(mir, args, return_ptr)); + try!(self.push_stack_frame(mir, return_ptr)); + + for (i, (src, size)) in arg_srcs.into_iter().enumerate() { + let dest = self.current_frame().locals[i]; + try!(self.memory.copy(src, dest, size)); + } + TerminatorTarget::Call } @@ -393,7 +413,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("expected Repr::Array target"), }, - Closure(..) => unimplemented!(), + Closure(..) => self.assign_to_aggregate(dest, &dest_repr, 0, operands), } } @@ -422,7 +442,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } let src = try!(self.eval_operand(operand)); - let src_ty = self.current_frame().mir.operand_ty(self.tcx, operand); + let src_ty = self.operand_ty(operand); use rustc::mir::repr::CastKind::*; match kind { @@ -465,7 +485,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { - self.current_frame().mir.operand_ty(self.tcx, operand) + self.current_frame().mir + .operand_ty(self.tcx, operand) + .subst(self.tcx, self.current_substs()) } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { @@ -623,6 +645,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + ty::TyClosure(_, ref closure_substs) => + self.make_aggregate_repr(iter::once(closure_substs.upvar_tys.iter().cloned())), + ref t => panic!("can't convert type to repr: {:?}", t), }; @@ -736,32 +761,18 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { traits::VtableImpl(vtable_impl) => { let impl_did = vtable_impl.impl_def_id; let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the - // impl and those from the method: - let impl_substs = vtable_impl.substs.with_method_from(&substs); + // Create a concatenated set of substitutions which includes those from the impl + // and those from the method: + let impl_substs = vtable_impl.substs.with_method_from(substs); let substs = self.tcx.mk_substs(impl_substs); let mth = self.tcx.get_impl_method(impl_did, substs, mname); - println!("{:?} {:?}", mth.method.def_id, mth.substs); (mth.method.def_id, mth.substs) } - traits::VtableClosure(_vtable_closure) => { - // The substitutions should have no type parameters remaining after passing - // through fulfill_obligation - let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - unimplemented!() - // vtable_closure.closure_def_id - // vtable_closure.substs - // trait_closure_kind - // let method_ty = def_ty(tcx, def_id, substs); - // let fn_ptr_ty = match method_ty.sty { - // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - // _ => unreachable!("expected fn item type, found {}", - // method_ty) - // }; - // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) - } + traits::VtableClosure(vtable_closure) => + (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), + traits::VtableFnPointer(_fn_ty) => { let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); unimplemented!() @@ -775,6 +786,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // }; // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } + traits::VtableObject(ref _data) => { unimplemented!() // Callee { @@ -825,7 +837,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } ty::FnDiverging => None, }; - miri.push_stack_frame(CachedMir::Ref(mir), &[], return_ptr).unwrap(); + miri.push_stack_frame(CachedMir::Ref(mir), return_ptr).unwrap(); miri.run().unwrap(); if let Some(ret) = return_ptr { diff --git a/test/closures.rs b/test/closures.rs new file mode 100644 index 0000000000000..bff172d45b479 --- /dev/null +++ b/test/closures.rs @@ -0,0 +1,38 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn simple() -> i32 { + let y = 10; + let f = |x| x + y; + f(2) +} + +#[miri_run] +fn crazy_closure() -> (i32, i32, i32) { + fn inner(t: T) -> (i32, T, T) { + struct NonCopy; + let x = NonCopy; + + let a = 2; + let b = 40; + let f = move |y, z, asdf| { + drop(x); + (a + b + y + z, asdf, t) + }; + f(a, b, t) + } + + inner(10) +} + +// #[miri_run] +// fn closure_arg_adjustment_problem() -> i64 { +// fn once(f: F) { f(2); } +// let mut y = 1; +// { +// let f = |x| y += x; +// once(f); +// } +// y +// } From ee47ce3978728cfc51f1874d31dbd6d2061c24fd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 11:52:13 -0600 Subject: [PATCH 0134/1096] Normalize associated types when monomorphizing. --- src/interpreter.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3e1444ad51640..c8259063f4209 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,7 @@ use arena::TypedArena; use rustc::middle::const_eval; use rustc::middle::def_id::DefId; +use rustc::middle::infer; use rustc::middle::subst::{self, Subst, Substs}; use rustc::middle::traits; use rustc::middle::ty::{self, TyCtxt}; @@ -12,6 +13,7 @@ use std::cell::RefCell; use std::iter; use std::ops::Deref; use std::rc::Rc; +use syntax::ast; use syntax::codemap::DUMMY_SP; use error::EvalResult; @@ -485,9 +487,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { - self.current_frame().mir - .operand_ty(self.tcx, operand) - .subst(self.tcx, self.current_substs()) + let ty = self.current_frame().mir.operand_ty(self.tcx, operand); + self.monomorphize(ty) } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { @@ -594,12 +595,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { + let substituted = ty.subst(self.tcx, self.current_substs()); + infer::normalize_associated_type(self.tcx, &substituted) + } + fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize { self.ty_to_repr(ty).size() } fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { - let ty = ty.subst(self.tcx, self.current_substs()); + let ty = self.monomorphize(ty); if let Some(repr) = self.repr_cache.borrow().get(ty) { return repr; @@ -724,9 +730,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { - use rustc::middle::infer; - use syntax::ast; - // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables); From b1af71e217384b229716f9a0eca82f6d310bc9d5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 11:52:28 -0600 Subject: [PATCH 0135/1096] Implement more intrinsics. --- src/interpreter.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c8259063f4209..b8572efd260ac 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -315,12 +315,23 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let dest_size = self.lvalue_repr(ret_ptr).size(); match name { - "size_of" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty) as u64; - try!(self.memory.write_uint(dest, size, dest_size)); + "copy_nonoverlapping" => { + let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_size = self.ty_size(elem_ty); + + let src_arg = try!(self.eval_operand(&args[0])); + let dest_arg = try!(self.eval_operand(&args[1])); + let count_arg = try!(self.eval_operand(&args[2])); + + let src = try!(self.memory.read_ptr(src_arg)); + let dest = try!(self.memory.read_ptr(dest_arg)); + let count = try!(self.memory.read_int(count_arg, self.memory.pointer_size)); + + try!(self.memory.copy(src, dest, count as usize * elem_size)); } + "forget" => {} + "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.ty_size(pointee_ty) as isize; @@ -332,6 +343,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_ptr(dest, result_ptr)); } + "size_of" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty) as u64; + try!(self.memory.write_uint(dest, size, dest_size)); + } + + "uninit" => {} + name => panic!("can't handle intrinsic: {}", name), } From 31b8c1777037a92ba818e9461e2927b2791eb7b5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 11:53:24 -0600 Subject: [PATCH 0136/1096] Reformat for consistency. --- src/interpreter.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b8572efd260ac..7c62f701856d2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -335,10 +335,13 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.ty_size(pointee_ty) as isize; - let ptr_arg = try!(self.eval_operand(&args[0])); + + let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); - let ptr = try!(self.memory.read_ptr(ptr_arg)); + + let ptr = try!(self.memory.read_ptr(ptr_arg)); let offset = try!(self.memory.read_int(offset_arg, self.memory.pointer_size)); + let result_ptr = ptr.offset(offset as isize * pointee_size); try!(self.memory.write_ptr(dest, result_ptr)); } From e910d40e6a650c06edc6e43a77a823170c47b4f2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 12:11:39 -0600 Subject: [PATCH 0137/1096] Add error message for missing MIR. --- src/interpreter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 7c62f701856d2..80a86764c44bf 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -743,7 +743,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use rustc::middle::cstore::CrateStore; let cs = &self.tcx.sess.cstore; - let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap(); + let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { + panic!("no mir for {:?}", def_id); + }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); CachedMir::Owned(cached) From de10839f96e0aaa7abe7de540c9f2fa5e59eab1f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 12:11:57 -0600 Subject: [PATCH 0138/1096] Avoid an integer underflow error. --- src/memory.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index c563e84a90c5d..6878fbdce6b03 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -119,7 +119,8 @@ impl Memory { for &mut (ref mut offset, _) in &mut relocations { alloc.relocations.remove(offset); - *offset += dest.offset - src.offset; + *offset += dest.offset; + *offset -= src.offset; } (bytes, relocations) From 81f49ed1c0c42ae2d404e4283147ea5d63ac6b17 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 12:42:09 -0600 Subject: [PATCH 0139/1096] Implement transmute. --- src/interpreter.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index 80a86764c44bf..f04013dc99d32 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -352,6 +352,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(dest, size, dest_size)); } + "transmute" => { + let src = try!(self.eval_operand(&args[0])); + try!(self.memory.copy(src, dest, dest_size)); + } + "uninit" => {} name => panic!("can't handle intrinsic: {}", name), From dbc9913b7d0e4521430ef19862652df22c9e76c7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 23:03:46 -0600 Subject: [PATCH 0140/1096] Implement pointer primvals and comparison ops on them. --- src/error.rs | 11 +++++--- src/interpreter.rs | 59 +++++++++++++++++++++++++++++++++++++------ src/memory.rs | 28 +++------------------ src/primval.rs | 62 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 116 insertions(+), 44 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9d8830f0d5347..f16bdc3b6b3c8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,8 +6,9 @@ pub enum EvalError { DanglingPointerDeref, InvalidBool, PointerOutOfBounds, - InvalidPointerAccess, + ReadPointerAsBytes, ReadBytesAsPointer, + InvalidPointerMath, } pub type EvalResult = Result; @@ -18,10 +19,12 @@ impl Error for EvalError { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidBool => "invalid boolean value read", EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", - EvalError::InvalidPointerAccess => - "a raw memory access tried to access part of a pointer value as bytes", + EvalError::ReadPointerAsBytes => + "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::ReadBytesAsPointer => - "attempted to read some raw bytes as a pointer address", + "attempted to interpret some raw bytes as a pointer address", + EvalError::InvalidPointerMath => + "attempted to do math or a comparison on pointers into different allocations", } } diff --git a/src/interpreter.rs b/src/interpreter.rs index f04013dc99d32..fe4217ca9aa7e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -16,9 +16,9 @@ use std::rc::Rc; use syntax::ast; use syntax::codemap::DUMMY_SP; -use error::EvalResult; +use error::{EvalError, EvalResult}; use memory::{self, FieldRepr, Memory, Pointer, Repr}; -use primval; +use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = true; @@ -404,19 +404,20 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { BinaryOp(bin_op, ref left, ref right) => { let left_ptr = try!(self.eval_operand(left)); let left_ty = self.operand_ty(left); - let left_val = try!(self.memory.read_primval(left_ptr, left_ty)); + let left_val = try!(self.read_primval(left_ptr, left_ty)); let right_ptr = try!(self.eval_operand(right)); let right_ty = self.operand_ty(right); - let right_val = try!(self.memory.read_primval(right_ptr, right_ty)); + let right_val = try!(self.read_primval(right_ptr, right_ty)); - self.memory.write_primval(dest, primval::binary_op(bin_op, left_val, right_val)) + let val = try!(primval::binary_op(bin_op, left_val, right_val)); + self.memory.write_primval(dest, val) } UnaryOp(un_op, ref operand) => { let ptr = try!(self.eval_operand(operand)); let ty = self.operand_ty(operand); - let val = try!(self.memory.read_primval(ptr, ty)); + let val = try!(self.read_primval(ptr, ty)); self.memory.write_primval(dest, primval::unary_op(un_op, val)) } @@ -627,6 +628,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { infer::normalize_associated_type(self.tcx, &substituted) } + fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { + ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) + } + fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize { self.ty_to_repr(ty).size() } @@ -671,7 +676,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - if ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) { + if self.type_is_sized(ty) { Repr::Primitive { size: self.memory.pointer_size } } else { Repr::Primitive { size: self.memory.pointer_size * 2 } @@ -725,6 +730,46 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } + pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { + use syntax::ast::{IntTy, UintTy}; + let val = match ty.sty { + ty::TyBool => PrimVal::Bool(try!(self.memory.read_bool(ptr))), + ty::TyInt(IntTy::I8) => PrimVal::I8(try!(self.memory.read_int(ptr, 1)) as i8), + ty::TyInt(IntTy::I16) => PrimVal::I16(try!(self.memory.read_int(ptr, 2)) as i16), + ty::TyInt(IntTy::I32) => PrimVal::I32(try!(self.memory.read_int(ptr, 4)) as i32), + ty::TyInt(IntTy::I64) => PrimVal::I64(try!(self.memory.read_int(ptr, 8)) as i64), + ty::TyUint(UintTy::U8) => PrimVal::U8(try!(self.memory.read_uint(ptr, 1)) as u8), + ty::TyUint(UintTy::U16) => PrimVal::U16(try!(self.memory.read_uint(ptr, 2)) as u16), + ty::TyUint(UintTy::U32) => PrimVal::U32(try!(self.memory.read_uint(ptr, 4)) as u32), + ty::TyUint(UintTy::U64) => PrimVal::U64(try!(self.memory.read_uint(ptr, 8)) as u64), + + // TODO(tsion): Pick the PrimVal dynamically. + ty::TyInt(IntTy::Is) => + PrimVal::I64(try!(self.memory.read_int(ptr, self.memory.pointer_size))), + ty::TyUint(UintTy::Us) => + PrimVal::U64(try!(self.memory.read_uint(ptr, self.memory.pointer_size))), + + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + if self.type_is_sized(ty) { + match self.memory.read_ptr(ptr) { + Ok(p) => PrimVal::AbstractPtr(p), + Err(EvalError::ReadBytesAsPointer) => { + let n = try!(self.memory.read_uint(ptr, self.memory.pointer_size)); + PrimVal::IntegerPtr(n) + } + Err(e) => return Err(e), + } + } else { + panic!("unimplemented: primitive read of fat pointer type: {:?}", ty); + } + } + + _ => panic!("primitive read of non-primitive type: {:?}", ty), + }; + Ok(val) + } + fn current_frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } diff --git a/src/memory.rs b/src/memory.rs index 6878fbdce6b03..1690ea8dc0b05 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,4 @@ use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; -use rustc::middle::ty; use std::collections::{BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; use std::mem; @@ -169,26 +168,6 @@ impl Memory { Ok(()) } - pub fn read_primval(&self, ptr: Pointer, ty: ty::Ty) -> EvalResult { - use syntax::ast::{IntTy, UintTy}; - match ty.sty { - ty::TyBool => self.read_bool(ptr).map(PrimVal::Bool), - ty::TyInt(IntTy::I8) => self.read_int(ptr, 1).map(|n| PrimVal::I8(n as i8)), - ty::TyInt(IntTy::I16) => self.read_int(ptr, 2).map(|n| PrimVal::I16(n as i16)), - ty::TyInt(IntTy::I32) => self.read_int(ptr, 4).map(|n| PrimVal::I32(n as i32)), - ty::TyInt(IntTy::I64) => self.read_int(ptr, 8).map(|n| PrimVal::I64(n as i64)), - ty::TyUint(UintTy::U8) => self.read_uint(ptr, 1).map(|n| PrimVal::U8(n as u8)), - ty::TyUint(UintTy::U16) => self.read_uint(ptr, 2).map(|n| PrimVal::U16(n as u16)), - ty::TyUint(UintTy::U32) => self.read_uint(ptr, 4).map(|n| PrimVal::U32(n as u32)), - ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)), - - // TODO(tsion): Pick the PrimVal dynamically. - ty::TyInt(IntTy::Is) => self.read_int(ptr, self.pointer_size).map(PrimVal::I64), - ty::TyUint(UintTy::Us) => self.read_uint(ptr, self.pointer_size).map(PrimVal::U64), - _ => panic!("primitive read of non-primitive type: {:?}", ty), - } - } - pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { match val { PrimVal::Bool(b) => self.write_bool(ptr, b), @@ -199,7 +178,8 @@ impl Memory { PrimVal::U8(n) => self.write_uint(ptr, n as u64, 1), PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), - PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::U64(n) | PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::AbstractPtr(_p) => unimplemented!(), } } @@ -258,7 +238,7 @@ impl Allocation { if n == 0 { Ok(()) } else { - Err(EvalError::InvalidPointerAccess) + Err(EvalError::ReadPointerAsBytes) } } @@ -267,7 +247,7 @@ impl Allocation { if self.count_overlapping_relocations(start, end) == 0 { Ok(()) } else { - Err(EvalError::InvalidPointerAccess) + Err(EvalError::ReadPointerAsBytes) } } } diff --git a/src/primval.rs b/src/primval.rs index a34da06d59717..c9117d033fdd5 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,26 +1,32 @@ use rustc::mir::repr as mir; +use error::{EvalError, EvalResult}; +use memory::Pointer; + #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimVal { Bool(bool), I8(i8), I16(i16), I32(i32), I64(i64), U8(u8), U16(u16), U32(u32), U64(u64), + + AbstractPtr(Pointer), + IntegerPtr(u64), } -pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { +pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult { macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ use rustc::mir::repr::BinOp::*; use self::PrimVal::*; match bin_op { - Add => $v($l + $r), - Sub => $v($l - $r), - Mul => $v($l * $r), - Div => $v($l / $r), - Rem => $v($l % $r), + Add => $v($l + $r), + Sub => $v($l - $r), + Mul => $v($l * $r), + Div => $v($l / $r), + Rem => $v($l % $r), BitXor => $v($l ^ $r), BitAnd => $v($l & $r), - BitOr => $v($l | $r), + BitOr => $v($l | $r), // TODO(tsion): Can have differently-typed RHS. Shl => $v($l << $r), @@ -36,8 +42,18 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { }) } + fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult { + use rustc::mir::repr::BinOp::*; + match bin_op { + Eq => Ok(Bool(false)), + Ne => Ok(Bool(true)), + Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), + _ => unimplemented!(), + } + } + use self::PrimVal::*; - match (left, right) { + let val = match (left, right) { (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), (I32(l), I32(r)) => int_binops!(I32, l, r), @@ -46,8 +62,36 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal { (U16(l), U16(r)) => int_binops!(U16, l, r), (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + + (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), + + (AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) => + return unrelated_ptr_ops(bin_op), + + (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => { + if l_ptr.alloc_id != r_ptr.alloc_id { + return unrelated_ptr_ops(bin_op); + } + + let l = l_ptr.offset; + let r = r_ptr.offset; + + use rustc::mir::repr::BinOp::*; + match bin_op { + Eq => Bool(l == r), + Ne => Bool(l != r), + Lt => Bool(l < r), + Le => Bool(l <= r), + Gt => Bool(l > r), + Ge => Bool(l >= r), + _ => unimplemented!(), + } + } + _ => unimplemented!(), - } + }; + + Ok(val) } pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal { From 5d4a804100f0ad643fb589f93851ceaee427fd02 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 23:04:07 -0600 Subject: [PATCH 0141/1096] Implement the dummy 'assume' intrinsic. --- src/interpreter.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index fe4217ca9aa7e..a2c44f4fed598 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -315,6 +315,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let dest_size = self.lvalue_repr(ret_ptr).size(); match name { + "assume" => {} + "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.ty_size(elem_ty); @@ -330,6 +332,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.copy(src, dest, count as usize * elem_size)); } + // TODO(tsion): Mark as dropped? "forget" => {} "offset" => { From 26c4772f517885f58643f5a962ccd49425765376 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 23:19:39 -0600 Subject: [PATCH 0142/1096] Implement string literals. --- src/interpreter.rs | 12 +++++++++++- src/memory.rs | 4 ++++ test/strings.rs | 12 ++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100755 test/strings.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index a2c44f4fed598..620f994ddcf0b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -587,6 +587,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), }, + // FIXME(tsion): Wrong for fat pointers. Deref => try!(self.memory.read_ptr(base_ptr)), _ => unimplemented!(), @@ -599,6 +600,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(ptr) } + // TODO(tsion): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { use rustc::middle::const_eval::ConstVal::*; match *const_val { @@ -609,7 +611,15 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)); Ok(ptr) } - Str(ref _s) => unimplemented!(), + Str(ref s) => { + let psize = self.memory.pointer_size; + let static_ptr = self.memory.allocate(s.len()); + let ptr = self.memory.allocate(psize * 2); + try!(self.memory.write_bytes(static_ptr, s.as_bytes())); + try!(self.memory.write_ptr(ptr, static_ptr)); + try!(self.memory.write_uint(ptr.offset(psize as isize), s.len() as u64, psize)); + Ok(ptr) + } ByteStr(ref _bs) => unimplemented!(), Bool(b) => { let ptr = self.memory.allocate(1); diff --git a/src/memory.rs b/src/memory.rs index 1690ea8dc0b05..e5ce009d3731e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -144,6 +144,10 @@ impl Memory { Ok(()) } + pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { + self.get_bytes_mut(ptr, src.len()).map(|dest| dest.clone_from_slice(src)) + } + pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { let alloc = try!(self.get(ptr.alloc_id)); try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + self.pointer_size)); diff --git a/test/strings.rs b/test/strings.rs new file mode 100755 index 0000000000000..0b9c3faff64d8 --- /dev/null +++ b/test/strings.rs @@ -0,0 +1,12 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn empty() -> &'static str { + "" +} + +#[miri_run] +fn hello() -> &'static str { + "Hello, world!" +} From 668f2b6fd475af40e1b19d49f04ee5feab83a68f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 18 Mar 2016 23:20:59 -0600 Subject: [PATCH 0143/1096] Implement bytestring literals. --- src/interpreter.rs | 9 ++++++++- test/strings.rs | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 620f994ddcf0b..a15eec77b5b7f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -620,7 +620,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(ptr.offset(psize as isize), s.len() as u64, psize)); Ok(ptr) } - ByteStr(ref _bs) => unimplemented!(), + ByteStr(ref bs) => { + let psize = self.memory.pointer_size; + let static_ptr = self.memory.allocate(bs.len()); + let ptr = self.memory.allocate(psize); + try!(self.memory.write_bytes(static_ptr, bs)); + try!(self.memory.write_ptr(ptr, static_ptr)); + Ok(ptr) + } Bool(b) => { let ptr = self.memory.allocate(1); try!(self.memory.write_bool(ptr, b)); diff --git a/test/strings.rs b/test/strings.rs index 0b9c3faff64d8..a442901bb5975 100755 --- a/test/strings.rs +++ b/test/strings.rs @@ -10,3 +10,8 @@ fn empty() -> &'static str { fn hello() -> &'static str { "Hello, world!" } + +#[miri_run] +fn hello_bytes() -> &'static [u8; 13] { + b"Hello, world!" +} From 1eb66b6701ed9bf6c9c06310814fff6463da4594 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 19 Mar 2016 09:09:13 -0600 Subject: [PATCH 0144/1096] Handle "offset" intrinsic on integer pointers. --- src/interpreter.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a15eec77b5b7f..52463c1af55ca 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -342,11 +342,21 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); - let ptr = try!(self.memory.read_ptr(ptr_arg)); let offset = try!(self.memory.read_int(offset_arg, self.memory.pointer_size)); - let result_ptr = ptr.offset(offset as isize * pointee_size); - try!(self.memory.write_ptr(dest, result_ptr)); + match self.memory.read_ptr(ptr_arg) { + Ok(ptr) => { + let result_ptr = ptr.offset(offset as isize * pointee_size); + try!(self.memory.write_ptr(dest, result_ptr)); + } + Err(EvalError::ReadBytesAsPointer) => { + let psize = self.memory.pointer_size; + let addr = try!(self.memory.read_int(ptr_arg, psize)); + let result_addr = addr + offset * pointee_size as i64; + try!(self.memory.write_int(dest, result_addr, psize)); + } + Err(e) => return Err(e), + } } "size_of" => { From 6c6cea28bde5078ed1ab07245d088e10d13756f9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 19 Mar 2016 10:01:53 -0600 Subject: [PATCH 0145/1096] Write intrinsic result to correct lvalue. --- src/interpreter.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 52463c1af55ca..73011886cdb0a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -238,7 +238,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match fn_ty.abi { Abi::RustIntrinsic => { let name = self.tcx.item_name(def_id).as_str(); - try!(self.call_intrinsic(&name, substs, args)) + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.ty_size(ty); + try!(self.call_intrinsic(&name, substs, args, + return_ptr.unwrap(), size)) + } + ty::FnDiverging => unimplemented!(), + } } Abi::Rust | Abi::RustCall => { @@ -308,12 +315,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn call_intrinsic(&mut self, name: &str, substs: &'tcx Substs<'tcx>, - args: &[mir::Operand<'tcx>]) -> EvalResult + args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize) + -> EvalResult { - let ret_ptr = &mir::Lvalue::ReturnPointer; - let dest = try!(self.eval_lvalue(ret_ptr)); - let dest_size = self.lvalue_repr(ret_ptr).size(); - match name { "assume" => {} From 20f152296ac397f900fc033587a231a3a3d2e4df Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 19 Mar 2016 11:01:33 -0600 Subject: [PATCH 0146/1096] Implement min_align_of and a hacky mul_with_overflow. --- src/interpreter.rs | 23 +++++++++++++++++++++++ src/lib.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 73011886cdb0a..3f710ba2e2b23 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -339,6 +339,29 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // TODO(tsion): Mark as dropped? "forget" => {} + "min_align_of" => { + try!(self.memory.write_int(dest, 1, dest_size)); + } + + // FIXME(tsion): Handle different integer types correctly. + "mul_with_overflow" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty); + + let left_arg = try!(self.eval_operand(&args[0])); + let right_arg = try!(self.eval_operand(&args[1])); + + let left = try!(self.memory.read_int(left_arg, size)); + let right = try!(self.memory.read_int(right_arg, size)); + + let (n, overflowed) = unsafe { + ::std::intrinsics::mul_with_overflow::(left, right) + }; + + try!(self.memory.write_int(dest, n, size)); + try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); + } + "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.ty_size(pointee_ty) as isize; diff --git a/src/lib.rs b/src/lib.rs index 8646db3c222fb..d231e6f1a6b3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(btree_range, collections_bound, rustc_private)] +#![feature(btree_range, collections_bound, core_intrinsics, rustc_private)] // From rustc. extern crate arena; From 6a000b37cc468947d080c761b68a7a8da7cd29b3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 19 Mar 2016 11:07:19 -0600 Subject: [PATCH 0147/1096] Simplify Misc casts (still incorrect). --- src/interpreter.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3f710ba2e2b23..e244899318860 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -536,14 +536,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Misc => { - if pointee_type(src_ty).is_some() && pointee_type(dest_ty).is_some() { - // FIXME(tsion): Wrong for fat pointers. - self.memory.copy(src, dest, 8) - } else { - // FIXME(tsion): Wrong for almost everything. - self.memory.copy(src, dest, 8) - // panic!("can't handle cast: {:?}", rvalue); - } + // FIXME(tsion): Wrong for almost everything. + let size = dest_repr.size(); + self.memory.copy(src, dest, size) + // panic!("can't handle cast: {:?}", rvalue); } _ => panic!("can't handle cast: {:?}", rvalue), From 28ccc2bf658e8fbd7265703ce8d361c2a982fe03 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 18:41:39 -0600 Subject: [PATCH 0148/1096] Implement the __rust_allocate C ABI function. --- src/interpreter.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e244899318860..e5a593239cf6b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -14,6 +14,7 @@ use std::iter; use std::ops::Deref; use std::rc::Rc; use syntax::ast; +use syntax::attr; use syntax::codemap::DUMMY_SP; use error::{EvalError, EvalResult}; @@ -248,6 +249,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + Abi::C => + try!(self.call_c_abi(def_id, args, return_ptr.unwrap())), + Abi::Rust | Abi::RustCall => { // TODO(tsion): Adjust the first argument when calling a Fn or // FnMut closure via FnOnce::call_once. @@ -295,7 +299,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { TerminatorTarget::Call } - abi => panic!("can't handle function with ABI {:?}", abi), + abi => panic!("can't handle function with {:?} ABI", abi), } } @@ -397,6 +401,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.copy(src, dest, dest_size)); } + // TODO(tsion): Mark bytes as undef. "uninit" => {} name => panic!("can't handle intrinsic: {}", name), @@ -408,6 +413,34 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(TerminatorTarget::Call) } + fn call_c_abi(&mut self, def_id: DefId, args: &[mir::Operand<'tcx>], dest: Pointer) + -> EvalResult + { + let name = self.tcx.item_name(def_id); + let attrs = self.tcx.get_attrs(def_id); + let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { + Some(ln) => ln.clone(), + None => name.as_str(), + }; + + match &link_name[..] { + "__rust_allocate" => { + let size_arg = try!(self.eval_operand(&args[0])); + let _align_arg = try!(self.eval_operand(&args[1])); + let size = try!(self.memory.read_uint(size_arg, self.memory.pointer_size)); + let ptr = self.memory.allocate(size as usize); + try!(self.memory.write_ptr(dest, ptr)); + } + + _ => panic!("can't call C ABI function: {}", link_name), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(TerminatorTarget::Call) + } + fn assign_to_aggregate(&mut self, dest: Pointer, dest_repr: &Repr, variant: usize, operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { match *dest_repr { From 493b5f649c3de3fd511a39dd6bcf4ebce8553810 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 19:23:57 -0600 Subject: [PATCH 0149/1096] Implement the move_val_init intrinsic. --- src/interpreter.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index e5a593239cf6b..2b299e5c65aa2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -347,6 +347,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_int(dest, 1, dest_size)); } + "move_val_init" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty); + + let ptr_arg = try!(self.eval_operand(&args[0])); + let ptr = try!(self.memory.read_ptr(ptr_arg)); + + let val = try!(self.eval_operand(&args[1])); + try!(self.memory.copy(val, ptr, size)); + } + // FIXME(tsion): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); From 2e12b220be89a9253bb43ee19cb6de755add9c5f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 20:15:13 -0600 Subject: [PATCH 0150/1096] Stop unintentionally clearing source relocations when copying. --- src/memory.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index e5ce009d3731e..496c757beafb9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -106,25 +106,25 @@ impl Memory { } pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let (src_bytes, relocations) = { + let (src_bytes, mut relocations) = { let alloc = try!(self.get_mut(src.alloc_id)); try!(alloc.check_relocation_edges(src.offset, src.offset + size)); let bytes = alloc.bytes[src.offset..src.offset + size].as_mut_ptr(); - let mut relocations: Vec<(usize, AllocId)> = alloc.relocations + let relocations: Vec<(usize, AllocId)> = alloc.relocations .range(Included(&src.offset), Excluded(&(src.offset + size))) .map(|(&k, &v)| (k, v)) .collect(); - for &mut (ref mut offset, _) in &mut relocations { - alloc.relocations.remove(offset); - *offset += dest.offset; - *offset -= src.offset; - } - (bytes, relocations) }; + // Update relocation offsets for the new positions in the destination allocation. + for &mut (ref mut offset, _) in &mut relocations { + *offset += dest.offset; + *offset -= src.offset; + } + let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); // TODO(tsion): Clear the destination range's existing relocations. From c8781e3c01e5f4c27168b02ba97752ceafb296db Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 20:16:40 -0600 Subject: [PATCH 0151/1096] Support fn pointer type sizes. --- src/interpreter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index 2b299e5c65aa2..20722dced62f6 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -780,6 +780,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + ty::TyFnPtr(..) => Repr::Primitive { size: self.memory.pointer_size }, + ty::TyClosure(_, ref closure_substs) => self.make_aggregate_repr(iter::once(closure_substs.upvar_tys.iter().cloned())), From 2245a4b96d191cbf236c348447fd9a7a9eb1314a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 20:18:09 -0600 Subject: [PATCH 0152/1096] Add first test for std::vec::Vec. --- test/heap.rs | 5 ----- test/vecs.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100755 test/vecs.rs diff --git a/test/heap.rs b/test/heap.rs index c40165909df02..05efc56f0f33e 100755 --- a/test/heap.rs +++ b/test/heap.rs @@ -10,8 +10,3 @@ fn make_box() -> Box<(i16, i16)> { fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } - -// #[miri_run] -// fn make_vec() -> Vec { -// Vec::new() -// } diff --git a/test/vecs.rs b/test/vecs.rs new file mode 100755 index 0000000000000..e3cb67b1ada4b --- /dev/null +++ b/test/vecs.rs @@ -0,0 +1,15 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn make_vec() -> Vec { + let v = Vec::with_capacity(4); + v.push(1); + v.push(2); + v +} + +// #[miri_run] +// fn make_vec_macro() -> Vec { +// vec![1, 2] +// } From 62c5083f300f4be76d3655bcfd4945b2ac608e2e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 20:33:46 -0600 Subject: [PATCH 0153/1096] Reduce duplication for integer reprs. --- src/interpreter.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 20722dced62f6..188c0590e52ac 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -743,17 +743,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use syntax::ast::{IntTy, UintTy}; let repr = match ty.sty { ty::TyBool => Repr::Primitive { size: 1 }, - ty::TyInt(IntTy::Is) => Repr::Primitive { size: self.memory.pointer_size }, - ty::TyInt(IntTy::I8) => Repr::Primitive { size: 1 }, - ty::TyInt(IntTy::I16) => Repr::Primitive { size: 2 }, - ty::TyInt(IntTy::I32) => Repr::Primitive { size: 4 }, - ty::TyInt(IntTy::I64) => Repr::Primitive { size: 8 }, - - ty::TyUint(UintTy::Us) => Repr::Primitive { size: self.memory.pointer_size }, - ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, - ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, - ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, - ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, + + ty::TyInt(IntTy::I8) | ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, + ty::TyInt(IntTy::I16) | ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, + ty::TyInt(IntTy::I32) | ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, + ty::TyInt(IntTy::I64) | ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, + + ty::TyInt(IntTy::Is) | ty::TyUint(UintTy::Us) => + Repr::Primitive { size: self.memory.pointer_size }, ty::TyTuple(ref fields) => self.make_aggregate_repr(iter::once(fields.iter().cloned())), From 40d0a1f67fcde5ea3b24edefc1a4121cbde0fe62 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 21:30:31 -0600 Subject: [PATCH 0154/1096] Implement length access and indexing for fixed-sized arrays. --- src/interpreter.rs | 49 +++++++++++++++++++++++++++++++++++----------- test/arrays.rs | 8 +++++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 188c0590e52ac..3cc70a0627080 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -531,6 +531,20 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + Len(ref lvalue) => { + let ty = self.lvalue_ty(lvalue); + match ty.sty { + ty::TyArray(_, n) => { + let psize = self.memory.pointer_size; + self.memory.write_uint(dest, n as u64, psize) + } + ty::TySlice(_) => { + unimplemented!() + } + _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), + } + } + Ref(_, _, ref lvalue) => { let ptr = try!(self.eval_lvalue(lvalue)); self.memory.write_ptr(dest, ptr) @@ -635,16 +649,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - fn eval_lvalue(&self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { - let frame = self.current_frame(); - + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - ReturnPointer => - frame.return_ptr.expect("ReturnPointer used in a function with no return value"), - Arg(i) => frame.locals[i as usize], - Var(i) => frame.locals[frame.var_offset + i as usize], - Temp(i) => frame.locals[frame.temp_offset + i as usize], + ReturnPointer => self.current_frame().return_ptr + .expect("ReturnPointer used in a function with no return value"), + Arg(i) => self.current_frame().locals[i as usize], + Var(i) => self.current_frame().locals[self.current_frame().var_offset + i as usize], + Temp(i) => self.current_frame().locals[self.current_frame().temp_offset + i as usize], Projection(ref proj) => { let base_ptr = try!(self.eval_lvalue(&proj.base)); @@ -667,7 +679,19 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FIXME(tsion): Wrong for fat pointers. Deref => try!(self.memory.read_ptr(base_ptr)), - _ => unimplemented!(), + Index(ref operand) => { + let base_ty = self.lvalue_ty(&proj.base); + let elem_size = match base_ty.sty { + ty::TyArray(elem_ty, _) => self.ty_size(elem_ty), + ty::TySlice(elem_ty) => self.ty_size(elem_ty), + _ => panic!("indexing expected an array or slice, got {:?}", base_ty), + }; + let n_ptr = try!(self.eval_operand(operand)); + let n = try!(self.memory.read_uint(n_ptr, self.memory.pointer_size)); + base_ptr.offset(n as isize * elem_size as isize) + } + + ref p => panic!("can't handle lvalue projection: {:?}", p), } } @@ -720,6 +744,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> ty::Ty<'tcx> { + self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx) + } + fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { let substituted = ty.subst(self.tcx, self.current_substs()); infer::normalize_associated_type(self.tcx, &substituted) @@ -762,7 +790,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { self.make_aggregate_repr(variants) } - ty::TyArray(ref elem_ty, length) => Repr::Array { + ty::TyArray(elem_ty, length) => Repr::Array { elem_size: self.ty_size(elem_ty), length: length, }, @@ -823,7 +851,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { size: max_variant_size + discr_size, variants: variants, } - } pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { diff --git a/test/arrays.rs b/test/arrays.rs index 1add02561e38e..2939acfeaee15 100755 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -17,7 +17,13 @@ fn array_array() -> [[u8; 2]; 3] { } #[miri_run] -fn indexing() -> i32 { +fn index_unsafe() -> i32 { let a = [0, 10, 20, 30]; unsafe { *a.get_unchecked(2) } } + +#[miri_run] +fn index() -> i32 { + let a = [0, 10, 20, 30]; + a[2] +} From dbd01d071a7e86b90b57f2db01113b7f2f4f9204 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 22:07:25 -0600 Subject: [PATCH 0155/1096] Refactor some names. --- src/interpreter.rs | 34 +++++++++++++++++++--------------- src/memory.rs | 6 +++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3cc70a0627080..1bbc9c6668bce 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -117,11 +117,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } 'outer: while !self.stack.is_empty() { - let mut current_block = self.current_frame().next_block; + let mut current_block = self.frame().next_block; loop { print_trace(¤t_block, ":", self.stack.len()); - let current_mir = self.current_frame().mir.clone(); // Cloning a reference. + let current_mir = self.mir().clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { @@ -228,7 +228,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Call { ref func, ref args, ref destination, .. } => { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { - self.current_frame_mut().next_block = target; + self.frame_mut().next_block = target; return_ptr = Some(try!(self.eval_lvalue(lv))); } @@ -292,7 +292,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.push_stack_frame(mir, return_ptr)); for (i, (src, size)) in arg_srcs.into_iter().enumerate() { - let dest = self.current_frame().locals[i]; + let dest = self.frame().locals[i]; try!(self.memory.copy(src, dest, size)); } @@ -609,7 +609,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { - let ty = self.current_frame().mir.operand_ty(self.tcx, operand); + let ty = self.mir().operand_ty(self.tcx, operand); self.monomorphize(ty) } @@ -639,7 +639,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueRepr). fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> &'arena Repr { use rustc::mir::tcx::LvalueTy; - match self.current_frame().mir.lvalue_ty(self.tcx, lvalue) { + match self.mir().lvalue_ty(self.tcx, lvalue) { LvalueTy::Ty { ty } => self.ty_to_repr(ty), LvalueTy::Downcast { ref adt_def, substs, variant_index } => { let field_tys = adt_def.variants[variant_index].fields.iter() @@ -652,11 +652,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - ReturnPointer => self.current_frame().return_ptr + ReturnPointer => self.frame().return_ptr .expect("ReturnPointer used in a function with no return value"), - Arg(i) => self.current_frame().locals[i as usize], - Var(i) => self.current_frame().locals[self.current_frame().var_offset + i as usize], - Temp(i) => self.current_frame().locals[self.current_frame().temp_offset + i as usize], + Arg(i) => self.frame().locals[i as usize], + Var(i) => self.frame().locals[self.frame().var_offset + i as usize], + Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], Projection(ref proj) => { let base_ptr = try!(self.eval_lvalue(&proj.base)); @@ -745,11 +745,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> ty::Ty<'tcx> { - self.current_frame().mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx) + self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx) } fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { - let substituted = ty.subst(self.tcx, self.current_substs()); + let substituted = ty.subst(self.tcx, self.substs()); infer::normalize_associated_type(self.tcx, &substituted) } @@ -893,15 +893,19 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(val) } - fn current_frame(&self) -> &Frame<'a, 'tcx> { + fn frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } - fn current_frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { + fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn current_substs(&self) -> &'tcx Substs<'tcx> { + fn mir(&self) -> &mir::Mir<'tcx> { + &self.frame().mir + } + + fn substs(&self) -> &'tcx Substs<'tcx> { self.substs_stack.last().cloned().unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())) } diff --git a/src/memory.rs b/src/memory.rs index 496c757beafb9..ab5f56920cfec 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -23,19 +23,19 @@ pub struct Allocation { // TODO(tsion): undef mask } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Pointer { pub alloc_id: AllocId, pub offset: usize, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct FieldRepr { pub offset: usize, pub size: usize, } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum Repr { /// Representation for a non-aggregate type such as a boolean, integer, character or pointer. Primitive { From f7d0e0423b3874e348dcf322342c29f31f5f98d9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 22:59:13 -0600 Subject: [PATCH 0156/1096] Support fat pointer reborrowing, length checking. --- src/interpreter.rs | 107 ++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 1bbc9c6668bce..c6f840f71c24b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -74,6 +74,19 @@ struct Frame<'a, 'tcx: 'a> { temp_offset: usize, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct Lvalue { + ptr: Pointer, + extra: LvalueExtra, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum LvalueExtra { + None, + Length(u64), + // Vtable(memory::AllocId), +} + #[derive(Clone)] enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), @@ -195,7 +208,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = try!(self.eval_lvalue(discr)); + let discr_ptr = try!(self.eval_lvalue(discr)).ptr; let discr_size = self.lvalue_repr(discr).size(); let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); @@ -215,7 +228,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Switch { ref discr, ref targets, .. } => { - let adt_ptr = try!(self.eval_lvalue(discr)); + let adt_ptr = try!(self.eval_lvalue(discr)).ptr; let adt_repr = self.lvalue_repr(discr); let discr_size = match *adt_repr { Repr::Aggregate { discr_size, .. } => discr_size, @@ -229,7 +242,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { self.frame_mut().next_block = target; - return_ptr = Some(try!(self.eval_lvalue(lv))); + return_ptr = Some(try!(self.eval_lvalue(lv)).ptr); } let func_ty = self.operand_ty(func); @@ -475,7 +488,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { - let dest = try!(self.eval_lvalue(lvalue)); + let dest = try!(self.eval_lvalue(lvalue)).ptr; let dest_repr = self.lvalue_repr(lvalue); use rustc::mir::repr::Rvalue::*; @@ -532,22 +545,33 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Len(ref lvalue) => { + let src = try!(self.eval_lvalue(lvalue)); let ty = self.lvalue_ty(lvalue); - match ty.sty { - ty::TyArray(_, n) => { - let psize = self.memory.pointer_size; - self.memory.write_uint(dest, n as u64, psize) - } - ty::TySlice(_) => { - unimplemented!() - } + let len = match ty.sty { + ty::TyArray(_, n) => n as u64, + ty::TySlice(_) => if let LvalueExtra::Length(n) = src.extra { + n + } else { + panic!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); + }, _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), - } + }; + let psize = self.memory.pointer_size; + self.memory.write_uint(dest, len, psize) } Ref(_, _, ref lvalue) => { - let ptr = try!(self.eval_lvalue(lvalue)); - self.memory.write_ptr(dest, ptr) + let lv = try!(self.eval_lvalue(lvalue)); + try!(self.memory.write_ptr(dest, lv.ptr)); + match lv.extra { + LvalueExtra::None => {}, + LvalueExtra::Length(len) => { + let psize = self.memory.pointer_size; + let len_ptr = dest.offset(psize as isize); + try!(self.memory.write_uint(len_ptr, len, psize)); + } + } + Ok(()) } Box(ty) => { @@ -557,18 +581,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Cast(kind, ref operand, dest_ty) => { - fn pointee_type<'tcx>(ptr_ty: ty::Ty<'tcx>) -> Option> { - match ptr_ty.sty { - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyBox(ty) => { - Some(ty) - } - - _ => None, - } - } - let src = try!(self.eval_operand(operand)); let src_ty = self.operand_ty(operand); @@ -597,7 +609,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FIXME(tsion): Wrong for almost everything. let size = dest_repr.size(); self.memory.copy(src, dest, size) - // panic!("can't handle cast: {:?}", rvalue); } _ => panic!("can't handle cast: {:?}", rvalue), @@ -622,7 +633,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok((try!(self.eval_lvalue(lvalue)), self.lvalue_repr(lvalue))), + Consume(ref lvalue) => + Ok((try!(self.eval_lvalue(lvalue)).ptr, self.lvalue_repr(lvalue))), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { @@ -649,7 +661,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { ReturnPointer => self.frame().return_ptr @@ -659,8 +671,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], Projection(ref proj) => { - let base_ptr = try!(self.eval_lvalue(&proj.base)); + let base_ptr = try!(self.eval_lvalue(&proj.base)).ptr; let base_repr = self.lvalue_repr(&proj.base); + let base_ty = self.lvalue_ty(&proj.base); use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, _) => match *base_repr { @@ -676,11 +689,24 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), }, - // FIXME(tsion): Wrong for fat pointers. - Deref => try!(self.memory.read_ptr(base_ptr)), + Deref => { + let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); + println!("{:?}", pointee_ty); + let ptr = try!(self.memory.read_ptr(base_ptr)); + let extra = match pointee_ty.sty { + ty::TySlice(_) => { + let psize = self.memory.pointer_size; + let len_ptr = base_ptr.offset(psize as isize); + let len = try!(self.memory.read_uint(len_ptr, psize)); + LvalueExtra::Length(len) + } + ty::TyTrait(_) => unimplemented!(), + _ => LvalueExtra::None, + }; + return Ok(Lvalue { ptr: ptr, extra: extra }); + } Index(ref operand) => { - let base_ty = self.lvalue_ty(&proj.base); let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) => self.ty_size(elem_ty), ty::TySlice(elem_ty) => self.ty_size(elem_ty), @@ -698,7 +724,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ref l => panic!("can't handle lvalue: {:?}", l), }; - Ok(ptr) + Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } // TODO(tsion): Try making const_to_primval instead. @@ -1004,6 +1030,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } +fn pointee_type<'tcx>(ptr_ty: ty::Ty<'tcx>) -> Option> { + match ptr_ty.sty { + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyBox(ty) => { + Some(ty) + } + _ => None, + } +} + impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { From 49e8d3ef5e9a55c97eea8534311ff2e11149fa30 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 23:09:27 -0600 Subject: [PATCH 0157/1096] Simplify handling of Result in eval_assignment. --- src/interpreter.rs | 51 +++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c6f840f71c24b..218bc4622debe 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -495,7 +495,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match *rvalue { Use(ref operand) => { let src = try!(self.eval_operand(operand)); - self.memory.copy(src, dest, dest_repr.size()) + try!(self.memory.copy(src, dest, dest_repr.size())); } BinaryOp(bin_op, ref left, ref right) => { @@ -508,39 +508,36 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let right_val = try!(self.read_primval(right_ptr, right_ty)); let val = try!(primval::binary_op(bin_op, left_val, right_val)); - self.memory.write_primval(dest, val) + try!(self.memory.write_primval(dest, val)); } UnaryOp(un_op, ref operand) => { let ptr = try!(self.eval_operand(operand)); let ty = self.operand_ty(operand); let val = try!(self.read_primval(ptr, ty)); - self.memory.write_primval(dest, primval::unary_op(un_op, val)) + try!(self.memory.write_primval(dest, primval::unary_op(un_op, val))); } Aggregate(ref kind, ref operands) => { use rustc::mir::repr::AggregateKind::*; match *kind { - Tuple => self.assign_to_aggregate(dest, &dest_repr, 0, operands), + Tuple | Closure(..) => + try!(self.assign_to_aggregate(dest, &dest_repr, 0, operands)), Adt(_, variant_idx, _) => - self.assign_to_aggregate(dest, &dest_repr, variant_idx, operands), - - Vec => match *dest_repr { - Repr::Array { elem_size, length } => { - assert_eq!(length, operands.len()); - for (i, operand) in operands.iter().enumerate() { - let src = try!(self.eval_operand(operand)); - let offset = i * elem_size; - let elem_dest = dest.offset(offset as isize); - try!(self.memory.copy(src, elem_dest, elem_size)); - } - Ok(()) + try!(self.assign_to_aggregate(dest, &dest_repr, variant_idx, operands)), + + Vec => if let Repr::Array { elem_size, length } = *dest_repr { + assert_eq!(length, operands.len()); + for (i, operand) in operands.iter().enumerate() { + let src = try!(self.eval_operand(operand)); + let offset = i * elem_size; + let elem_dest = dest.offset(offset as isize); + try!(self.memory.copy(src, elem_dest, elem_size)); } - _ => panic!("expected Repr::Array target"), + } else { + panic!("expected Repr::Array target"); }, - - Closure(..) => self.assign_to_aggregate(dest, &dest_repr, 0, operands), } } @@ -557,7 +554,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), }; let psize = self.memory.pointer_size; - self.memory.write_uint(dest, len, psize) + try!(self.memory.write_uint(dest, len, psize)); } Ref(_, _, ref lvalue) => { @@ -571,13 +568,12 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(len_ptr, len, psize)); } } - Ok(()) } Box(ty) => { let size = self.ty_size(ty); let ptr = self.memory.allocate(size); - self.memory.write_ptr(dest, ptr) + try!(self.memory.write_ptr(dest, ptr)); } Cast(kind, ref operand, dest_ty) => { @@ -594,11 +590,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let size = self.memory.pointer_size; - self.memory.write_uint( - dest.offset(size as isize), - length as u64, - size, - ) + let len_ptr = dest.offset(size as isize); + try!(self.memory.write_uint(len_ptr, length as u64, size)); } _ => panic!("can't handle cast: {:?}", rvalue), @@ -608,7 +601,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Misc => { // FIXME(tsion): Wrong for almost everything. let size = dest_repr.size(); - self.memory.copy(src, dest, size) + try!(self.memory.copy(src, dest, size)); } _ => panic!("can't handle cast: {:?}", rvalue), @@ -617,6 +610,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ref r => panic!("can't handle rvalue: {:?}", r), } + + Ok(()) } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { From 95e225d76580d6180e55d3e0cb21eeb1f2d2a4d7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 23:11:06 -0600 Subject: [PATCH 0158/1096] Monomorphize lvalue types. --- src/interpreter.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 218bc4622debe..5609dba58145e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -614,11 +614,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(()) } - fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { - let ty = self.mir().operand_ty(self.tcx, operand); - self.monomorphize(ty) - } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { self.eval_operand_and_repr(op).map(|(p, _)| p) } @@ -766,7 +761,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> ty::Ty<'tcx> { - self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx) + self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx)) + } + + fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { + self.monomorphize(self.mir().operand_ty(self.tcx, operand)) } fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { From 0de1bbefd5c169f33fba97d43cb2bd27acce0f50 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 20 Mar 2016 23:24:27 -0600 Subject: [PATCH 0159/1096] Refactor isize/usize read/writes. --- src/interpreter.rs | 41 +++++++++++++++++------------------------ src/memory.rs | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5609dba58145e..839e76b710f7b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -348,7 +348,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let src = try!(self.memory.read_ptr(src_arg)); let dest = try!(self.memory.read_ptr(dest_arg)); - let count = try!(self.memory.read_int(count_arg, self.memory.pointer_size)); + let count = try!(self.memory.read_isize(count_arg)); try!(self.memory.copy(src, dest, count as usize * elem_size)); } @@ -397,7 +397,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); - let offset = try!(self.memory.read_int(offset_arg, self.memory.pointer_size)); + let offset = try!(self.memory.read_isize(offset_arg)); match self.memory.read_ptr(ptr_arg) { Ok(ptr) => { @@ -405,10 +405,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_ptr(dest, result_ptr)); } Err(EvalError::ReadBytesAsPointer) => { - let psize = self.memory.pointer_size; - let addr = try!(self.memory.read_int(ptr_arg, psize)); + let addr = try!(self.memory.read_isize(ptr_arg)); let result_addr = addr + offset * pointee_size as i64; - try!(self.memory.write_int(dest, result_addr, psize)); + try!(self.memory.write_isize(dest, result_addr)); } Err(e) => return Err(e), } @@ -451,7 +450,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "__rust_allocate" => { let size_arg = try!(self.eval_operand(&args[0])); let _align_arg = try!(self.eval_operand(&args[1])); - let size = try!(self.memory.read_uint(size_arg, self.memory.pointer_size)); + let size = try!(self.memory.read_usize(size_arg)); let ptr = self.memory.allocate(size as usize); try!(self.memory.write_ptr(dest, ptr)); } @@ -553,8 +552,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { }, _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), }; - let psize = self.memory.pointer_size; - try!(self.memory.write_uint(dest, len, psize)); + try!(self.memory.write_usize(dest, len)); } Ref(_, _, ref lvalue) => { @@ -563,9 +561,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { - let psize = self.memory.pointer_size; - let len_ptr = dest.offset(psize as isize); - try!(self.memory.write_uint(len_ptr, len, psize)); + let len_ptr = dest.offset(self.memory.pointer_size as isize); + try!(self.memory.write_usize(len_ptr, len)); } } } @@ -589,9 +586,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let size = self.memory.pointer_size; - let len_ptr = dest.offset(size as isize); - try!(self.memory.write_uint(len_ptr, length as u64, size)); + let len_ptr = dest.offset(self.memory.pointer_size as isize); + try!(self.memory.write_usize(len_ptr, length as u64)); } _ => panic!("can't handle cast: {:?}", rvalue), @@ -685,9 +681,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let ptr = try!(self.memory.read_ptr(base_ptr)); let extra = match pointee_ty.sty { ty::TySlice(_) => { - let psize = self.memory.pointer_size; - let len_ptr = base_ptr.offset(psize as isize); - let len = try!(self.memory.read_uint(len_ptr, psize)); + let len_ptr = base_ptr.offset(self.memory.pointer_size as isize); + let len = try!(self.memory.read_usize(len_ptr)); LvalueExtra::Length(len) } ty::TyTrait(_) => unimplemented!(), @@ -703,7 +698,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = try!(self.eval_operand(operand)); - let n = try!(self.memory.read_uint(n_ptr, self.memory.pointer_size)); + let n = try!(self.memory.read_usize(n_ptr)); base_ptr.offset(n as isize * elem_size as isize) } @@ -734,7 +729,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let ptr = self.memory.allocate(psize * 2); try!(self.memory.write_bytes(static_ptr, s.as_bytes())); try!(self.memory.write_ptr(ptr, static_ptr)); - try!(self.memory.write_uint(ptr.offset(psize as isize), s.len() as u64, psize)); + try!(self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)); Ok(ptr) } ByteStr(ref bs) => { @@ -887,10 +882,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ty::TyUint(UintTy::U64) => PrimVal::U64(try!(self.memory.read_uint(ptr, 8)) as u64), // TODO(tsion): Pick the PrimVal dynamically. - ty::TyInt(IntTy::Is) => - PrimVal::I64(try!(self.memory.read_int(ptr, self.memory.pointer_size))), - ty::TyUint(UintTy::Us) => - PrimVal::U64(try!(self.memory.read_uint(ptr, self.memory.pointer_size))), + ty::TyInt(IntTy::Is) => PrimVal::I64(try!(self.memory.read_isize(ptr))), + ty::TyUint(UintTy::Us) => PrimVal::U64(try!(self.memory.read_usize(ptr))), ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { @@ -898,7 +891,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match self.memory.read_ptr(ptr) { Ok(p) => PrimVal::AbstractPtr(p), Err(EvalError::ReadBytesAsPointer) => { - let n = try!(self.memory.read_uint(ptr, self.memory.pointer_size)); + let n = try!(self.memory.read_usize(ptr)); PrimVal::IntegerPtr(n) } Err(e) => return Err(e), diff --git a/src/memory.rs b/src/memory.rs index ab5f56920cfec..3ac5688c19093 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -215,6 +215,24 @@ impl Memory { pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<()> { self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::(n, size).unwrap()) } + + pub fn read_isize(&self, ptr: Pointer) -> EvalResult { + self.read_int(ptr, self.pointer_size) + } + + pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { + let size = self.pointer_size; + self.write_int(ptr, n, size) + } + + pub fn read_usize(&self, ptr: Pointer) -> EvalResult { + self.read_uint(ptr, self.pointer_size) + } + + pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<()> { + let size = self.pointer_size; + self.write_uint(ptr, n, size) + } } impl Allocation { From 2994732a81b9f5803dd154d4e24b6024f6c6e851 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 01:04:50 -0600 Subject: [PATCH 0160/1096] Remove debug print. --- src/interpreter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 839e76b710f7b..3706a802566c9 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -677,7 +677,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - println!("{:?}", pointee_ty); let ptr = try!(self.memory.read_ptr(base_ptr)); let extra = match pointee_ty.sty { ty::TySlice(_) => { From 207463d9a0cbdbc4afd2cf11899f71a7cbd047f4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 02:37:31 -0600 Subject: [PATCH 0161/1096] Add array indexing for-loop test. --- src/memory.rs | 51 ++++++++++++++++++++++++++++---------------------- test/arrays.rs | 10 ++++++++++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 3ac5688c19093..cd0ee4f687892 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -100,36 +100,44 @@ impl Memory { } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { + try!(self.clear_relocations(ptr, size)); let alloc = try!(self.get_mut(ptr.alloc_id)); - try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size)); Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } + fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + let start = ptr.offset.saturating_sub(self.pointer_size - 1); + let end = ptr.offset + size; + let alloc = try!(self.get_mut(ptr.alloc_id)); + let keys: Vec<_> = alloc.relocations + .range(Included(&start), Excluded(&end)) + .map(|(&k, _)| k) + .collect(); + for k in keys { + alloc.relocations.remove(&k); + } + Ok(()) + } + + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + let relocations: Vec<_> = try!(self.get_mut(src.alloc_id)).relocations + .range(Included(&src.offset), Excluded(&(src.offset + size))) + .map(|(&offset, &alloc_id)| { + // Update relocation offsets for the new positions in the destination allocation. + (offset + dest.offset - src.offset, alloc_id) + }).collect(); + try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); + Ok(()) + } + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let (src_bytes, mut relocations) = { + let src_bytes = { let alloc = try!(self.get_mut(src.alloc_id)); try!(alloc.check_relocation_edges(src.offset, src.offset + size)); - let bytes = alloc.bytes[src.offset..src.offset + size].as_mut_ptr(); - - let relocations: Vec<(usize, AllocId)> = alloc.relocations - .range(Included(&src.offset), Excluded(&(src.offset + size))) - .map(|(&k, &v)| (k, v)) - .collect(); - - (bytes, relocations) + alloc.bytes[src.offset..src.offset + size].as_mut_ptr() }; - - // Update relocation offsets for the new positions in the destination allocation. - for &mut (ref mut offset, _) in &mut relocations { - *offset += dest.offset; - *offset -= src.offset; - } - let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); - // TODO(tsion): Clear the destination range's existing relocations. - try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); - // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and // `dest` could possibly overlap. @@ -141,7 +149,7 @@ impl Memory { } } - Ok(()) + self.copy_relocations(src, dest, size) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { @@ -160,7 +168,6 @@ impl Memory { } } - // TODO(tsion): Detect invalid writes here and elsewhere. pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> { { let size = self.pointer_size; diff --git a/test/arrays.rs b/test/arrays.rs index 2939acfeaee15..cd4c91c220a0e 100755 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -27,3 +27,13 @@ fn index() -> i32 { let a = [0, 10, 20, 30]; a[2] } + +#[miri_run] +fn index_for_loop() -> usize { + let mut sum = 0; + let a = [0, 10, 20, 30]; + for i in 0..a.len() { + sum += a[i]; + } + sum +} From 6f2e50caea91426f4baac282905a3ba5d64adcbd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 02:39:41 -0600 Subject: [PATCH 0162/1096] Add slice iterator for-loop test. --- test/arrays.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/arrays.rs b/test/arrays.rs index cd4c91c220a0e..572fabe112f24 100755 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -37,3 +37,13 @@ fn index_for_loop() -> usize { } sum } + +#[miri_run] +fn for_loop() -> usize { + let mut sum = 0; + let a = [0, 10, 20, 30]; + for &n in &a { + sum += n; + } + sum +} From 27e82b60b08e96076d36825c082bf6a3c2842bce Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 02:41:07 -0600 Subject: [PATCH 0163/1096] Fix vec test compile error. --- test/vecs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vecs.rs b/test/vecs.rs index e3cb67b1ada4b..2fb113b3da208 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -3,7 +3,7 @@ #[miri_run] fn make_vec() -> Vec { - let v = Vec::with_capacity(4); + let mut v = Vec::with_capacity(4); v.push(1); v.push(2); v From f439a97cf43e829d1fbc198ba517661c3f47cf84 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 02:41:22 -0600 Subject: [PATCH 0164/1096] Uncomment now-working vec! macro test. --- test/vecs.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/vecs.rs b/test/vecs.rs index 2fb113b3da208..4b35263435fa5 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -9,7 +9,7 @@ fn make_vec() -> Vec { v } -// #[miri_run] -// fn make_vec_macro() -> Vec { -// vec![1, 2] -// } +#[miri_run] +fn make_vec_macro() -> Vec { + vec![1, 2] +} From 877d2d900e8acb64fa72b6e8308c399881fa1f62 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 02:54:20 -0600 Subject: [PATCH 0165/1096] Abort miri if the Rust code had compilation errors. --- src/bin/miri.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 7867526f26d42..8efa1a6864def 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -16,6 +16,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(|state| { + state.session.abort_if_errors(); interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); }); From 936537ea84934469a82ce768510966fd95f7977b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 03:19:07 -0600 Subject: [PATCH 0166/1096] Add vec::IntoIter and fold test. --- test/vecs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/vecs.rs b/test/vecs.rs index 4b35263435fa5..53ca55aac1dc1 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -13,3 +13,8 @@ fn make_vec() -> Vec { fn make_vec_macro() -> Vec { vec![1, 2] } + +#[miri_run] +fn vec_int_iter() -> i32 { + vec![1, 2, 3, 4].into_iter().fold(0, |x, y| x + y) +} From 6eef7cc01aa1f24c3a2462123cb4590a641bcd4c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 03:19:48 -0600 Subject: [PATCH 0167/1096] Make mir::Rvalue handling exhaustive (some still unimplemented). --- src/interpreter.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 3706a802566c9..8584b1af3cfbb 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -540,6 +540,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + Repeat(_, _) => unimplemented!(), + Len(ref lvalue) => { let src = try!(self.eval_lvalue(lvalue)); let ty = self.lvalue_ty(lvalue); @@ -604,7 +606,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - ref r => panic!("can't handle rvalue: {:?}", r), + Slice { .. } => unimplemented!(), + InlineAsm(_) => unimplemented!(), } Ok(()) From dc5fbf17ca6f0f4c9485e2b5f78af0e7d769e23a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 03:34:24 -0600 Subject: [PATCH 0168/1096] Support [x; N] array repeat rvalues. --- src/interpreter.rs | 15 ++++++++++++--- test/arrays.rs | 19 ++----------------- test/loops.rs | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8584b1af3cfbb..9b3312c224dfe 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -530,8 +530,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { assert_eq!(length, operands.len()); for (i, operand) in operands.iter().enumerate() { let src = try!(self.eval_operand(operand)); - let offset = i * elem_size; - let elem_dest = dest.offset(offset as isize); + let elem_dest = dest.offset((i * elem_size) as isize); try!(self.memory.copy(src, elem_dest, elem_size)); } } else { @@ -540,7 +539,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - Repeat(_, _) => unimplemented!(), + Repeat(ref operand, _) => { + if let Repr::Array { elem_size, length } = *dest_repr { + let src = try!(self.eval_operand(operand)); + for i in 0..length { + let elem_dest = dest.offset((i * elem_size) as isize); + try!(self.memory.copy(src, elem_dest, elem_size)); + } + } else { + panic!("expected Repr::Array target"); + } + } Len(ref lvalue) => { let src = try!(self.eval_lvalue(lvalue)); diff --git a/test/arrays.rs b/test/arrays.rs index 572fabe112f24..835e09780fc48 100755 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -29,21 +29,6 @@ fn index() -> i32 { } #[miri_run] -fn index_for_loop() -> usize { - let mut sum = 0; - let a = [0, 10, 20, 30]; - for i in 0..a.len() { - sum += a[i]; - } - sum -} - -#[miri_run] -fn for_loop() -> usize { - let mut sum = 0; - let a = [0, 10, 20, 30]; - for &n in &a { - sum += n; - } - sum +fn array_repeat() -> [u8; 8] { + [42; 8] } diff --git a/test/loops.rs b/test/loops.rs index 1cb8d464f79c1..b59c813a423e0 100644 --- a/test/loops.rs +++ b/test/loops.rs @@ -13,3 +13,23 @@ fn factorial_loop() -> i64 { product } + +#[miri_run] +fn index_for_loop() -> usize { + let mut sum = 0; + let a = [0, 10, 20, 30]; + for i in 0..a.len() { + sum += a[i]; + } + sum +} + +#[miri_run] +fn for_loop() -> usize { + let mut sum = 0; + let a = [0, 10, 20, 30]; + for &n in &a { + sum += n; + } + sum +} From 69f41facb94a1a848dd87e9a2ccfd5b12c03e98d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 03:42:34 -0600 Subject: [PATCH 0169/1096] Support intrinsics::overflowing_sub for vec![x; n]. --- src/interpreter.rs | 15 +++++++++++++++ test/vecs.rs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/interpreter.rs b/src/interpreter.rs index 9b3312c224dfe..affd83bb07742 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -413,6 +413,21 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + // FIXME(tsion): Handle different integer types correctly. Use primvals? + "overflowing_sub" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty); + + let left_arg = try!(self.eval_operand(&args[0])); + let right_arg = try!(self.eval_operand(&args[1])); + + let left = try!(self.memory.read_int(left_arg, size)); + let right = try!(self.memory.read_int(right_arg, size)); + + let n = left.wrapping_sub(right); + try!(self.memory.write_int(dest, n, size)); + } + "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.ty_size(ty) as u64; diff --git a/test/vecs.rs b/test/vecs.rs index 53ca55aac1dc1..763a1153ce922 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -14,6 +14,11 @@ fn make_vec_macro() -> Vec { vec![1, 2] } +#[miri_run] +fn make_vec_macro_repeat() -> Vec { + vec![42; 8] +} + #[miri_run] fn vec_int_iter() -> i32 { vec![1, 2, 3, 4].into_iter().fold(0, |x, y| x + y) From 3fc9b7dd2c94968faedca8efbf3f313d38da68b2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 04:01:52 -0600 Subject: [PATCH 0170/1096] Make more MIR match statements exhaustive. --- src/interpreter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index affd83bb07742..e5c45d99961d8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -655,7 +655,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.const_to_ptr(value)), self.ty_to_repr(ty), )), - ref l => panic!("can't handle item literal: {:?}", l), + Item { .. } => unimplemented!(), } } } @@ -683,6 +683,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], + Static(_def_id) => unimplemented!(), + Projection(ref proj) => { let base_ptr = try!(self.eval_lvalue(&proj.base)).ptr; let base_repr = self.lvalue_repr(&proj.base); @@ -728,11 +730,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { base_ptr.offset(n as isize * elem_size as isize) } - ref p => panic!("can't handle lvalue projection: {:?}", p), + ConstantIndex { .. } => unimplemented!(), } } - - ref l => panic!("can't handle lvalue: {:?}", l), }; Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) From 11c78fbdc8289e31b11a4ebda9f4930794de16bb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 04:12:07 -0600 Subject: [PATCH 0171/1096] Fix typo in test name. --- test/vecs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vecs.rs b/test/vecs.rs index 763a1153ce922..b2f2f27ceeabf 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -20,6 +20,6 @@ fn make_vec_macro_repeat() -> Vec { } #[miri_run] -fn vec_int_iter() -> i32 { +fn vec_into_iter() -> i32 { vec![1, 2, 3, 4].into_iter().fold(0, |x, y| x + y) } From 3cb200a2bacc1dc9cb83cf065235d28c2e2681a9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 04:18:30 -0600 Subject: [PATCH 0172/1096] Add tests involving Rc, Arc, and Cell. --- test/std.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/std.rs diff --git a/test/std.rs b/test/std.rs new file mode 100644 index 0000000000000..916a85bc1a573 --- /dev/null +++ b/test/std.rs @@ -0,0 +1,19 @@ +#![feature(custom_attribute, box_syntax)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn rc_cell() -> i32 { + use std::rc::Rc; + use std::cell::Cell; + let r = Rc::new(Cell::new(42)); + let x = r.get(); + r.set(x + x); + r.get() +} + +#[miri_run] +fn arc() -> i32 { + use std::sync::Arc; + let a = Arc::new(42); + *a +} From 59e25cc52cb5ae4c51f6378b7ffb9db1d5588da6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 04:19:27 -0600 Subject: [PATCH 0173/1096] Hide execution traces (they are getting long...) --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e5c45d99961d8..ec63e2dd0b957 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -21,7 +21,7 @@ use error::{EvalError, EvalResult}; use memory::{self, FieldRepr, Memory, Pointer, Repr}; use primval::{self, PrimVal}; -const TRACE_EXECUTION: bool = true; +const TRACE_EXECUTION: bool = false; struct Interpreter<'a, 'tcx: 'a, 'arena> { /// The results of the type checker, from rustc. From 5b0164b0fa950e384e91c807e79df4b51a4bcd50 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 04:37:28 -0600 Subject: [PATCH 0174/1096] Add simple test of assert_eq!. --- test/std.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/std.rs b/test/std.rs index 916a85bc1a573..fec5845858581 100644 --- a/test/std.rs +++ b/test/std.rs @@ -17,3 +17,8 @@ fn arc() -> i32 { let a = Arc::new(42); *a } + +#[miri_run] +fn true_assert() { + assert_eq!(1, 1); +} From 600ff26e659b306a36788b67de6e9c6e6e774ae7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 05:27:34 -0600 Subject: [PATCH 0175/1096] Refactor handling of relocations. --- src/lib.rs | 2 +- src/memory.rs | 104 ++++++++++++++++++++++++++------------------------ 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d231e6f1a6b3d..ebbeff6d95d41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(btree_range, collections_bound, core_intrinsics, rustc_private)] +#![feature(btree_range, collections, collections_bound, core_intrinsics, rustc_private)] // From rustc. extern crate arena; diff --git a/src/memory.rs b/src/memory.rs index cd0ee4f687892..cff4cc35c84e0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,5 @@ -use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; -use std::collections::{BTreeMap, HashMap}; +use byteorder::{ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; +use std::collections::{btree_map, BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; use std::mem; use std::ptr; @@ -94,25 +94,46 @@ impl Memory { } fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { - let alloc = try!(self.get(ptr.alloc_id)); - try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size)); - Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) + try!(self.check_readable_bytes(ptr, size)); + try!(self.get(ptr.alloc_id)).checked_slice(ptr.offset, size) } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { try!(self.clear_relocations(ptr, size)); - let alloc = try!(self.get_mut(ptr.alloc_id)); - Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) + try!(self.get_mut(ptr.alloc_id)).checked_slice_mut(ptr.offset, size) } - fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + fn check_readable_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + if try!(self.relocations(ptr, size)).count() == 0 { + // TODO(tsion): Track and check for undef bytes. + Ok(()) + } else { + Err(EvalError::ReadPointerAsBytes) + } + } + + fn relocations(&self, ptr: Pointer, size: usize) + -> EvalResult> + { let start = ptr.offset.saturating_sub(self.pointer_size - 1); let end = ptr.offset + size; + let alloc = try!(self.get(ptr.alloc_id)); + Ok(alloc.relocations.range(Included(&start), Excluded(&end))) + } + + fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + let overlapping_start = try!(self.relocations(ptr, 0)).count(); + let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); + if overlapping_start + overlapping_end == 0 { + Ok(()) + } else { + Err(EvalError::ReadPointerAsBytes) + } + } + + fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); let alloc = try!(self.get_mut(ptr.alloc_id)); - let keys: Vec<_> = alloc.relocations - .range(Included(&start), Excluded(&end)) - .map(|(&k, _)| k) - .collect(); for k in keys { alloc.relocations.remove(&k); } @@ -131,10 +152,12 @@ impl Memory { } pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + // TODO(tsion): Track and check for undef bytes. + try!(self.check_relocation_edges(src, size)); + let src_bytes = { - let alloc = try!(self.get_mut(src.alloc_id)); - try!(alloc.check_relocation_edges(src.offset, src.offset + size)); - alloc.bytes[src.offset..src.offset + size].as_mut_ptr() + let alloc = try!(self.get(src.alloc_id)); + try!(alloc.checked_slice(src.offset, size)).as_ptr() }; let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); @@ -158,9 +181,8 @@ impl Memory { pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { let alloc = try!(self.get(ptr.alloc_id)); - try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + self.pointer_size)); - let bytes = &alloc.bytes[ptr.offset..ptr.offset + self.pointer_size]; - let offset = byteorder::NativeEndian::read_u64(bytes) as usize; + let mut bytes = try!(alloc.checked_slice(ptr.offset, self.pointer_size)); + let offset = bytes.read_uint::(self.pointer_size).unwrap() as usize; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), @@ -168,14 +190,13 @@ impl Memory { } } - pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> { + pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<()> { { let size = self.pointer_size; - let bytes = try!(self.get_bytes_mut(dest, size)); - byteorder::NativeEndian::write_u64(bytes, ptr_val.offset as u64); + let mut bytes = try!(self.get_bytes_mut(dest, size)); + bytes.write_uint::(ptr.offset as u64, size).unwrap(); } - let alloc = try!(self.get_mut(dest.alloc_id)); - alloc.relocations.insert(dest.offset, ptr_val.alloc_id); + try!(self.get_mut(dest.alloc_id)).relocations.insert(dest.offset, ptr.alloc_id); Ok(()) } @@ -243,40 +264,23 @@ impl Memory { } impl Allocation { - fn check_bounds(&self, start: usize, end: usize) -> EvalResult<()> { + fn checked_slice(&self, offset: usize, size: usize) -> EvalResult<&[u8]> { + let start = offset; + let end = start + size; if start <= self.bytes.len() && end <= self.bytes.len() { - Ok(()) + Ok(&self.bytes[start..end]) } else { Err(EvalError::PointerOutOfBounds) } } - fn count_overlapping_relocations(&self, start: usize, end: usize) -> usize { - self.relocations.range( - // FIXME(tsion): Assuming pointer size is 8. Move this method to Memory. - Included(&start.saturating_sub(8 - 1)), - Excluded(&end) - ).count() - } - - fn check_relocation_edges(&self, start: usize, end: usize) -> EvalResult<()> { - try!(self.check_bounds(start, end)); - let n = - self.count_overlapping_relocations(start, start) + - self.count_overlapping_relocations(end, end); - if n == 0 { - Ok(()) - } else { - Err(EvalError::ReadPointerAsBytes) - } - } - - fn check_no_relocations(&self, start: usize, end: usize) -> EvalResult<()> { - try!(self.check_bounds(start, end)); - if self.count_overlapping_relocations(start, end) == 0 { - Ok(()) + fn checked_slice_mut(&mut self, offset: usize, size: usize) -> EvalResult<&mut [u8]> { + let start = offset; + let end = start + size; + if start <= self.bytes.len() && end <= self.bytes.len() { + Ok(&mut self.bytes[start..end]) } else { - Err(EvalError::ReadPointerAsBytes) + Err(EvalError::PointerOutOfBounds) } } } From 69c41f53726f9b91d2806bb5d9f31c37a83aaa6c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 05:42:42 -0600 Subject: [PATCH 0176/1096] Write the correct size for PrimVall::IntegerPtr. --- src/memory.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index cff4cc35c84e0..72dd0d9b1e9ed 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -201,6 +201,7 @@ impl Memory { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { + let pointer_size = self.pointer_size; match val { PrimVal::Bool(b) => self.write_bool(ptr, b), PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), @@ -210,7 +211,8 @@ impl Memory { PrimVal::U8(n) => self.write_uint(ptr, n as u64, 1), PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), - PrimVal::U64(n) | PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::AbstractPtr(_p) => unimplemented!(), } } From e6c58d827730ee247cdb68886e14cdc5da15b438 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 18:51:08 -0600 Subject: [PATCH 0177/1096] Assert the absence of fat pointers more often. --- src/interpreter.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index ec63e2dd0b957..a80c9ba6bdb93 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = try!(self.eval_lvalue(discr)).ptr; + let discr_ptr = try!(self.eval_lvalue(discr)).to_ptr(); let discr_size = self.lvalue_repr(discr).size(); let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); @@ -228,7 +228,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Switch { ref discr, ref targets, .. } => { - let adt_ptr = try!(self.eval_lvalue(discr)).ptr; + let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); let adt_repr = self.lvalue_repr(discr); let discr_size = match *adt_repr { Repr::Aggregate { discr_size, .. } => discr_size, @@ -242,7 +242,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { self.frame_mut().next_block = target; - return_ptr = Some(try!(self.eval_lvalue(lv)).ptr); + return_ptr = Some(try!(self.eval_lvalue(lv)).to_ptr()); } let func_ty = self.operand_ty(func); @@ -502,7 +502,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { - let dest = try!(self.eval_lvalue(lvalue)).ptr; + let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); let dest_repr = self.lvalue_repr(lvalue); use rustc::mir::repr::Rvalue::*; @@ -647,7 +647,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => - Ok((try!(self.eval_lvalue(lvalue)).ptr, self.lvalue_repr(lvalue))), + Ok((try!(self.eval_lvalue(lvalue)).to_ptr(), self.lvalue_repr(lvalue))), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { @@ -686,7 +686,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Static(_def_id) => unimplemented!(), Projection(ref proj) => { - let base_ptr = try!(self.eval_lvalue(&proj.base)).ptr; + let base_ptr = try!(self.eval_lvalue(&proj.base)).to_ptr(); let base_repr = self.lvalue_repr(&proj.base); let base_ty = self.lvalue_ty(&proj.base); use rustc::mir::repr::ProjectionElem::*; @@ -1054,6 +1054,13 @@ fn pointee_type<'tcx>(ptr_ty: ty::Ty<'tcx>) -> Option> { } } +impl Lvalue { + fn to_ptr(self) -> Pointer { + assert_eq!(self.extra, LvalueExtra::None); + self.ptr + } +} + impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { From 530315a220866df8c34c3a3551b4eef949eea23a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 18:51:25 -0600 Subject: [PATCH 0178/1096] Add a fat byte-slice coercion test. --- test/strings.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/strings.rs b/test/strings.rs index a442901bb5975..7db84d35cd52f 100755 --- a/test/strings.rs +++ b/test/strings.rs @@ -15,3 +15,8 @@ fn hello() -> &'static str { fn hello_bytes() -> &'static [u8; 13] { b"Hello, world!" } + +#[miri_run] +fn hello_bytes_fat() -> &'static [u8] { + b"Hello, world!" +} From f6b1282eee33e2967775b8139614de361bf3c6f2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 21 Mar 2016 18:53:39 -0600 Subject: [PATCH 0179/1096] Add a commented-out RefCell test. --- test/std.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/std.rs b/test/std.rs index fec5845858581..17511a193718e 100644 --- a/test/std.rs +++ b/test/std.rs @@ -11,6 +11,17 @@ fn rc_cell() -> i32 { r.get() } +// TODO(tsion): borrow code needs to evaluate string statics via Lvalue::Static +// #[miri_run] +// fn rc_refcell() -> i32 { +// use std::rc::Rc; +// use std::cell::RefCell; +// let r = Rc::new(RefCell::new(42)); +// *r.borrow_mut() += 10; +// let x = *r.borrow(); +// x +// } + #[miri_run] fn arc() -> i32 { use std::sync::Arc; From f96c76e8786c08a653089afd96300818838305b8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 22 Mar 2016 00:48:28 -0600 Subject: [PATCH 0180/1096] Use Box<[u8]> instead of Vec for allocations. --- src/memory.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 72dd0d9b1e9ed..fc66b847ef9d0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -18,7 +18,7 @@ pub struct AllocId(u64); #[derive(Debug)] pub struct Allocation { - pub bytes: Vec, + pub bytes: Box<[u8]>, pub relocations: BTreeMap, // TODO(tsion): undef mask } @@ -76,7 +76,10 @@ impl Memory { pub fn allocate(&mut self, size: usize) -> Pointer { let id = AllocId(self.next_id); - let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new() }; + let alloc = Allocation { + bytes: vec![0; size].into_boxed_slice(), + relocations: BTreeMap::new(), + }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; Pointer { From 87458955ddf1c819d31b7709298b4ba43aea88ab Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 23 Mar 2016 19:44:05 -0600 Subject: [PATCH 0181/1096] Refactor memory/allocation handling. --- src/memory.rs | 97 +++++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index fc66b847ef9d0..28c14127b9ea2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -96,42 +96,41 @@ impl Memory { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } - fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { - try!(self.check_readable_bytes(ptr, size)); - try!(self.get(ptr.alloc_id)).checked_slice(ptr.offset, size) + fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + let alloc = try!(self.get(ptr.alloc_id)); + if ptr.offset + size > alloc.bytes.len() { + return Err(EvalError::PointerOutOfBounds); + } + Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { - try!(self.clear_relocations(ptr, size)); - try!(self.get_mut(ptr.alloc_id)).checked_slice_mut(ptr.offset, size) + fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { + let alloc = try!(self.get_mut(ptr.alloc_id)); + if ptr.offset + size > alloc.bytes.len() { + return Err(EvalError::PointerOutOfBounds); + } + Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - fn check_readable_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<()> { - if try!(self.relocations(ptr, size)).count() == 0 { - // TODO(tsion): Track and check for undef bytes. - Ok(()) - } else { - Err(EvalError::ReadPointerAsBytes) + fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + if try!(self.relocations(ptr, size)).count() != 0 { + return Err(EvalError::ReadPointerAsBytes); } + // TODO(tsion): Track and check for undef bytes. + self.get_bytes_unchecked(ptr, size) + } + + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { + try!(self.clear_relocations(ptr, size)); + self.get_bytes_unchecked_mut(ptr, size) } fn relocations(&self, ptr: Pointer, size: usize) -> EvalResult> { let start = ptr.offset.saturating_sub(self.pointer_size - 1); - let end = ptr.offset + size; - let alloc = try!(self.get(ptr.alloc_id)); - Ok(alloc.relocations.range(Included(&start), Excluded(&end))) - } - - fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { - let overlapping_start = try!(self.relocations(ptr, 0)).count(); - let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); - if overlapping_start + overlapping_end == 0 { - Ok(()) - } else { - Err(EvalError::ReadPointerAsBytes) - } + let end = start + size; + Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) } fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { @@ -143,13 +142,22 @@ impl Memory { Ok(()) } + fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + let overlapping_start = try!(self.relocations(ptr, 0)).count(); + let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); + if overlapping_start + overlapping_end != 0 { + return Err(EvalError::ReadPointerAsBytes); + } + Ok(()) + } + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let relocations: Vec<_> = try!(self.get_mut(src.alloc_id)).relocations - .range(Included(&src.offset), Excluded(&(src.offset + size))) + let relocations: Vec<_> = try!(self.relocations(src, size)) .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. (offset + dest.offset - src.offset, alloc_id) - }).collect(); + }) + .collect(); try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); Ok(()) } @@ -158,10 +166,7 @@ impl Memory { // TODO(tsion): Track and check for undef bytes. try!(self.check_relocation_edges(src, size)); - let src_bytes = { - let alloc = try!(self.get(src.alloc_id)); - try!(alloc.checked_slice(src.offset, size)).as_ptr() - }; + let src_bytes = try!(self.get_bytes_unchecked_mut(src, size)).as_mut_ptr(); let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes @@ -183,10 +188,10 @@ impl Memory { } pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { + let size = self.pointer_size; + let offset = try!(self.get_bytes_unchecked(ptr, size)) + .read_uint::(size).unwrap() as usize; let alloc = try!(self.get(ptr.alloc_id)); - let mut bytes = try!(alloc.checked_slice(ptr.offset, self.pointer_size)); - let offset = bytes.read_uint::(self.pointer_size).unwrap() as usize; - match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), None => Err(EvalError::ReadBytesAsPointer), @@ -268,28 +273,6 @@ impl Memory { } } -impl Allocation { - fn checked_slice(&self, offset: usize, size: usize) -> EvalResult<&[u8]> { - let start = offset; - let end = start + size; - if start <= self.bytes.len() && end <= self.bytes.len() { - Ok(&self.bytes[start..end]) - } else { - Err(EvalError::PointerOutOfBounds) - } - } - - fn checked_slice_mut(&mut self, offset: usize, size: usize) -> EvalResult<&mut [u8]> { - let start = offset; - let end = start + size; - if start <= self.bytes.len() && end <= self.bytes.len() { - Ok(&mut self.bytes[start..end]) - } else { - Err(EvalError::PointerOutOfBounds) - } - } -} - impl Pointer { pub fn offset(self, i: isize) -> Self { Pointer { offset: (self.offset as isize + i) as usize, ..self } From 5451b6115b77fd13c68c43316086e9db0b9748d7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 23 Mar 2016 21:40:58 -0600 Subject: [PATCH 0182/1096] Reorganize memory methods. --- src/memory.rs | 122 ++++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 28c14127b9ea2..a6d6cae1386d2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,12 +7,6 @@ use std::ptr; use error::{EvalError, EvalResult}; use primval::PrimVal; -pub struct Memory { - alloc_map: HashMap, - next_id: u64, - pub pointer_size: usize, -} - #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AllocId(u64); @@ -29,6 +23,12 @@ pub struct Pointer { pub offset: usize, } +impl Pointer { + pub fn offset(self, i: isize) -> Self { + Pointer { offset: (self.offset as isize + i) as usize, ..self } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct FieldRepr { pub offset: usize, @@ -63,6 +63,22 @@ pub enum Repr { }, } +impl Repr { + pub fn size(&self) -> usize { + match *self { + Repr::Primitive { size } => size, + Repr::Aggregate { size, .. } => size, + Repr::Array { elem_size, length } => elem_size * length, + } + } +} + +pub struct Memory { + alloc_map: HashMap, + next_id: u64, + pub pointer_size: usize, +} + impl Memory { pub fn new() -> Self { Memory { @@ -88,6 +104,10 @@ impl Memory { } } + //////////////////////////////////////////////////////////////////////////////// + // Allocation accessors + //////////////////////////////////////////////////////////////////////////////// + pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) } @@ -96,6 +116,10 @@ impl Memory { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } + //////////////////////////////////////////////////////////////////////////////// + // Byte accessors + //////////////////////////////////////////////////////////////////////////////// + fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { let alloc = try!(self.get(ptr.alloc_id)); if ptr.offset + size > alloc.bytes.len() { @@ -125,42 +149,9 @@ impl Memory { self.get_bytes_unchecked_mut(ptr, size) } - fn relocations(&self, ptr: Pointer, size: usize) - -> EvalResult> - { - let start = ptr.offset.saturating_sub(self.pointer_size - 1); - let end = start + size; - Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) - } - - fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { - let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); - let alloc = try!(self.get_mut(ptr.alloc_id)); - for k in keys { - alloc.relocations.remove(&k); - } - Ok(()) - } - - fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { - let overlapping_start = try!(self.relocations(ptr, 0)).count(); - let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); - if overlapping_start + overlapping_end != 0 { - return Err(EvalError::ReadPointerAsBytes); - } - Ok(()) - } - - fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let relocations: Vec<_> = try!(self.relocations(src, size)) - .map(|(&offset, &alloc_id)| { - // Update relocation offsets for the new positions in the destination allocation. - (offset + dest.offset - src.offset, alloc_id) - }) - .collect(); - try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); - Ok(()) - } + //////////////////////////////////////////////////////////////////////////////// + // Reading and writing + //////////////////////////////////////////////////////////////////////////////// pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { // TODO(tsion): Track and check for undef bytes. @@ -271,20 +262,45 @@ impl Memory { let size = self.pointer_size; self.write_uint(ptr, n, size) } -} -impl Pointer { - pub fn offset(self, i: isize) -> Self { - Pointer { offset: (self.offset as isize + i) as usize, ..self } + //////////////////////////////////////////////////////////////////////////////// + // Relocations + //////////////////////////////////////////////////////////////////////////////// + + fn relocations(&self, ptr: Pointer, size: usize) + -> EvalResult> + { + let start = ptr.offset.saturating_sub(self.pointer_size - 1); + let end = start + size; + Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) } -} -impl Repr { - pub fn size(&self) -> usize { - match *self { - Repr::Primitive { size } => size, - Repr::Aggregate { size, .. } => size, - Repr::Array { elem_size, length } => elem_size * length, + fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); + let alloc = try!(self.get_mut(ptr.alloc_id)); + for k in keys { + alloc.relocations.remove(&k); } + Ok(()) + } + + fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + let overlapping_start = try!(self.relocations(ptr, 0)).count(); + let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); + if overlapping_start + overlapping_end != 0 { + return Err(EvalError::ReadPointerAsBytes); + } + Ok(()) + } + + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + let relocations: Vec<_> = try!(self.relocations(src, size)) + .map(|(&offset, &alloc_id)| { + // Update relocation offsets for the new positions in the destination allocation. + (offset + dest.offset - src.offset, alloc_id) + }) + .collect(); + try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); + Ok(()) } } From 33e924d38347db97f403fc775dfb3a31cd3e0c37 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Mar 2016 22:25:08 -0600 Subject: [PATCH 0183/1096] Add undefined byte tracking. --- src/memory.rs | 220 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index a6d6cae1386d2..f1ad5d66e1abc 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -14,7 +14,21 @@ pub struct AllocId(u64); pub struct Allocation { pub bytes: Box<[u8]>, pub relocations: BTreeMap, - // TODO(tsion): undef mask + + /// Stores a list of indices `[a_0, a_1, ..., a_n]`. Bytes in the range `0..a_0` are considered + /// defined, `a_0..a_1` are undefined, `a_1..a_2` are defined and so on until + /// `a_n..bytes.len()`. These ranges are all end-exclusive. + /// + /// In general a byte's definedness can be found by binary searching this list of indices, + /// finding where the byte would fall, and taking the position of nearest index mod 2. This + /// yields 0 for defined and 1 for undefined. + /// + /// Some noteworthy cases: + /// * `[]` represents a fully-defined allocation. + /// * `[0]` represents a fully-undefined allocation. (The empty `0..0` is defined and + /// `0..bytes.len()` is undefined.) + /// * However, to avoid allocation, fully-undefined allocations can be represented as `None`. + pub undef_mask: Option>, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -95,6 +109,7 @@ impl Memory { let alloc = Allocation { bytes: vec![0; size].into_boxed_slice(), relocations: BTreeMap::new(), + undef_mask: None, }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; @@ -146,6 +161,7 @@ impl Memory { fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { try!(self.clear_relocations(ptr, size)); + try!(self.mark_definedness(ptr, size, true)); self.get_bytes_unchecked_mut(ptr, size) } @@ -303,4 +319,206 @@ impl Memory { try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); Ok(()) } + + //////////////////////////////////////////////////////////////////////////////// + // Undefined bytes + //////////////////////////////////////////////////////////////////////////////// + + fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<()> { + let mut alloc = try!(self.get_mut(ptr.alloc_id)); + alloc.mark_definedness(ptr.offset, ptr.offset + size, new_state); + Ok(()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Undefined byte tracking +//////////////////////////////////////////////////////////////////////////////// + +impl Allocation { + /// Mark the range `start..end` (end-exclusive) as defined or undefined, depending on + /// `new_state`. + fn mark_definedness(&mut self, start: usize, end: usize, new_state: bool) { + // There is no need to track undef masks for zero-sized allocations. + let len = self.bytes.len(); + if len == 0 { + return; + } + + // Returns whether the new state matches the state of a given undef mask index. The way + // undef masks are represented, boundaries at even indices are undefined and those at odd + // indices are defined. + let index_matches_new_state = |i| i % 2 == new_state as usize; + + // Lookup the undef mask index where the given endpoint `i` is or should be inserted. + let lookup_endpoint = |undef_mask: &[usize], i: usize| -> (usize, bool) { + let (index, should_insert); + match undef_mask.binary_search(&i) { + // Region endpoint is on an undef mask boundary. + Ok(j) => { + // This endpoint's index must be incremented if the boundary's state matches + // the region's new state so that the boundary is: + // 1. Excluded from deletion when handling the inclusive left-hand endpoint. + // 2. Included for deletion when handling the exclusive right-hand endpoint. + index = j + index_matches_new_state(j) as usize; + + // Don't insert a new mask boundary; simply reuse or delete the matched one. + should_insert = false; + } + + // Region endpoint is not on a mask boundary. + Err(j) => { + // This is the index after the nearest mask boundary which has the same state. + index = j; + + // Insert a new boundary if this endpoint's state doesn't match the state of + // this position. + should_insert = index_matches_new_state(j); + } + } + (index, should_insert) + }; + + match self.undef_mask { + // There is an existing undef mask, with arbitrary existing boundaries. + Some(ref mut undef_mask) => { + // Determine where the new range's endpoints fall within the current undef mask. + let (start_index, insert_start) = lookup_endpoint(undef_mask, start); + let (end_index, insert_end) = lookup_endpoint(undef_mask, end); + + // Delete all the undef mask boundaries overwritten by the new range. + undef_mask.drain(start_index..end_index); + + // Insert any new boundaries deemed necessary with two exceptions: + // 1. Never insert an endpoint equal to the allocation length; it's implicit. + // 2. Never insert a start boundary equal to the end boundary. + if insert_end && end != len { + undef_mask.insert(start_index, end); + } + if insert_start && start != end { + undef_mask.insert(start_index, start); + } + } + + // There is no existing undef mask. This is taken as meaning the entire allocation is + // currently undefined. If the new state is false, meaning undefined, do nothing. + None => if new_state { + let mut mask = if start == 0 { + // 0..end is defined. + Vec::new() + } else { + // 0..0 is defined, 0..start is undefined, start..end is defined. + vec![0, start] + }; + + // Don't insert the end boundary if it's equal to the allocation length; that + // boundary is implicit. + if end != len { + mask.push(end); + } + self.undef_mask = Some(mask); + }, + } + } +} + +#[cfg(test)] +mod test { + use memory::Allocation; + use std::collections::BTreeMap; + + fn alloc_with_mask(len: usize, undef_mask: Option>) -> Allocation { + Allocation { + bytes: vec![0; len].into_boxed_slice(), + relocations: BTreeMap::new(), + undef_mask: undef_mask, + } + } + + #[test] + fn large_undef_mask() { + let mut alloc = alloc_with_mask(20, Some(vec![4, 8, 12, 16])); + + alloc.mark_definedness(8, 11, false); + assert_eq!(alloc.undef_mask, Some(vec![4, 11, 12, 16])); + + alloc.mark_definedness(8, 11, true); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); + + alloc.mark_definedness(8, 12, false); + assert_eq!(alloc.undef_mask, Some(vec![4, 16])); + + alloc.mark_definedness(8, 12, true); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); + + alloc.mark_definedness(9, 11, true); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); + + alloc.mark_definedness(9, 11, false); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 9, 11, 12, 16])); + + alloc.mark_definedness(9, 10, true); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 10, 11, 12, 16])); + + alloc.mark_definedness(8, 12, true); + assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); + } + + #[test] + fn empty_undef_mask() { + let mut alloc = alloc_with_mask(0, None); + + alloc.mark_definedness(0, 0, false); + assert_eq!(alloc.undef_mask, None); + + alloc.mark_definedness(0, 0, true); + assert_eq!(alloc.undef_mask, None); + } + + #[test] + fn small_undef_mask() { + let mut alloc = alloc_with_mask(8, None); + + alloc.mark_definedness(0, 4, false); + assert_eq!(alloc.undef_mask, None); + + alloc.mark_definedness(0, 4, true); + assert_eq!(alloc.undef_mask, Some(vec![4])); + + alloc.mark_definedness(4, 8, false); + assert_eq!(alloc.undef_mask, Some(vec![4])); + + alloc.mark_definedness(4, 8, true); + assert_eq!(alloc.undef_mask, Some(vec![])); + + alloc.mark_definedness(0, 8, true); + assert_eq!(alloc.undef_mask, Some(vec![])); + + alloc.mark_definedness(0, 8, false); + assert_eq!(alloc.undef_mask, Some(vec![0])); + + alloc.mark_definedness(0, 8, true); + assert_eq!(alloc.undef_mask, Some(vec![])); + + alloc.mark_definedness(4, 8, false); + assert_eq!(alloc.undef_mask, Some(vec![4])); + + alloc.mark_definedness(0, 8, false); + assert_eq!(alloc.undef_mask, Some(vec![0])); + + alloc.mark_definedness(2, 5, true); + assert_eq!(alloc.undef_mask, Some(vec![0, 2, 5])); + + alloc.mark_definedness(4, 6, false); + assert_eq!(alloc.undef_mask, Some(vec![0, 2, 4])); + + alloc.mark_definedness(0, 3, true); + assert_eq!(alloc.undef_mask, Some(vec![4])); + + alloc.mark_definedness(2, 6, true); + assert_eq!(alloc.undef_mask, Some(vec![6])); + + alloc.mark_definedness(3, 7, false); + assert_eq!(alloc.undef_mask, Some(vec![3])); + } } From 68ccf3904eb71a436c0abd9018efb6b33348f4b5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Mar 2016 23:25:35 -0600 Subject: [PATCH 0184/1096] Add method for checking if a range is defined in an allocation. --- src/memory.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index f1ad5d66e1abc..4a3160607b654 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -336,9 +336,38 @@ impl Memory { //////////////////////////////////////////////////////////////////////////////// impl Allocation { + /// Check whether the range `start..end` (end-exclusive) in this allocation is entirely + /// defined. + fn is_range_defined(&self, start: usize, end: usize) -> bool { + debug_assert!(start <= end); + debug_assert!(end <= self.bytes.len()); + + // An empty range is always fully defined. + if start == end { + return true; + } + + match self.undef_mask { + Some(ref undef_mask) => { + // If `start` lands directly on a boundary, it belongs to the range after the + // boundary, hence the increment in the `Ok` arm. + let i = match undef_mask.binary_search(&start) { Ok(j) => j + 1, Err(j) => j }; + + // The range is fully defined if and only if both: + // 1. The start value falls into a defined range (with even parity). + // 2. The end value is in the same range as the start value. + i % 2 == 0 && undef_mask.get(i).map(|&x| end <= x).unwrap_or(true) + } + None => false, + } + } + /// Mark the range `start..end` (end-exclusive) as defined or undefined, depending on /// `new_state`. fn mark_definedness(&mut self, start: usize, end: usize, new_state: bool) { + debug_assert!(start <= end); + debug_assert!(end <= self.bytes.len()); + // There is no need to track undef masks for zero-sized allocations. let len = self.bytes.len(); if len == 0 { @@ -439,6 +468,22 @@ mod test { fn large_undef_mask() { let mut alloc = alloc_with_mask(20, Some(vec![4, 8, 12, 16])); + assert!(alloc.is_range_defined(0, 0)); + assert!(alloc.is_range_defined(0, 3)); + assert!(alloc.is_range_defined(0, 4)); + assert!(alloc.is_range_defined(1, 3)); + assert!(alloc.is_range_defined(1, 4)); + assert!(alloc.is_range_defined(4, 4)); + assert!(!alloc.is_range_defined(0, 5)); + assert!(!alloc.is_range_defined(1, 5)); + assert!(!alloc.is_range_defined(4, 5)); + assert!(!alloc.is_range_defined(4, 8)); + assert!(alloc.is_range_defined(8, 12)); + assert!(!alloc.is_range_defined(12, 16)); + assert!(alloc.is_range_defined(16, 20)); + assert!(!alloc.is_range_defined(15, 20)); + assert!(!alloc.is_range_defined(0, 20)); + alloc.mark_definedness(8, 11, false); assert_eq!(alloc.undef_mask, Some(vec![4, 11, 12, 16])); @@ -467,12 +512,15 @@ mod test { #[test] fn empty_undef_mask() { let mut alloc = alloc_with_mask(0, None); + assert!(alloc.is_range_defined(0, 0)); alloc.mark_definedness(0, 0, false); assert_eq!(alloc.undef_mask, None); + assert!(alloc.is_range_defined(0, 0)); alloc.mark_definedness(0, 0, true); assert_eq!(alloc.undef_mask, None); + assert!(alloc.is_range_defined(0, 0)); } #[test] From acf2ceb534e1f72f088110bf3a348a34ce040ac1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Mar 2016 23:56:49 -0600 Subject: [PATCH 0185/1096] Check for undefinedness when reading from memory. --- src/error.rs | 12 +++++++++--- src/memory.rs | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index f16bdc3b6b3c8..08ef5a46ff2d4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ pub enum EvalError { ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, + ReadUndefBytes, } pub type EvalResult = Result; @@ -16,15 +17,20 @@ pub type EvalResult = Result; impl Error for EvalError { fn description(&self) -> &str { match *self { - EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", - EvalError::InvalidBool => "invalid boolean value read", - EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", + EvalError::DanglingPointerDeref => + "dangling pointer was dereferenced", + EvalError::InvalidBool => + "invalid boolean value read", + EvalError::PointerOutOfBounds => + "pointer offset outside bounds of allocation", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::ReadBytesAsPointer => "attempted to interpret some raw bytes as a pointer address", EvalError::InvalidPointerMath => "attempted to do math or a comparison on pointers into different allocations", + EvalError::ReadUndefBytes => + "attempted to read undefined bytes", } } diff --git a/src/memory.rs b/src/memory.rs index 4a3160607b654..b44a412a5880f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -155,7 +155,7 @@ impl Memory { if try!(self.relocations(ptr, size)).count() != 0 { return Err(EvalError::ReadPointerAsBytes); } - // TODO(tsion): Track and check for undef bytes. + try!(self.check_defined(ptr, size)); self.get_bytes_unchecked(ptr, size) } @@ -170,7 +170,6 @@ impl Memory { //////////////////////////////////////////////////////////////////////////////// pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - // TODO(tsion): Track and check for undef bytes. try!(self.check_relocation_edges(src, size)); let src_bytes = try!(self.get_bytes_unchecked_mut(src, size)).as_mut_ptr(); @@ -187,6 +186,7 @@ impl Memory { } } + // TODO(tsion): Copy undef ranges from src to dest. self.copy_relocations(src, dest, size) } @@ -196,6 +196,7 @@ impl Memory { pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { let size = self.pointer_size; + try!(self.check_defined(ptr, size)); let offset = try!(self.get_bytes_unchecked(ptr, size)) .read_uint::(size).unwrap() as usize; let alloc = try!(self.get(ptr.alloc_id)); @@ -291,6 +292,7 @@ impl Memory { Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) } + // TODO(tsion): Mark partially-overwritten relocations as undefined. fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); let alloc = try!(self.get_mut(ptr.alloc_id)); @@ -324,6 +326,15 @@ impl Memory { // Undefined bytes //////////////////////////////////////////////////////////////////////////////// + fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + let alloc = try!(self.get(ptr.alloc_id)); + if !alloc.is_range_defined(ptr.offset, ptr.offset + size) { + panic!(); + return Err(EvalError::ReadUndefBytes); + } + Ok(()) + } + fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<()> { let mut alloc = try!(self.get_mut(ptr.alloc_id)); alloc.mark_definedness(ptr.offset, ptr.offset + size, new_state); From 62fab9268e28be815bf4d81be763eca049e05cb4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Mar 2016 23:57:14 -0600 Subject: [PATCH 0186/1096] Fix bug where &str's lengths were not copied. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a80c9ba6bdb93..fcdf2e8f4646d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -708,7 +708,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); let ptr = try!(self.memory.read_ptr(base_ptr)); let extra = match pointee_ty.sty { - ty::TySlice(_) => { + ty::TySlice(_) | ty::TyStr => { let len_ptr = base_ptr.offset(self.memory.pointer_size as isize); let len = try!(self.memory.read_usize(len_ptr)); LvalueExtra::Length(len) From 56e118f86c4d5bda0099defcb2b441abd8f366b9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 27 Mar 2016 00:29:02 -0600 Subject: [PATCH 0187/1096] Mark partially-overwritten relocations as undefined. --- src/memory.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index b44a412a5880f..76c41b0c814c3 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -292,13 +292,27 @@ impl Memory { Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) } - // TODO(tsion): Mark partially-overwritten relocations as undefined. fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + // Find all relocations overlapping the given range. let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); + if keys.len() == 0 { return Ok(()); } + + // Find the start and end of the given range and its outermost relocations. + let start = ptr.offset; + let end = start + size; + let first = *keys.first().unwrap(); + let last = *keys.last().unwrap() + self.pointer_size; + let alloc = try!(self.get_mut(ptr.alloc_id)); - for k in keys { - alloc.relocations.remove(&k); - } + + // Mark parts of the outermost relocations as undefined if they partially fall outside the + // given range. + if first < start { alloc.mark_definedness(first, start, false); } + if last > end { alloc.mark_definedness(end, last, false); } + + // Forget all the relocations. + for k in keys { alloc.relocations.remove(&k); } + Ok(()) } @@ -329,7 +343,6 @@ impl Memory { fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { let alloc = try!(self.get(ptr.alloc_id)); if !alloc.is_range_defined(ptr.offset, ptr.offset + size) { - panic!(); return Err(EvalError::ReadUndefBytes); } Ok(()) From 62294d0c427708f4452ac948baf30f666f8c6cea Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Mar 2016 16:37:07 -0600 Subject: [PATCH 0188/1096] Mark bytes undefined in uninit intrinsic. --- src/interpreter.rs | 5 +++-- src/memory.rs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index fcdf2e8f4646d..72e5a56c14121 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -439,8 +439,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.copy(src, dest, dest_size)); } - // TODO(tsion): Mark bytes as undef. - "uninit" => {} + "uninit" => { + try!(self.memory.mark_definedness(dest, dest_size, false)); + } name => panic!("can't handle intrinsic: {}", name), } diff --git a/src/memory.rs b/src/memory.rs index 76c41b0c814c3..18638b8b0321d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -348,7 +348,9 @@ impl Memory { Ok(()) } - fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<()> { + pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) + -> EvalResult<()> + { let mut alloc = try!(self.get_mut(ptr.alloc_id)); alloc.mark_definedness(ptr.offset, ptr.offset + size, new_state); Ok(()) From 1861dbc2aba60ccd378baa112d74bcc4f5052d9f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Mar 2016 17:43:23 -0600 Subject: [PATCH 0189/1096] Update for changes in rustc master. --- src/interpreter.rs | 56 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 72e5a56c14121..969a0fad4d0b5 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,12 +1,13 @@ use arena::TypedArena; +use rustc::infer; use rustc::middle::const_eval; use rustc::middle::def_id::DefId; -use rustc::middle::infer; -use rustc::middle::subst::{self, Subst, Substs}; -use rustc::middle::traits; -use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; +use rustc::traits::{self, ProjectionMode}; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::subst::{self, Subst, Substs}; +use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; @@ -195,8 +196,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult { - use rustc::mir::repr::Terminator::*; - let target = match *terminator { + use rustc::mir::repr::TerminatorKind::*; + let target = match terminator.kind { Return => TerminatorTarget::Return, Goto { target } => TerminatorTarget::Block(target), @@ -632,7 +633,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Slice { .. } => unimplemented!(), - InlineAsm(_) => unimplemented!(), + InlineAsm { .. } => unimplemented!(), } Ok(()) @@ -973,7 +974,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables); + let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( @@ -997,7 +998,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { /// Trait method, which has to be resolved to an impl method. pub fn trait_method(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) - -> (DefId, &'tcx Substs<'tcx>) { + -> (DefId, &'tcx Substs<'tcx>) + { let method_item = self.tcx.impl_or_trait_item(def_id); let trait_id = method_item.container().id(); let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); @@ -1009,7 +1011,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // and those from the method: let impl_substs = vtable_impl.substs.with_method_from(substs); let substs = self.tcx.mk_substs(impl_substs); - let mth = self.tcx.get_impl_method(impl_did, substs, mname); + let mth = get_impl_method(self.tcx, impl_did, substs, mname); (mth.method.def_id, mth.substs) } @@ -1072,6 +1074,40 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { } } +#[derive(Debug)] +pub struct ImplMethod<'tcx> { + pub method: Rc>, + pub substs: &'tcx Substs<'tcx>, + pub is_provided: bool, +} + +/// Locates the applicable definition of a method, given its name. +pub fn get_impl_method<'tcx>( + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + substs: &'tcx Substs<'tcx>, + name: ast::Name, +) -> ImplMethod<'tcx> { + assert!(!substs.types.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + ImplMethod { + method: node_item.item, + substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node), + is_provided: node_item.node.is_from_trait(), + } + } + None => { + tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id)) + } + } +} + pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { /// Print the given allocation and all allocations it depends on. fn print_allocation_tree(memory: &Memory, alloc_id: memory::AllocId) { From 71b94c9a5d68bcbb4667efb6edc054eda8be5665 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Mar 2016 17:59:48 -0600 Subject: [PATCH 0190/1096] Add a basic test for specialization. --- test/specialization.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 test/specialization.rs diff --git a/test/specialization.rs b/test/specialization.rs new file mode 100755 index 0000000000000..ac510ec4cb4da --- /dev/null +++ b/test/specialization.rs @@ -0,0 +1,19 @@ +#![feature(custom_attribute, specialization)] +#![allow(dead_code, unused_attributes)] + +trait IsUnit { + fn is_unit() -> bool; +} + +impl IsUnit for T { + default fn is_unit() -> bool { false } +} + +impl IsUnit for () { + fn is_unit() -> bool { true } +} + +#[miri_run] +fn specialization() -> (bool, bool) { + (i32::is_unit(), <()>::is_unit()) +} From 63fdd46f9af41398fd2191135336e63d118114c5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Mar 2016 21:08:08 -0600 Subject: [PATCH 0191/1096] Handle custom discriminant values and detect invalid discriminants. --- src/error.rs | 3 +++ src/interpreter.rs | 34 ++++++++++++++++++++++++---------- test/c_enums.rs | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) create mode 100755 test/c_enums.rs diff --git a/src/error.rs b/src/error.rs index 08ef5a46ff2d4..65dd187fa0b60 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::fmt; pub enum EvalError { DanglingPointerDeref, InvalidBool, + InvalidDiscriminant, PointerOutOfBounds, ReadPointerAsBytes, ReadBytesAsPointer, @@ -21,6 +22,8 @@ impl Error for EvalError { "dangling pointer was dereferenced", EvalError::InvalidBool => "invalid boolean value read", + EvalError::InvalidDiscriminant => + "invalid enum discriminant value read", EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation", EvalError::ReadPointerAsBytes => diff --git a/src/interpreter.rs b/src/interpreter.rs index 969a0fad4d0b5..f2e7702717d60 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -228,7 +228,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { TerminatorTarget::Block(target_block) } - Switch { ref discr, ref targets, .. } => { + Switch { ref discr, ref targets, adt_def } => { let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); let adt_repr = self.lvalue_repr(discr); let discr_size = match *adt_repr { @@ -236,7 +236,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { _ => panic!("attmpted to switch on non-aggregate type"), }; let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size)); - TerminatorTarget::Block(targets[discr_val as usize]) + + let matching = adt_def.variants.iter() + .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + + match matching { + Some(i) => TerminatorTarget::Block(targets[i]), + None => return Err(EvalError::InvalidDiscriminant), + } } Call { ref func, ref args, ref destination, .. } => { @@ -481,13 +488,18 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(TerminatorTarget::Call) } - fn assign_to_aggregate(&mut self, dest: Pointer, dest_repr: &Repr, variant: usize, - operands: &[mir::Operand<'tcx>]) -> EvalResult<()> { + fn assign_to_aggregate( + &mut self, + dest: Pointer, + dest_repr: &Repr, + variant: usize, + discr: Option, + operands: &[mir::Operand<'tcx>], + ) -> EvalResult<()> { match *dest_repr { Repr::Aggregate { discr_size, ref variants, .. } => { if discr_size > 0 { - let discr = variant as u64; - try!(self.memory.write_uint(dest, discr, discr_size)); + try!(self.memory.write_uint(dest, discr.unwrap(), discr_size)); } let after_discr = dest.offset(discr_size as isize); for (field, operand) in variants[variant].iter().zip(operands) { @@ -538,10 +550,12 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use rustc::mir::repr::AggregateKind::*; match *kind { Tuple | Closure(..) => - try!(self.assign_to_aggregate(dest, &dest_repr, 0, operands)), + try!(self.assign_to_aggregate(dest, &dest_repr, 0, None, operands)), - Adt(_, variant_idx, _) => - try!(self.assign_to_aggregate(dest, &dest_repr, variant_idx, operands)), + Adt(adt_def, variant, _) => { + let discr = Some(adt_def.variants[variant].disr_val.to_u64_unchecked()); + try!(self.assign_to_aggregate(dest, &dest_repr, variant, discr, operands)); + } Vec => if let Repr::Array { elem_size, length } = *dest_repr { assert_eq!(length, operands.len()); @@ -668,7 +682,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use rustc::mir::tcx::LvalueTy; match self.mir().lvalue_ty(self.tcx, lvalue) { LvalueTy::Ty { ty } => self.ty_to_repr(ty), - LvalueTy::Downcast { ref adt_def, substs, variant_index } => { + LvalueTy::Downcast { adt_def, substs, variant_index } => { let field_tys = adt_def.variants[variant_index].fields.iter() .map(|f| f.ty(self.tcx, substs)); self.repr_arena.alloc(self.make_aggregate_repr(iter::once(field_tys))) diff --git a/test/c_enums.rs b/test/c_enums.rs new file mode 100755 index 0000000000000..190e82ac01c5e --- /dev/null +++ b/test/c_enums.rs @@ -0,0 +1,21 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +enum Foo { + Bar = 42, + Baz, + Quux = 100, +} + +#[miri_run] +fn foo() -> [u8; 3] { + [Foo::Bar as u8, Foo::Baz as u8, Foo::Quux as u8] +} + +#[miri_run] +fn unsafe_match() -> bool { + match unsafe { std::mem::transmute::(43) } { + Foo::Baz => true, + _ => false, + } +} From e4dcdcab650f1c32864ac243fbdb410e5115a957 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 29 Mar 2016 19:08:45 -0600 Subject: [PATCH 0192/1096] Remove unnecessary Result return in push_stack_frame. --- src/interpreter.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index f2e7702717d60..342ca3628c247 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -162,9 +162,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(()) } - fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, return_ptr: Option) - -> EvalResult<()> - { + fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, return_ptr: Option) { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); @@ -185,8 +183,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { var_offset: num_args, temp_offset: num_args + num_vars, }); - - Ok(()) } fn pop_stack_frame(&mut self) { @@ -310,7 +306,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mir = self.load_mir(def_id); self.substs_stack.push(substs); - try!(self.push_stack_frame(mir, return_ptr)); + self.push_stack_frame(mir, return_ptr); for (i, (src, size)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; @@ -1149,7 +1145,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } ty::FnDiverging => None, }; - miri.push_stack_frame(CachedMir::Ref(mir), return_ptr).unwrap(); + miri.push_stack_frame(CachedMir::Ref(mir), return_ptr); miri.run().unwrap(); if let Some(ret) = return_ptr { From 6a8bb2c1c09f791a3a8a7f259933e73daeb0c28c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 29 Mar 2016 19:09:26 -0600 Subject: [PATCH 0193/1096] Add initial error reporting via rustc's interface. --- src/interpreter.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 342ca3628c247..8f450388c7bd7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -16,7 +16,7 @@ use std::ops::Deref; use std::rc::Rc; use syntax::ast; use syntax::attr; -use syntax::codemap::DUMMY_SP; +use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{self, FieldRepr, Memory, Pointer, Repr}; @@ -122,6 +122,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } + fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { + if let Err(ref e) = r { + let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); + err.emit(); + } + r + } + fn run(&mut self) -> EvalResult<()> { use std::fmt::Debug; fn print_trace(t: &T, suffix: &'static str, indent: usize) { @@ -141,13 +149,15 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { for stmt in &block_data.statements { print_trace(stmt, "", self.stack.len() + 1); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - try!(self.eval_assignment(lvalue, rvalue)); + let result = self.eval_assignment(lvalue, rvalue); + try!(self.maybe_report(stmt.span, result)); } let terminator = block_data.terminator(); print_trace(terminator, "", self.stack.len() + 1); - match try!(self.eval_terminator(terminator)) { + let result = self.eval_terminator(terminator); + match try!(self.maybe_report(terminator.span, result)) { TerminatorTarget::Block(block) => current_block = block, TerminatorTarget::Return => { self.pop_stack_frame(); @@ -1146,7 +1156,11 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) ty::FnDiverging => None, }; miri.push_stack_frame(CachedMir::Ref(mir), return_ptr); - miri.run().unwrap(); + if let Err(_e) = miri.run() { + // // TODO(tsion): report error + // let err = tcx.struct_err() + } + tcx.sess.abort_if_errors(); if let Some(ret) = return_ptr { println!("Result:"); From 17df5cfec316d798f5cca703093a0e4a8fb2fcc8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 29 Mar 2016 22:13:31 -0600 Subject: [PATCH 0194/1096] Move substs stack management into main stack managment fns. --- src/interpreter.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 8f450388c7bd7..45145e039516e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -161,7 +161,6 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { TerminatorTarget::Block(block) => current_block = block, TerminatorTarget::Return => { self.pop_stack_frame(); - self.substs_stack.pop(); continue 'outer; } TerminatorTarget::Call => continue 'outer, @@ -172,7 +171,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(()) } - fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, return_ptr: Option) { + fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, + return_ptr: Option) + { + self.substs_stack.push(substs); + let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); @@ -198,6 +201,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn pop_stack_frame(&mut self) { let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); // TODO(tsion): Deallocate local variables. + self.substs_stack.pop(); } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) @@ -315,8 +319,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } let mir = self.load_mir(def_id); - self.substs_stack.push(substs); - self.push_stack_frame(mir, return_ptr); + self.push_stack_frame(mir, substs, return_ptr); for (i, (src, size)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; @@ -1155,10 +1158,10 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } ty::FnDiverging => None, }; - miri.push_stack_frame(CachedMir::Ref(mir), return_ptr); - if let Err(_e) = miri.run() { - // // TODO(tsion): report error - // let err = tcx.struct_err() + let substs = miri.tcx.mk_substs(Substs::empty()); + miri.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); + if let Err(e) = miri.run() { + tcx.sess.err(&e.to_string()); } tcx.sess.abort_if_errors(); From d25ddb3130990b82cb6d6f2622b4461e2dcaf477 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 30 Mar 2016 22:04:53 -0600 Subject: [PATCH 0195/1096] Add stack traces to error notes. --- src/interpreter.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 45145e039516e..5b7b60433b1c7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -50,6 +50,12 @@ struct Interpreter<'a, 'tcx: 'a, 'arena> { /// exists separately from `stack` because it must contain the `Substs` for a function while /// *creating* the `Frame` for that same function. substs_stack: Vec<&'tcx Substs<'tcx>>, + + // TODO(tsion): Merge with `substs_stack`. Also try restructuring `Frame` to accomodate. + /// A stack of the things necessary to print good strack traces: + /// * Function DefIds and Substs to print proper substituted function names. + /// * Spans pointing to specific function calls in the source. + name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>, } /// A stack frame. @@ -119,12 +125,26 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { memory: Memory::new(), stack: Vec::new(), substs_stack: Vec::new(), + name_stack: Vec::new(), } } fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { if let Err(ref e) = r { let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); + for &(def_id, substs, span) in self.name_stack.iter().rev() { + // FIXME(tsion): Find a way to do this without this Display impl hack. + use rustc::util::ppaux; + use std::fmt; + struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); + impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], + |tcx| tcx.lookup_item_type(self.0).generics) + } + } + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + } err.emit(); } r @@ -161,6 +181,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { TerminatorTarget::Block(block) => current_block = block, TerminatorTarget::Return => { self.pop_stack_frame(); + self.name_stack.pop(); continue 'outer; } TerminatorTarget::Call => continue 'outer, @@ -288,7 +309,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FnMut closure via FnOnce::call_once. // Only trait methods can have a Self parameter. - let (def_id, substs) = if substs.self_ty().is_some() { + let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { self.trait_method(def_id, substs) } else { (def_id, substs) @@ -318,8 +339,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - let mir = self.load_mir(def_id); - self.push_stack_frame(mir, substs, return_ptr); + let mir = self.load_mir(resolved_def_id); + self.name_stack.push((def_id, substs, terminator.span)); + self.push_stack_frame(mir, resolved_substs, return_ptr); for (i, (src, size)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; From e72d8f8dc66cc0c20af129d4c99ca4e47fdc08ea Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 31 Mar 2016 22:34:07 -0600 Subject: [PATCH 0196/1096] Update for changes in rustc master. --- src/interpreter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5b7b60433b1c7..09b380f92b7c7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,6 @@ use arena::TypedArena; use rustc::infer; -use rustc::middle::const_eval; +use rustc::middle::const_val; use rustc::middle::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; @@ -786,8 +786,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } // TODO(tsion): Try making const_to_primval instead. - fn const_to_ptr(&mut self, const_val: &const_eval::ConstVal) -> EvalResult { - use rustc::middle::const_eval::ConstVal::*; + fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { + use rustc::middle::const_val::ConstVal::*; match *const_val { Float(_f) => unimplemented!(), Integral(int) => { From 9e3e2decba6de7312b9d024c3dc4f60025c22077 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 3 Apr 2016 23:20:44 -0600 Subject: [PATCH 0197/1096] Add slides for final presentation. --- .gitignore | 2 +- final-presentation/latexmkrc | 12 + final-presentation/rust-logo-512x512.png | Bin 0 -> 96029 bytes final-presentation/slides.tex | 444 +++++++++++++++++++++++ 4 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 final-presentation/latexmkrc create mode 100644 final-presentation/rust-logo-512x512.png create mode 100644 final-presentation/slides.tex diff --git a/.gitignore b/.gitignore index 7b8cc5f430cbf..bbd046fa2735f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target /doc +/final-presentation/out *.dot *.mir -*.png diff --git a/final-presentation/latexmkrc b/final-presentation/latexmkrc new file mode 100644 index 0000000000000..23aa1a481b3eb --- /dev/null +++ b/final-presentation/latexmkrc @@ -0,0 +1,12 @@ +# vim: ft=perl + +$pdf_mode = 1; +$pdflatex = 'lualatex --shell-escape %O %S'; +$out_dir = 'out'; + +# This improves latexmk's detection of source files and generated files. +$recorder = 1; + +# Ignore always-regenerated *.pyg files from the minted package when considering +# whether to run pdflatex again. +$hash_calc_ignore_pattern{'pyg'} = '.*'; diff --git a/final-presentation/rust-logo-512x512.png b/final-presentation/rust-logo-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..38484c670e01f3f355672d6c95f79f035a963a44 GIT binary patch literal 96029 zcmZs?XIxX!(mkAn0HKGD)X+hYqBLm<9YFy_rFRq*6cD77gr)(MA}AM|8MW7b3UBkIWu$C%&awg?>GxHBPM!YdH?{xbi-Kx4gdhUxCH?q zG#8h{;6LX8VBqo%eVx0(Q=2X0_w6QYsYA6R<<&ATM?>BqoV}wf9YjpP)ALz<-8Cgy zRM%-;Diocg{Yx*NHg6gvgS8_I2m_M40l6z%ZZEzxyn83|2KtA;?1%;e1brSQtcng$ z&V<+{Ze}RBw8zl>|K9M&oCXqtS)plCDk#?fJ+X*jN4c?7P%F^ZMnWvy$tM4MK_@VN z2P-)1D~yyh<_8Zr_5bgUYJk0!qYgg&Ij=Q62%y$_OB{4yb?txOJ61j$KZ5MTbC{7C z*sT8@r1JRbuY8CfmB#gzy`qY<$a$#AyK#`UYr@ofR$Hiq9DxZ*Zs7`0W+UUZ0rJ+ut!y zB{DRk+Ws5JP04-k8V`*HOT(>-@i--lm?VN~J3VN>(@pyPU_4(#(WYe^G+#Ld!2{2m zmx8usVz5mQk4jPumO%fDR2J(x3*T_QrM5}VOSh|D+)WXp*r#24LWex;Dd$1ygB!lm zew*Qg`I*A%glm1o=NXQG{~2t9UQT$fkyU!g)0AbnY5^L8bBlt`dG|#?tuv)8$d*xD z>l{Lo@|G9oym0U{l)f345;M$3Jc-zj4!M}flPU8oJoa3N)>WbMhpPaV; z#m^ErRNgz2b0+yB0gyNCZ;~feE+l;(ytJx<({_aSY3l&OTdlkH)=aKE)e!OBf)E0V zK14Lm2X(zHc{xctv$FJ;H-xX1>{=5X)+RJigl-sxo=%O|A8?B0PMGjvu)*=wP=X{L|4DSs9YAG&BVDS0RiAwBjQ^*Dy~Cfku?4w~YJ!>X~z3tO!c? z%QaO#9y~f1Yra0sw7bwiOeA9?TuU-D^Y zg0lB46ZdX|Kl1qN_#pn<68RUQS<6n^*YKn6ytjeaMiRxuVV+den0?|a2od@w@IdGr zLwK{_&3UURHrk^*{9>Fm8~@&W|2K<&a{~BoR7bRO&npmK>?6`vlcYa+L}dQwTKp}vbSae=>|}hUWJ@ibCN$i;rAd>fJdC7q8tzvsNI3uU z0hifdqX7tGI9b?wD>dg(R=t};ekumF=$>Dkr_qUvu5Wkl(M-MdADHPkf}AB^Y?hBz z>{vbU7f}A`LXT$G>Rn||X(_Elfo&oHk0SK9h)dVyX>GQJLS-7hnBCX7dRU4<*T|U* z;UMMD1JWz+VE}xgL~m+qt&IoeFt+=)Z zl8^m{oP216c0~;hZ4}k7jt|d-CT%0dsqy3-|T+TrCj4?)PRdMj){~&z_ zy-DDhL|M6`8f*i8kT-Dcl|j`KDH>D9n*X+5}AO9}5$ ziSLc!lEgOxXup_z>#g02ztyc$W>!^i<)Fbv(so}4o1DK?GoBp869Run^iP;%YzuWg z3!?5ZDGadcY@Aw?jw+m3)CDI-x0m<>kJrOF%ITzRN*xL-!R;S7X!w1Z=B$6|WJ@q6 z8oT)Z*#!RwRB=(FQyVV{h+00}*G`u8W{OwR_$spLE@??d{V!G|TC$1~9yvc=6{^(P z_IvyXxvaDSs}pFhe548^s}}TMkBM-UfK@+lFS+(sqqq1P7f|No8z%G>(^BIhf~fr6Lrdq2^hfRIQx?(`Wc?G5q871y3VW*&YfRVJ^xjoUUx8k`t^maFOYc?_S6?`Wv`CjcGRTc z=OZtF0jB5AHSj0S1b%8v>uEJ_g2v9sW>`5aT~Df#EwWwT?i!g3i5Z|GaY!!O>i zy#oYbT5l^w59WZ63a)@0Yu|#eU#~N${JaoiC2qHQ{H)pYWJ230I0Z1M_ka#wE}So+ zp}Af5;8uNe%t1v!ch9?DQ+scQ^me^KALI4ns7zr}F22H-8Drcm7+fr>B{hJET_CSf zfQlrtQq=Vup74BW1YEK{EWYWhE(g_{6{A}OZ>l>Jpxf+%; zobF$R8a4I(GkF`1qOL-HnclR0*ZNxyc5Ood8f+`#Tv`3h4*Fa=Y}j@YCw$CbCm(RX zDpn2ynAy2L`!&ByAOUvlaoT7=(k|PJBv6lp%naI@s<_7J_jYjI4h{ZB^$o{>0r9zZ zM#3*M&%^-1ksLH3cskg%@OL6A18@J+4`|g_-LE@KHrnj?Uame=N)Q?tcLPuQU@dDx z9m~1hu`h0>^^%prfCopHGo8MKh9nK%_d$ic0BmHhuQ3xG1}C1_gn9iKOL+H3_Bk{l z#@N;c2*w&Om*t;9?Qqi`3-l`Z+duFH)2CZ>3PP7xPI7(ZO@pzqV|R{_earDZHI-8O z(Hu~5elZK7jX&T6)+-(Jcp;xTG{6< z@q6^gjG`MX zqIO0v%Gra!om%GL*#Kj}&cR^p?sZaNoH4sL-m&fBUPxjEy1tyjzLmBjda#RxonI+8 zn(7w-eM(DUWRpy(NH>6iLFbiiyX3;qzXzfe6Liq`l@Wd#N+yY~Hwz z-mELtMiLY@uu#o5i;cT^O|Zy0w_MG0kuwjNYSkjU&HDWpS0K+BR)Hc1SH7?|llGd| zR(_8E?8+>+LoO}_%kbW}Lq3$xL@wezzSX>n#$;15<4O&7p$u09H~bH^XCJ0uLEAnN z5$h8}P?9-|}DVXjH7 z)+K}^6Q+FDmO<_kWVrgy;D-nC-Obus)spQf;9~GM?{N1S&64pvcCGRb)u!>uqHn+A z7~M;Z9bL4kWmQN6j>?>-#7?8VcO8PjW^DX4$9=TN)#?>7dElMIPY#es8#FelhchLw z$K;4~eap)dh;by$RXUX;$Gq*XQzpUFZf|_q>Gr6j)AhJ%=>1}J?q4|KSh$9IUKzNe zY{()ERvUY?so<|z@#z1#0CO!NMNdfg2icNM0K3;V|BkiDhcD&K)EX81G3Ys{9WKg! z`%ljL46YxDGJZAOwvI|vCyTruVJ%E`9Nf%yH$U>$${aV=ZohGVV%NQ{r*c?lf^>>hEFceuOi}wwEbfUG3g8baJty1e|(PdrB1s3fm zh*TPGPh!Oj9mWc4MPD%i#fm5Y&o$=}|t19Var3Yv7I_K-HVqi~m z90RX(Our6Og8ZI8UDkZ+r?gu?cD9bs>f3=>)rg+~nlWyQ?A&{?ZL)px{``)y4$r6a z(_!@iW7ujjBdF6pUG(GH0V9?R3|{7@QnqJoHoHty=rLa}PBZZKKNO7iHo!Y!k}@$2 zw{0RYT_SUbV3-B+4s4t{qfhV-_ot6NM{=wis}mE$r@r-^%m=-D+PeM?4r_B}kCbxS>^9I(m zT9UfWnVTujnVb5pp9{%h2@+!y%bfTs2$YxZoUO`jn@WSc$$NS!E>hf-@ve=lbC)!Y z-1RO%4DQGcN_DiU6@%uW(($kPZ(U&r+~fwJ9N%(bXR0Z&X8lcaK^r5DvvVeu5iFBr zzVK3GrzcbSe>KXf_l)Y+V@(1rI?S7f8T7FNK8B93hou?tkMMT2T2Bl9U03~-YX-aq zGk@zhxInPE)Bws=$h#6Dc+4-v&ZV)u<-TMJU0}at{vyJ!5}cBcV)AkA)CVq5Q?dZ0 zZzHe`_l&5CjJ%!M`|pXZr&^NJd#AdsV!O5(CcqhSh`2wKYyqsS>h|Y;K3wqPSa@BA z`aHsIHA;e}9;U{}36^4q==dhiLc53U}n&QlM+I6Z2evk`pW~D~ZvPLv8T$c4^z}`Zq zN=k;J>>;1hH6WgDu-BJOG->x_2Hu1XflFGy*f&#uU2-n2iDGYh&es4vJR*icZoaX= zEs0hpa|mlJ#zhCjv=J|Bb0 z{itvQ{F81-vYYL%=cgm&%ZbaU{OxE3ylnMzku7E{fR3SR+ph0)C2Ky{zaiU%YMHsP z=N-=)Lkwe#&hKmhl-|IX3E5i}Nj+zj_m!!!b5VpklgDBk5Q7${38nkMqvpd+Rpo)Z z`5&FUI1e_4@6#zGAM9bV@ej4#2|mK0xxz;6%?Q6|Ik1SDVIdr)nsPO9l6@y3HE zpUahZ4ERp^uH4CFx!37_7j`a^$8rkXoVXgU{q=qye(Hyyje1Hg!$J>~5#w81l%^+n zBK_#+!Het9n3M`%y;u1w%`gW#5+4!-+7dR*fD3M6A%OFM8E%!#OQ2`Gq-xI0&iQVAdbngm)!}# zjS2gbZ{=GP{~zdtZW2iznruX$gQ$!;8p!J52MK^mE+pyXyK0O?}n^;szP)>oN9heFA!S72$rquTb z0rKopiU{4jssBSo=gYaiC&)%YOuXUATvl0Hr%>4jcFgQoLeZLKx_b6wBUq-bQ^|(s zoaM%Y6(u`-@oZhX-8*d|tiPS7UguWte)hrEwa2B*6<2SS{YY$Fbn6wkRCVuVy1)Vb zwbL1pez*Ut)iSiEcKhE`c9dj0<)gmt>EKaE)^|Eei!5e_iPE;LpGGX1#P02(;tmB) zRa;qfph!*rL7p8sp=b-84nA;ug8%c)5c!z)HC~n3drZ&DP1xfM+JgD(oHe`1AU10D zy{sr-4!SPHOLUq2Jd}#HB?#RK*txXZbp4`=kNyV?k&u#)=C;I*iqo}K`{y`j8{++1 z^UwHi*=dqV^$uy->wU+Lo-zsVyH>(>1U|zzRHA4X0~2Sv27X#{m3-*&?9bNj{`Y> zl@p9jJ22hOdSe$uqpJjN{hXpr`!E__9XBtm@}lP^&r>-!D$l8PNYko5XQ(r+5>eI z-WKy%{6b5B+2SH&uaUL?=XYz;kw1C<{JlwpV{VHsEp}_deyV9l_gvoo;lcIE=$Su{ zXG5mK%spkQA>L+XU!E&%Ne}dG!nfb>YEL^peQ;t~_0dQDlKVS&v}_R2-CUn?F4Eof zZLEJl?Sn%J<1DAIv3BGD{`L+PYmmmz{dAkpq;FNA>~C_59;9wFSX#}5%$2LU0HfVz+vI z;9(zv7bkGx>m4Xn=zC`!OeCh^*+pG*#nZ}?1^bSzZkIb!7nKDh z=mox8y%}UPUf@9BT03xiwp2PjTD$%8$5G>bEo{Rdl@F@ZcewP@GTfshmBS` zJ>o0K{KQ(vm2{9@i~*6iYB-*$hwwFXaBL4Zb6J zRfxJPD zSkS{>`KMLwIZN}uq*s~Rb`xdmfa-QU0qK=Fs!Z7aps~Gd1HTUV$+>yM@_HD@XMFWd z3^kM@Bnji-HI4E2)#N%1Iky#=wRvU+W7I|;vMC0{8r{w2(tbYgzk$_bZO#4J79|0a6ruQ*~ojaXs#&DIeT908jVK`sc z^l&oH*qEL1iMGlgi5OjYOglTYg$)uU4DG#iDhbB<$T7ET&3FKuG^l%==_cVvOk!-* zCeVR5u+>77-FnMN_>DE#WttiN$Ps}d107vOfUp9Cu=6+m3-Z{jwtTFm9#xk+hcD@g zoFczlUqqSNZ5&Rub}WvY4==rMRVn60 zkk_Mc<#x((sD}mS@l-59x4sT>s7WgVf6ocT(Z708EbFK6E3T5Qy}jS?bAkdoSC1LD zBq7ac7FsW}Yv(526w%!I;w6-&1Io3`QJRN^eS{$;U?P`aW zg>rZmVMRyc&m{SHzp`GLpqhblLby%yAm^5xOd1@@$5(Ri+!8!G0~VJrdAzPmz9(b_xFRm)Y_0E&siFlg($FDZQGk57oh^Y4S768l-*gO*URv3pS#6tY5zte@_EIObSLS_~^`BneEMok%y)4#O8;`@;_moBXN< zb&k_UOQtmH)*i9D@I6=+AG4uK>$2+^`Kq%&EVtjeoO4y(VXp4oG$bl#)mOOJ!TEy( z*j~XPdGe?bWFO6{s}TpnLaEDiIW(UZBlKLae}Jx0T!ZN9VK~ym&w9Ip%jNU3+!lHZ zcdc;3cQ=zl?xJq;F22+Ej>37gIWYFpMf;Y*&;ZQx`B6jQmhs+sO@4~1>Uz-Jthe(H zVaT@#jN4)#5T*&dm}-4dc5y|eV%5@&oDVk2&a)oyR-A>YEm%JJ@c1m?^HYD>H6!AWYJ+{=wuI8@Tos9GG0@jL#E<}Z zNx(dqn;I!iA7hDdA9RRPi<;jqx+(_~k=kf(FwxCZr147UgVC%{h;Ko{{3Cdk1>5o+%9g5t_%LQG$#uKYl$CE1SRM+1_L9aKX&B%36 zARlDD(Xq#h14JIN!3ksIxYilAmj~g^NOwZVXk+VIMY&XF62p6>{sThu_16zi;vmYu ztuj*IHx%wzS!*2MqeaaaXxanxZ=g&BqVy8NcNIquiXwNWV*&X10{MxvAo<2K4bAZG zoU5UI+nlRzBE$uW?A@)&h_qey>67C7LYBFMY(I>i9MyL=QV{{?k$DUvxQCj$Im@53 zuAil1GVZ)b#zUVXp7TSh1ccwbeP1gF5maS(InM$Cc?h*FJ%rp;qHArn&$VU@7!x}^ zJMKw&!!wi}S>brqmzxSPCJA&ryic9}>z+@mX3`t(PPrIBG}PGyQkn{_2MX`X&_yoU zL+aIl|4Km@X>b#;`Pq!vb7irBDpcEEqkYb)Z0Y4ebHm;0YNR66M#CwaQy6`d@-k;D zAwQshv(sn6;#G~j#TqQad=p7ZT%`+k-y5+f+XXD6x(v4ak3x^Rk><8v2q-jBl?PdD7Q1(-sk+t^yYJ+3k@Oji*CWQh)J5ic$gRfcFkA5y@3TvV*i zl7iAb<$F!36n(Xb{alkpP3YNgw@Q9NoAZxlNVno#v^mWA9DNQMvJYl41Ki?EhODO6 zM^9r4)8z#dL}}I=czAXS&2$*WxM;rV!gO{3bdlZ;LOI!cCs(qLe6IZd5u=5?S#wtr z0(@r=QK|^4v$#4OgQJZU^dD7r`J~Vb&qzBIUp1Z^RHfcSeaBD~KHwius>^}AdfzzZ zmj5H1IR&LZXuVeb%+bwpNl3}g&nVz1gLzadqLSv1*%HCQwai1819Ohnz zPdoe^`*pWczGZQUPo4M=ze3uCTX{{WV)&-i)n4roDn44v{<$vKc$nKKi~6K&+mW=@ zon(GGDMnZuh|{(;O%i#tMqzTY0~38Yfsf>7-VdOO`L23Ua=jRlt|39V(sy_ITt`Cf zW9X!HQ~C*1Gm;9ieL?Fqn^P3-DON;HuM8tHa-hE4G}|Q5w-%~Ps&f|$K}yqqH*z7pg-{*eG1()5-^$^ExgobG-7TjFqqKJI~45wUAXlBYwc#uc5V z(Cz*Z*RqJ`80Mc=GQV@zOBb)5yN*7kA|xhE&=^y&73ZqXJ0p%nmw@>mTY`AuU(159 z_r3ca+n;m8s_>j`JyZ{NY}O{aPu8lB?)2uP9j@#oTB~7I1Pa|=N(LoV;GfZY-c1UH zyqGHbIMkEVE|5n@b(wR$SPst z;~WbHpdwkvxPL3!1cGC--n@OGUWLJr^L6KW=_1|ULGuiC)Bu_P`n=lpBk9mQi$>X$ zjhc?l{=bip&s;eP0c}0ndBE-2*ma&oCj*sTdZuUk#1fYTVi}2;fye7xIyvz!Dp@rOH_PPMGhFVllnPUFPE1=$)fFot;fHl|JE^`tAcg|6t9#!y6R3vfq zt+-gx>*iU}0jD~xMPqPZ3Zd4Py66*HB_5!2yS@tq-JO?u8j>w^pEBMLJU(M%#pbe8 z!rZ%dLN8Bd3YLqJAJ@|FJAF(&`*qi3QQnUYj-TkMJF3}DBtHo_I01g+{@TE04z;03 z?5q|%)b6WTi%_BrPfORZz8*g|-5TOL++{++WXeQ*uFLLBfWRl>^zW-F9;g2o%}wS zXT*3yjwalmQ8UFAolf^XClZ+kd<%_+}38lH2M^X+YTg`O?GxA@E4t8`T+7O+n2 z!adZVzP0mkXJokXJ293@4uJ2JjLYR3((E84C~4rOF}chP=fR3Nm2X0|DcW?A_mq^F zbky{(AeHuA?0vwm!}qpG-t0&+M*GH#%#m(c=yqdj_>twwpB}dbdk^vtfylcG|8zU~ z8+qp0so!o2hSC&9TF?PQbB(8-yqO>U5~>pP&(}~FMsKB9p;b%=RHKSmWu1sm_59c*z0EeyWSx5djy+LJ>Ce;`+F&eIS|iQZgFTu>V2fHUfmkP#7?jRGtktp?hO2 zizuT`6ip|p@m1OLFz2J_O`V)Yv|xy_xzT$=qWHH?%FT^`xufQb3BqJO<_nb*p7i8t z?&GtxF9Z2NpURe3S7Q3E@0H^|5aa)8Wgp(Fg(nd7dybL+uANf__`ddDW}j>|Jddj) za?rs?H?@1jge*nV!5If5OS{+N(OUt=A)2PWZ#{WTjk|AqI7400Rg{;vI5$}iX)WUB z$NVCVppd8EaOx&BtZB5}7J@vL56;)so}-!RFMny<=s|)^ga5F-4U@feH$M??Hn)Il6H1R4?Vbv zmEDc*gh{Ova86D0AZ{L+Y!6Hl2OIO;44_uq!$t!v^7e6y4WizlzZX76naeUR#sfFJ zV~aUdSVC=P_5lr`?9e2%6Fu-R!Hvm1*vwWXNQB~z zq5WWOY&dYf)d6#SEGT9*&)NWz?6(#QqBc>Z*=w+pVp-KiSHx&X>g&Q%q>N?3pNw0h zCjK&l>>i=@(Exd-tYQ9(CF+%xdZ+>j7Zaz8Ng7Y$ib$I!HO^wkHrQ?87^j_0C*3l< z8PPE(#Ikx;(`nSM{UbXThMF2-c~g+mI(D_B+vmz!r7^bCkXXIHYE!%=|K0O5UWA!0 z^rc`7%At-OZbMN@qCnist4KWsCKJg6#|7S8l&96iO*Z6X_gv;-$(pLLeObU5%NxK@ zTMxG-%)_A51R`aH4y8tV9ZvL74Gs#;zJ(ee9 z&e0xjvYE7{u}kv+4yAH^d(u%tX*VSF6fQF2g}ZT zsBxfFitPun5B|!+L!8gShgBD=0sm+5y<5op+4zq8jRx722UgU)#N?x(dxlBNPh{S9 zqI&yI=~g$njm8?Gl+q_&xgy9Xek?oxxN9QR4`1@0XLXuu(d;z!O?Hp+zCKN=2wGJV z>>4+Sqd*N$P0pUAe$QsXCt;kj-{!Q|yFJ+)iF0~LxCIJW6l8VzJmRH%$hs!~xRc_O zCQUY#+;u2^)9K{KN^!VBBo(*HKS2UtpB`lV-O1Utlq4|Tcx291wL4G`t2}YldA?a9 zu1At$v5-dhV#{ySMLtu>q!m_p^A*Yy#0vUVg)3cUfqW>r&?eMu<^Qt^QRXyBx}Tkk zX&7bKBd%LJ@~&mptP_5{!p}{be>Nwao^@-lU!__2`FI%%y}Y!HP2AHpAX2nes1$O7 z8{e@1$1Ik@$|SSdk#z$7nHcHt!{MF&?P~o)YH~k7YMBdO)4Otgoe)F) zXF4}lW@y>SD?k{8sM04Ecfdv3ZeYJCi7#?5J3Y1QGU3LA%-zJyMUCA#R^N1?td}i^ zopJ7=^le-hIn>XRNYS$?o$!g}N({=D6+U=;O_DVG@T`axxj&%2!SuLtf)6-!8?l^A zwUEs8(K!fA#Rhr>MXK( z`G3XDBFblvA?~O}e)($dO4-X`8ngTGI)ctN>z|R3Y^SkPQ9m!@Wozj-J( z-}zk<SRf2aL~MseMN8ChVo3oxzP`_kE13hGKJ5Uo;DV00DmW;f!%>s05d2hICk;|(4h zpNzqszjgE8zR85zekDfGVxy0|{F4hG$#_D@8H~PA{cJGGt?W~7 zrNy9<1DW4-iT}P#I`#ciUL03F>FQvZIH9b^u~AUTXIMiz1S>`K&pEBjNh%4>9@7M< zR1oD(TuwT)9Ne$gIbBPR_;3+!zsQ~bYQTh)J22UQIGM>0THue$P-O@mmbH%D(@Cna zNj|5YvoG@yf&sLRg?B+DAba-cjal?*6wH}&_VKb6>wEyXI|d-7^1pI~AZK#zsZPhC zzKwRMp3?2p0_!dS3$9vd0KhWgOp682aYDaNCs0a@l%acVTM;fNl;|T>j_SSCsMAN% z(8zL@L}7CH8?qy`m0_~LeE)Xmo8R{Ze_(q>elf?jNbDwx)jeOnEw*XM-^F2z|Bia|OJt#p^SAm7 zHe`W5RptJ!PH8S{E06Iw*C$s0{8HOO;##Wr2?2;oupMJd6P$UtLwkPT9R--?!k(NP7@j$=J=+wb3@%{dX-39Ews(geq9s zUzByJ`U$(;{?ee-$JiB%; zHgmW^fV|vdtiJ3pzY1hcWD;Y1Wm{td{c26bXD`MHy# zh(FfLzY7d!%I&eg$8PUep|VS=FqDWOamg?dGHnHcc4%I8uV47~O2H%0x}@>5_BpZ` zDGEji#w_n&rvc6J4+uA`FogJV1d1}+=ctN-Jl{i~aE4>zBJo2)2$6Y}J}S#u<;~d| z!^D$uzR8XLBVw(Ck7}A68Vv<4-C@l5&-9C$qk4;z@6T*GCbtwRf*hW7;+5Pmze&8j zK^bHJc}j_zHJ2o??%`?o;5yl;&E=0gO{f4dQcK#XW~G@Aa@%82^bX3*({6Dop-5DZ)pyeZM@Ip>RfqZY!XI)hZW?; zd@b-z@lsjL3o8jKLrDpKER2vieeZ&~Hl#HM+iGWCUlWbey}Y z1T1=}+z5nk!x_?@-i)}1L{ng9EeY4!qI zd*9Go-jU*)f{wK|lX?fre0M;YlVcK$_;u)Gm-6)8ggQGxhWit3}j zyWwP1=EO?DhDmRfJ16mevY{Z7$4-8)=7~`3*-+cYa85h8K|B6VOzsZ~lNi)jW$Zz8 zxLD#9<}v-%eec=>qb%s(90Q~AE#${RWsR5}*bsYFGtcgDS6rC^fI z7Rq$BpKd@$Ky|#ae2Brx8ks-*6TN#T4M4v@w$BU9%i*NM>YVk(*f|7#vEjpO?iH`P zbUDF?^77+~7yu0`&JkaTcO)DIyOaLyiFD{n9Ak({Ps6eIP2C4uXVuz&P^Qb=7MHw~ zH~ZfLF3v*4N7T=I?EE53WIFErTYn$l`z$DA_d&X3iMhQsX~X@5T`^&n_*R-79?1;3 zBS6+CqCRfJ;~L2=`F|FY-jkQ$m3|YXR6h&_mW;gGp3Xh{RQQ@d`0fCIuDAi`RN0uA2Az zAC2rsRdlWrgtY>Bq?Wl;lmL&)_@I$|6qjmTAg_S`yOq0hOu}Rx?+oM?xgL0wOzN5g zwhfrVkge}p>H)02MTuARz@?5_ee>Xo8YjM57zp&@_08q!ef%ksq_6}+EKZjj!NBVi z<0Z}d2FdWMwV;*e%_rvKc3M_H--u@e0ov^_S;w`3RoUWwW+ftY(`EyN^<^+x-9jWWJ!cU zbh!{F)<&UwJq^fF1^?G=u(8E^;d{Cke93L_HZnZY-My$T{z^7W#a{+x z@3Y@7wIJ1tJdzOx)sDYncNR%n7K%9%5~pn3hU}L=6w)E@q#R*4IG3@=uTd?VW!>zJ z@mB(^(8OUEq00{rGV*gbcB>kWuhtIPR<%srsj3_bFQnKUd_>T9I)S}c1;}E6Q=%NJ z!q%=if~X{3fn4Ju#9Ue|&k!aDen}kMp!gA?O;noN_u8w;YtBWmP+sY@OIYj6%auBD z=x}$!dzIZU8GmJd@6hhw_Yq@F7Hs&VMu+4!PU_8l_@b=f(iHi9TqLg=uQHTMnGcsp z*r~FMv`DpbNsfojKLx--sV>_H%>dYdO3D=h3p`=|w=n1-Vw;Iecd$!T6P&$H!~7RD@?oJOsd~7Uo}T(-FL~Nz%j`WqFCuOYgey z@{(@k#W=Hc?lti(yR#%2Bu472J_(VGNIwsBdLyg7(MeW<_sX0#Q6rH;yB%i}Zz!+# zR|MTnp^Ptz-To6UF`7b_i;+V6H6@6|lwPtv<0hcii~R$b3W2wtsEe#@d*X*lPQN7; zQ==^|4p#r9i--f5Lb6zOFQCNMmnts|y8p{c4-Bq{SYw~@V2uRyf2O#0m`Usl1HPA1 zD#TF5Csqfc@Dz%j0r}#n$ys^8TR#KBtDK32XDUgrP;&Zi{+!_fa=-6?Wyb~vCuMBp ze%;J88}{1sjye>ze8Q=5H;J1I^?l5I*{tV;9*Dk!PGH!jPg>Y$0Q9I%c$#i;&TL~} z8e)5x;FT#7>$@i=O?Ym%jcH>%@UoVB>gfUEeERpCd`3bKV9>T&Z{M7Oiu!GK3N*#l z`PwL6*bXE-{t0&rV&Q{Jy$^4qrJcW@?@J|^Z#A#X3w{f+ZoQi{ehU}Dt@GH`SsTsb@kuaN4@;VYep_SKJ{08b7F3XC!cy8yv~ z8ESH~F0eJe?2SQeX>KoydKt^O=;SGH@KqjzdItuuqWXpXLY=ComV6}1lRS$_wq*B{ z9{9T6U+nmQAUSY+lGECiY zLWFw<0jg5BJl?GJOsC6p?{6l&5KcBm2}4P#1C{0LRi_SNGbZ3LVa6&sdfj@VIKUds z%-NTy+2yBzx8TQH;*oQrMNgz}>M&kxW>8Xua)R9I6JIr@fj@;Ae1@)a=prxx4<~>! z?fvR>PiFf_h;@s#8yFoo(|vKQ$sqTsa#fNl-$aj0jmkbsZyELVjVd8U*C7fJe2Hrr z3*JWo_fgD?s4-nNF<8>}YcXNUqx5^N?c3$P;*`Rpq#iUk+u&&=)&g#)OQLKKy9RPE zQ&!!j`QYNMbs~Xv=h|;!5?=tmqR3EG@t0=kjocsJD5pgF3l@TiK|23{bALmGw!@k} z!M643xPhaaH!_8oq8!J@t-B3yZkVWXD`P;`ya?Km)<~VIs9e@ej0OfUG={_|2}AEB zBJM)!8-R^a3<5l0%8Da^eigR@o>3_X`yx;J%Wq~F^X@1H?sF|kZWQL0=hOYYVo17M zji=u%GO*rY@F3b(YU{O74D8Z~+*4Z3ljakoH^fZSMO*SnVk!RVN%{{%cmY6aMZ}pV zrg`kIQMc32IEuN{!>ZFb;!el@4yCKZuxL2RgsxsbMbl}Gub|aD;l=98^|;(rcVfAXP<#2u#)h(Yph^*t!cf76JV$44L{zlP8Zz zr28c1d<@L!jc9H`db^R1w%O#*w!(lMmp?wNFhvC+ z8pY_{0_QXHN1eI!XLdE=Fx{8iuvAI?<({m1J58ckt49S9JKt3qseWlebY~yl`GXSq zohbP|8(3?LwHM>?Itn}e+IDk;&{VupfP5xccg8a1|4SN_!y(7)&h-K>W85w=xr0sr z1QXCvfF`~JOA#nk;y^ZF-G!V*eq|Po(gl|~JXsbhX#A+KproUA8En|T?&$)DI}AyH z_;qp8pohcU3X-#Ek2~Nz{tCB?PUh&k)t90zLK#h{@kCUnQu+l`&JLLGPj0H3zlXON z^iaI(-=tE+dQnj-Ctuvp!n~I$rC;veWKG85+yU9YR9O}!lX-$8r^)M_o!H1aFCNv; z>hKi!^RuMvPq_&@rpn~x@%6)Tqf@Erq?$9WXFX^h#n06MPg_{3H@yFIdEhQB%l?5J zCuqv;P~dP}8`n3|=zcVDTA<1xVK-!w#%>iKiDP8%zg4E)LfWDnPPKYd6-Cs$F z_-+V)aAfXvji|qC`Q&Y)UUUr4e2w}#ZC)aZE_C?chM?u87?geHqPf3=jMyGY2~V*|!(?|bdg#GrSBH}(r&z0Yud@Mueo@&M@((y0c_Ip1O?epgQZI$V|KKcbtN&GQ6~)-A z!GP?ec&((DVy>C9m-mt#{`!_W(%GPdm7Sch58cix}h^}8;A`R6>ZbMATG%VW7Cz;Y8S z4GiHUMLLTsJwWN?DZ$wHdFa&!iEaymHA!+cp~+kG29N-(3m$$ncy8{){9qe|8GzT* zS`?tYG{s+}>k`iyvU*E4?;%?;!0Gp85}(u>_PV6ULh-tKM+}pEx}2m0Wv!_EBFA*V z<22HTdyv8eDdqW`gFVxDy+G=}#76RoUn;rHSZy^Ap7=V{RCR{HY3Rw`U|=1N*FxTM ze%?_3+MM+2Cnp&9R`d#<_+AA64uXkoCuqjz{|Pg*=SpEK+;+2Q+bSFNH=g9iI{%dd7N&PZmp= z!2XQ#HT+q1u^r4kWqJ?HCoqH8y54qTgDwqyZ>m?_J&cHTNBZws)AE%^Bfq8_>D^c1 zd4dWBFx??#ITvD3bd%XH2}JIbsJyDIBM0SywTeH)v_P(69u(>h_Nn11AIlrmyQe9s#Kj}s8{r6_t#Bbunab^y4?tZ5EM5a`n5T)jst{B+#I9F&#>MMGd zH~92QTVY$3YHOi8U@*p)#f5!+G)DnNC`Ru9(`b|d|RNTan5&wM`w?q}1!+S)T?Wrvyb$4d2l z=b6K3FiJK7xFwa?>2c(JN@w$%hcbHG)`Ec;G!a#J8E%XPGPoiw1AqjgOP{oLHA3@n zsr*AeZ#$H=3L$}b=>if}obB@@!9>86!;@i$H&aa(T&D07IC%Uif8TEVkq6=gcFdB3 zZb3#}BGco}nX1Ql?7DdYoVOX#UWq&qXS7~=dr=olIDm? z+TQ@-)geX9s6swgye6kqYDVDg4Q_J+M+S%y(|pV#6E_O76rI z^PcL$`b#;i+I%aW*|%5l&e{s^4s13>T@{o0H-$a8Z?!Y(wzzhb{ua$EihIs2uy`fe z=tri}gbGtT7_zsdY&G=RjHbb(Y_dX|#mGfebgVtFic%O$RTc&Nq=?r#o+}A#cZ;i8Pq|X?#$-2rK)^9y(%+f&pDqT^BQE?OilHp{6 z;KL)Wq^GR3zYbGyS}QE|g1N6?{oC!$)D@6cT~k^C_I}Bp#7*!-=2_9>?pSnq94b*D z-aDf2bg+G@Iu7TI?6bx=b}k>@sYF%!B@okPt5DYe!lUyzxrpd@0qLLZlagQ}goe5V z_pQa;W!WS%6E<41ZNKT^>vhA(z*QC+QFJum@a&=bV*k?>oR2Ky#NloTWDtJ-$gHIdQBP_o_!E^}$S_K)&K zu}|(ES(_7X6ax+NP||Uzb6+e7UJp@28S3cQo8LKH44LUYUYAR_2|c$_ox#uAUE+35 zevN;a6Eay5cK55p)Bdy~x9`)Wj4u%vfQP>45IfKvdD!aE6n11RGcbiTxTQD2JC|yZ zIifwFq|JXQZT|#S-!3xjAX2yibL0L; z)8{xv@cez5Uz2SuLR1@BT5VYP$bpc&xXc#ips}*lD;nMwq9omQ=(9q@=Y}hlzP3uhxji6j% zDW9C`6ly&qxM+)>RT#4f$E5i(JkKBFxEg7Dy;I&YRW=_@wlQot|M8vpf?89;y`Kjo zbuV3k(fmc#kn=Ji-R}~Rw@q6dVE8o1Z=sT*tt{wPHB=Ao)#|&(Oo)f_Y!Jd` z4;sn3o_?I?EjlCf`ejNpmnmMN;F1!8X=6$0Rq_$d!y$q@${Q@q8qZZ%1W4Zr+B+`u z^e^+w47T^{&VhQ8ycNL^uy<^rdOT)Qzi*L~I$+W!zC4cX=;UcBxKm)u6tmc{i(Y&O zp5h~(Dq^%KMkW+;rHRg|`)3tXV-t$`qpUS0jjy^1=BXnd?{j>TE56!btbS75g2+sY zGu!p+3n;WXrn%LVxzp`Wmf?7p6!*1KRa!lU*>+c%Qzr8&fkqh)M@2r1K6|>|ElWR#s%7{3$D~r}5}_^E=NlcggZ}qMaortzLJN~aB#We5Es#pLxF_60I_@-EfCwrYci92UPHnCGL2M=3!*16^(SoZlL-s+QH6#&fKJ zyMN9|cE@`>f)X1Y=$AAgG37Nim*9|%ICLJUof2&SD3J9!V_v^J7 zbDb2=0^$YrS{~ysA-*!jKm6nN)>bG?=in!#T5yY72y*D2wR#RWG>eWpXe)nUhTdV{ zL`7LNVs%>_+I4X}N78miL`EqyBTU(i$i&8g|2=oh9+|5YcqBhLbw^#SGM=c<8R1mN z6OYCdRc;JJR`tphyLGC;Deb5#2$&*IDzVLmxd_j9mH2>%z%O~c1zX_yzsJn?X6(o$ zCYG{=jauqD7*>%5J^slKEm*%=`9%vud(%#CKF8*STv>i0cjkI zqMP3k%zI3jI%fV5#ld_%JPZ*Z6$oO8h-q@b4x z;#xiWSXTBKx7NsmtGY6V68j;x$<=gfW$Ul5Mhp1=9VU4Wcy^mV&3yNpMHGFcUk^xDmMs_v~dS|${ zRfAu9J1FDIaY4F!r^>kO_t}B+KXZJVcb;-fb#XkS?89P$#@EJfU$Y6U%2#^hAN)H1 zvI%>!wxLJ`1P-mvJ@j}C5tDgMa+oW*w193ic6b2ZZ>y#byHzcU$Ie~*aPp=H{4s&# z>2JV6QIE*wY5_x$=&(ws(I#Rws&HIk%pe}%^mr9oA9KbKW1E{|{-3M$>rjDG2)F0@ zxKy6@=r^4b-yfxsyv|%Uc9)P{DV!(G7AKwzK5O~5>b*j5!FvT=AH<_?a+8MQay04< z4fZY8>U04IjA+RB+gd&*5f=2bWq?B{eXlFw|D`(TV93)?nfvk=EnT?Xjx7Ceny>L| z89b1gdxDINbwfl;sO)wkJ`X}!Z*I#BI>^hD_M41edb!k z*V!szgh=A_MWOwex69Qlk%r^ImPJO>2r@iz4heg z!(VQ{6QV7ySs)O=emA^~%FGwBd^bRWXof6(uG{awrSb8viQdSg(Y@vWJ`-B;t2@rD zpFSKE2C`=dn6Vd3E^eKF4BKYn;CyW)A?$z~^5vZ;}4n_=uchvet~>kV_hzbpt#5rM`n>?n5Sbh34czmWNw${I@Js^Q-?I4uB z@UqJ;yguboFX?t-2%!VtMZ`lNhhJ{CD8g;ZMRP8wHIbck zZFZo-s4AUR=V)`+u2ZTToS7U<(7swbU&p@k5%D&1oNsMH|$9*gLyd` z)19C%ZS8!tY-Z(Ohv#({XL!d)gctuS`JuXyYR){IbglTHn6mIo)R2t^a!rfV!d3Q8 z_>eEFadI5<+dX*%ad`BND+V<0%;1P`ED86O5kEo$0FQ`|IlwLN3?MKOczCmm!uRZ- zLhb_#;!SW1$5`13Etj){!0oqq*KH`}EqMk9-`7V0$;`VA9zh2x*adF?)qXT>wo;Ff z*bx9d6(LWc=;ohCo2hPg;s5^VDExv2*Hnz}|An(g<09I;ZaC>Wg zv$k);YCmzBe`J1CLliH6`Q-(U>rqW5)>IY<27>YL(AH-K7cZhzF{^Rzdrltn#7sun za?*-v&d}D1wF6*rpU}rwdJjQYK7$VsmThQJLNs5nporUkb4SL%B?ReQs+(;1@Sv5k zU^QHwl*qjL7ry;@ZpG5)=nCBfxuf-&f5hVvp7#-60AQBShr8seB^uhqH@%OT56v zi#Np6!7P-cwGyI`L485jL%vb)j28e7?IA>aYNViY`KCBK65Msx zG6zpzKnk9~5CW|J)d3fft4kkWoDDY)%-RJF!{7S&X%@BGMXgT&JQQ;UUrCYplL;ye$HEXnDFGh8OH}CEK(ANOJncBkHZNj5nLap12etyaSg1IkS5Z@edOyHD8vjzXDD%`c-K*W5A zTwuMLPVD0*CW#TKncKnyl;dgrc@5sfZvTka*EN2>;Xq_gHT(-6X?Smz88f^yAT!e> zeVCliESQR?YU@Orv0pDcAPBC^a$BTh1b%U{U@iH_9Pv^lwr2;QzqI7Bu;w;^j|i91 zgQ*Tr$_o0vI5>-bSWDLZ-D1S(#j!8##CE39C3doxA~{lOI*B14Pvqnc)jOE9eTULK z+tAkqEWDwX9&u}g=MMiacq|L(wjc`GxC0fUpdF7YhJYaG@uu*!e>hFFLwww(}c zmH;#U`vy3h=iZ4V4vFO^0j|PBhq`i1g!@=Yne~@@vBPY4Aa?X#a1&JS0QHuy(BRO9eAM#w_Yb796If7%v6{j)IoWvD$N6~8jin;r4 zm!kaOM~;^6YX!S-;EDSk%s0b$KH{qZ{!d`(w};h0`e_u}Mtwr1hadw;On3GDxQYia ziYTnXT**)jIli3hxYM%*>{~lsQ70G%IPdF4<=C4$efRUS_1so9(_xZ z=kYe5u@Lu=*b_t{mgr7|USmBs=lLS4^dBjI~unLX~y;lrok5Jl)U5Y^zb^oExVzxLFAS-2A9bcw2`NX%zl5y>&pIl zkocK76Vt(*izS<;!2vCi)9TPSyC)OwkYP4cr(^ej_17hZY6YD6p|k{asDdR$F&;QC zYSF^m`z6&DME(}>ydY(S(8mz!2^rix*%Nw#%A&yQ1A&fDFTS&JOe9`j*Eo>-Y+-Gy z^B8b4u5jdH-){f7>HKZ{`7!OiiQa7Uy*o*}k@+8b0xW{*4!+48%Jj;44QIAjUogU-p8nCb0(ZI zUxMzPo0b&Z58gIQ!qxgnV3~a@WlRRqsrLo#rM&p}ZF0ERnf5^>F^e`c87^PMaIsmS zN?l)`e8D!=w-ve@u0VLWYv(7;<%?XsQ$b3HXI}2r5B$;41~1dhf9*=`fzPQ0agc9Y z-35mj`sBo+{sCEQhN^>0<>313`B4@(sVw8!n&sP+Nx5Yit|}AmKSKElfug?Kwdp5f zQ#m-B@q@pfA*lhpLr>xX76Glj=f#OJz21V0M)P+vek~ksKSp#1s(4Q0DYf4CYL4S; z7{a+k)+a#Ip!7>pOQ6t)U2TdW2Y=d&ug!@GioCZ za6$8!=Ww=ee-7OSFg87|4{Dpb6_;BQMJy~gD*v7L0llGw%;_|5HmF!f~HG`chKm!d| z{t?8xHK3v?htQy^taRAt2;k7Y7$PtQi44Gw-IIRXy$|rCn}H9Muycyx-St#%yvayC;*{}yJVby zn(@bi)HD{=*t!13KxS#buAVzkeBns*al1hFjSYa_5%X2VUPs9KN(wb2px4?tmAv-|u z#2PjYLDYe7-o3q|ds0GmjnrQZ7hGZS-rvL{L!yc!@6{(?`PP8Py25ohHtX0f@3U2S z$VxShJlt${P-N`OuguR>WpkG?vun6lz*q#rPmP$$ibm0UGLiq*c zxlK5*Q`l4a*p?HK8N;c73Xhjy*tl-3L#kmQPaYusmSMd{dNRNv{Z)Nt2hi^0pJ}3= zHho;r_r_ zo>;f0EePL!&i{;WM^_ZQokCOw287-$J77v}rtrD_3@g1gY-z35%AmLe|N9GPIraD^ z{{sBhovyo@WK|sdN9U`9 z`uEn^3j{|LbkvAFG+-^%FQ z;M|Il0#!i#f%Xw?6GW(;1UPb|q{-h9abxfa?}z@HLxH)j8q2TV5sjYl;b<=5|RebB3HQy*Ux${twai^?QDh0GDxf9$@d)I4Vx#%+`JpEb#PaH4GPnz z+&ICp(Mk~2-#FoVyQ9kzm>O84x>Z9|3}k8p;V}`dxJ-n66Fe#09YU}m;CimrvJ z1fXQUl4`x?F~3@#ii1T0f%wG(4EW7>TF#DEMZiWn0+;y|GA(0=qapiw25-)WP#ae8 zX4)83a~J&s5!$(_686E{4P~5$Aedz!bN)LToZi@U+4p4wi&%uY+9SV*e9o#ux0lo5O z-F>(2^PIk|G@^z6WncksV|X-g5a2o~aJ&i|;uI?6Q*K4c5+q9J?gOU~#`gtQ88^of z55L|UcI!tEU1;xuZYBa1k;J%T08%JSjtG1YXgUn)|EEbv7Y9N~)}&U_s-r%KXafCU z$Gu=M=St%d{u}Mz{*tK)WWbu5R*T^gK2=)s7pRd-B3`fPeEv-o(Z?HzKhs`f<&eeF ziLo*td#4jD&`DI{uxWFjgndl;aM9%Z-}8gh_&8Lk29o#*76Ep)3vc2LqzI2-ryBhI zX1pak=ovhd3~k1RPGYaZ_7g6-8IQhwWePMeJ5-eo<~Ms->%(%7>*T{?#M#?M(Bfor zbZ7*R*oSU=4iq(xGUnqhPG|0(;;#(h80n3g+l+PYyjk4*lCE)U`JCWz^%9t?LwWPo zwigHmkd&`Zz&RiAa6(0_Sq=I3h0ZoOxu3qR8#Z+A2T#n_BK5BG;eAS%UYPb@>~z?x zA=nOK#h{O6EMv~|!(%R>q9csLQU-mM9tC#YM>`sU9G=z9y$EU!zJ5SU&P(z3v0Pc9 zlV1gWyPtrZZ6Dk3FyupVfONVd8l*E+ZcKEfZ7-;0== z^_=I%X}po|R@e>IBn0K>XlN%B85ztZRR%VgvzBBhRnMh#Q};D(rGL(kW24he-$qIP z%JpU?x&Au0CKdk}&-)GWi*IPq6?)pdqqCoVOeMmc&T~`}=@u$86o<%_9{QI4x{~?< zox9*l{w=8Lg535>q57eW74(s6813QZBz1&u38mNj5D%Slm}|K@1-^_!F{Lx!L^PD` zIHDGJT(Gh49u9=HSzgzU8)BKk9Ber2%=lb$RpM#}EL4)h(j`m-vo;7Ps8 z;#*?N{@)7^Pp^|Nk!HSB?x1$m|1^~VSw9CaY2F7^BJ7aH`KfNBr@f%yc8?p$3ShUIv*j-}TZ4%e7ou2N*jksz zfQMrvsl^{&Ik`(-36jNOx+_F1DQ`y>bViO5vBFAWr9A(|c!Xu#i5M_Ird-lkeQ_J~ zHgr_WUHE1W822GLJTZ+w*Ve9cWp8JPKWee7t81~ls3>?#qSx-_*cqq)M9|Ou)|k=) zJ71O0|Jbu{+sHnSc{ss5+&3HBAKA>wNb$XM_#y#d?EX_09+*o9azEiYztyZCVs7j! z>(wY?ByZW(or0H}D;Hjnaq)PMynt2~#S_cw+TD5%l-h!vIO1$g(U!3|{kCNH%VRzF z5-drrq8V|3p#pNI+SeZnbeCXivr&~If3B}R=74&g_YTwm|ebqVnc&+!iE`J*6u0-2Pv#y zx*aSW_uldX)rLcP6Z{WBv}c5c)TPv}e7M?GZ^{YILtEQZPYPKDXI28Xl$D^rplWL| z{z5fO6obtaBg~h1Ps=VEV7f_*Uuxat1Ia>9p`gWA_?Wd)++Kki^%lfj#6N^Z{aJl* zwedO4h(p7MxoqgMacf?cATtOJESk#kn#dXEr|F9cBilpdc-jrH-0g!{-gX8F*&aio zI!PDjywf_f>+75a6# zmz$}+QzhEJmS==1jw8O#OuS*(wnumydSs1InKNY4zX- zZmGP?HU7lOro}_7VI{WgZzki#hR`W^DfWA$7?}O8WB7j}VWEZE@*ES9%un2DaJY<4 z*p;!;?O|0U1*QGR_QkfGCej*s2*ywVtLH>bfH3stUOH*I^%V z=$c($!!DG=u@~4`KOqN-99&@qIV(vDomR10T4IU><9a(w0Plf$)_*NTM$)eBjhRBmUNsk<%z^Fi;8! zxp*_wCZ#GY?-R}(9$QBP+ww>`YcSUW*i>>P)uVSY^zXN3&0_%1hJCT~osWJ}zKbH> zyQGMJaQ`@R?=DR{?CwBU8cX*IEI5qMUMchpx?rl0ksQXWKxy=}MpQq;Y-6KkZ#NRy z3m1zqo6iJ!T-)D5kX@77PvqAGoU3L(lYb8_;)T|*fqisR)e+EeN9^TidXO#qxev>= zj3JOi-_C9dK=q@dl~9X{7_`lt>Jq0eW?Z)}%bP3;UFNA0Uq?+SztQ5Z!tFPUmsOACJDZSIFH;!80%2u~zr@9+WH&bgW2O zB-Pe9wqM-f8Cvf({o1{Bb-jS)|8bJPz^ZzZS|PIxD%s) z?v|gMp-MFdps1d3VswO_Lj2%bu&vwCkORm*z4p7^BK`1619xsMFvU|i8GTTSa!8VH zF|f086v=^~S6%Ia41_8p6$3U3!1%C$uLH-w7B3C#59C{foQH4w`JVJC-5;eCnB?|w z4*A;3`WP|$R>p^{a=)+F4wfwAr+u*;$ND8F&8QP&qRlDshmKFTo_YJ^u};3YXqT8s z@}~<|-F9j*G|NVR0vFNASX-*+J>cWWO$X4cw41YgUvEYr;IFr}5W{!|BU^r}!X;)= z1R#=)MLyy20%a5Fz*U$WO^0>xl)8@1a?(tw%-ESTT&69d+=ME&gT)-F3|c=&%2V9{ z9YsjlY`Pqv$-cLRxBjF{Q#sEJ(rzk;PSCV`HpEOKy#@lb*wWSoLhZZhoo%};9PQB~HE~8e?nh0T`Rf5`uT#{cTcFD;AcCz>1i=5TAv~`M-k1MFe0P@L~00a>ROePSUZJY4)3N_F(X8hq3nhGG`s)XYm;3`9J< zp8Jv&Lx+;LOG`eHhDy+(X){5OUAU$yNrHbjAr~!%z#yEUGZCkP>*1&(DCC07I8JdZ zR0ePMz;XGOTTt;;c&j~PP2zRV>Ig{K|AxeHg{}3=F;BA2Tw!2Um4WGQ1!Sdl?MPD(uD)~iY@d2&um&glTI$L*NJYyO!tQ(mB-|4o9QJ0edLgv34fB}KBQT(Hk=X31G zJbrEPEb-JuZUU;i76^xc6p13B)JcBo2JxXb-*VAg!h1C$K+GvbPa89~UK<|4`Vl>=J0$}2xP#KW1x zrgqG$TV(EGm=ZC*rZ&48ciA6yWb}$2&iSj(%Ch>OE(^ zZcc`t?KtK3APTz-87(n*ycu>{lRP_!qp1Mau`=h^wiOn(^H)PG$YM)ET!RNEc>~W2 z6^kMH%PL9`oQTW&tVW2(0q=peX!VQYhZ(??vC8o}arQalQb(-N67eCAAnS+Z_lpQ3 zmk@C89U@Qx1n|ekfn&e=sH7JZuN99oVFP#Twt#+Ac0XjsJWITZk^R=ue3$$0!;0A{P^f)xFu@ZA;o zOb+AD2dapP$|CIzWh9XtfAVs7?_;R<2W#K>iiJwnJ`9PAa&~yIMh1s_d0}`*zUoiW z)hz_DdbBG5xvECN|4Sy$m_H~o`+0@Om?8u@g1Iz8E4yOSoF6Ip{5(P#Ad=#gSB1j7-%P4Z6U@H{N?#&Q_-3o!wz9&eU9e9FnS6VnZFW9>)1BES% zR@0`>);WeMw=EPTea{c9*nFeT2~;-4k`~&GAW$iwK?#FH@MsY( z5`g8p0|aiu8BhWL=Yc^hTk#nSKKi{08Bv14z-m1(eQGy66$ z+YjV=ib70!fC2+1qvJ$Y%K8(q!`~xD8L+1g-2Zo_g`cp0JX0!e8O-$ zLpMU4CJ_TVlkbV{BNnQynQ}0|A`;&nB4FC0%5cE}48{XBiZoRK_a1;l4ONQ(W1WfF z)P2a-t7d&Xs!m|8B|&$!cIY_Kj{@!y3Y%M~l%181^#CZ_*c-hv@zAMGcH3qufZJt@ zm)BmN#>&E&OU#&CLRE_SbjZK|KDoSjl>VBadU=ei)Dft#r=vi{n)ujBX$y{H8=jkr-vg{fWwt& z11De~4ng;XPb6H@%Kp?C4U`(~ym`tZ_2Jn4&sg0{4pHsUnATA-k&!J%_UW~fia#r- zN#yh;1FNY8Nf%PAOIn}+o1J9JMGV^1Tjoa{S>^RT3+x%VCSAQX@KLU_$`OKM3AQ6WF$`&|_xR{i`{ykx=^QMkCdtAQ` z1a3nwz6HIx3VX7h!eEl;GVC<<(R7O!VGj@mBA%>~jc3A&ufTUfpos3pWb~pFV8~b? zonFw)7HPMaDTlgp*l)`qo)e}QpE6}0ZC3x8S-HX(6^MABSmojQs_8q$;4C2PkT=3D zS#sCKj50l(@mxkN3P?P9^4Br{^-EZ{XlIJn7(V?hF@4W98XkR_e6Ze67Z)iqe?r+? z9DY+6c_S2C)ZnYup&ZH$;Baum^~jh-{N{<8K~MIZe0=CeTv_-5wuu76>eSM!BTkLj ztB zLL{Capp@Z3N(Ybt*)PCBQC(R8(1cf4Lxj?K$=le}7$vs!&~{4`-YShnO8i2;kq8|f z&zvA(ydOU-ku= z@!Ve!_k6Pmi4aCj@EL1zg5=f8zDaYKY=S80VEfZu4|51;$iChZ{S{a0pu7rJ9q_!tWz98qG2HPOVJa^89w$6BnmpRsilP8rKQH|o+UWkb zR^5NEsk}e_+yPqEhGERZ;4416v)+`Ibx;VU-5e%eZcy>N7e#kuX`Q`yO^gnx~{J=B3hXXZam$e@XYu<12_ zJ^ki&$oNL^s+rV_e? zxN>Q)=UwJU9)L6xrvVb0Q_czi5f>{ZB-MI(Z()i2;3u{WKwSRHLv(m#bb+(tQkBj9 ziKCwrB+9P=IAo5Q3?UKU{G9VC<8N|}aR4uCCM;6~Wc#vZ#HR6wcH<BJ?IQHK= z z620~i^HrAe^1HqD0mN=q+^RHBy$}yEXz(Ju84YB>TuK9BPf9n_G3Y@AXHgwrWT8)3 zQCuwuSUYTgqMp_DlmJS3e}Br9&ie+lPNR;jsFK%sCg2O2Z29lpJ(&v;{fi#_wZBCP zr|=RG;TEa6|LCLH-v2*!%hT9zV0|M2RvL>ox0bPPhYrptUd^2>gxL5|4d z%?h5NVj>4Esn@;-#C8{)KfD*@P3DO$@}Zn1h#_2s~1hVc-R*JK5!H5S$FG1 z2w7l=+{UL6FHUDA(LqFkWMjFn+{DKRfMOk@W*{)DVxG&x35;v;A5-RKz+8bw*fx=@ zvGRU|W!l;$0USyGfgxPP(AgR%CsMOv=l|A8H`qo_kwP1Lj_5F6C2+|D>Y_KYSbdN2 z&irVEsCn|ioxw+&71gVQio2VrI;mqTwP=UFJ&`fU6=Lq_JTV5UmJ&h|C!V^zrK-t$ z8kj0IlHv}7(@pC#q-^>f@%58PQ)sAbKsgKr+)A602RyH6bP#Wgp5q3d2@WN4$`~&J zzzW<>Mx&Qi>zZU9vhNEdP=uat2q@FERGaax5=TyPiis=ES}^n=6$5-#+3bYWIBObI zSdrq9ia@0b``1Q@YQ+h8z4P(pnd5k@H)~%{h#EavbSFVF`FK=Rw&Pf_p`+K;v9}yS z#Rv9iCo<|UJr@LoEmr*qz{99ZOJ2LiIu!kau;Gy&(omQ-7YEUOF-a{*z%#3=S35?t z_Pw^|NZuzEWr2wy9v;9NDFMuC1wX0l7@1q-=-%v}2I#Z*S>B{z?Zfl9yahLI_-VXhZ03vDz};2hr}Jma6Emw3`XfIFK@yeuX5Ic_@QAA}@QU;gc_OU2H8jS^UC&dA#YUh%TfqMAP{(V zPH$k=k8)At3Lk*tJ-m+O7ude(zh+V*tO(Yxs$Ki4&>dhbfuD zW4CwN!kk!~INr{-0|Kvr02X_H2qN#vUnB#2;G$bwE>*BYlW9~rKj5jz&O}qq=t*b{ zn#ql#hmIM=@AO~#k`D!W?vNkT2j|ze?+#Eg5kApW|5qLXuU7Hml`i`VL1dy_5|o!U zc%gwn*w{(NX??gmXFkN^iqdtjdcz^Cq0 zTYfm)Z<#~e>@SfVm8k1HYr7o(QsfhSH^RY)S+4Jw!*U6OcU3(tX=I)r2eUIu2I$Qj z@d}N0{S&G1ti=lXMH$;!lI>?f9?&2^y6llU^il6cV}q+H>gh^+0gKXy3lFDB!}B-C5%E(z$Cj{G~$Pl&ffXaE$v1m%Pge@XH`qj;CId z7;ZnQDLv0<;i(G|uYFKae$L}i!AYd7>lOHy2Ay3K1Gvx&n<1Ppohu(i7y|v zd&Eiy)2LjP^Qw1~1IB6`dTczi7x7w+%o8;py6}Jhpl2*Ci;YF91{>RY!t(&z1^suE zT@f=BC;w4B5q~;Qmqvcjm1LB0d09^6eaTT&=qq>XbTPHvpDNR{ZNKPdJ|x4-KN=lX z-5+)D62%-i5;A$G$XHji2ore%jqdtOK3}~10hj^ti>|u8*`>)jGZ|NHLpP>2Z5XeKyEyIH1^tYp;JPYWn$1n z6P38|4Gj543EVM$|JGfZ@<^lcZO4rOoaP6Gv--+KDVzMCob{D!6WzFYM+s43{ZZ#V zDes%`N4#ez76vl%EmxUyzY|0**wQLuiRZd+83K`VBZ&2}HI``+qE(+j+fl_bVjjbi zKUwB^gwc~y_0Px3&ZzXN>;*eb0j4ae+HKYA8g8+qIm|+am<`^nza~%@AP@%IEzs=r1dR0s3F9pq`BO>goODmbRk2W7l2 zInE3GXdoab%4oQ;t9sw>W>Acz0|gP7%qIN5U)P3uUmlH*FlbB$f!uKN^&^sJ_8N)s zdhX4QdkdPH?EUo?{HixTC{Hm~KxIs4|5UH5S|oFl?D_VMgZJQq%llWcUGZ#i36gMA!RHbky&Bj2v<}o^#3@z@_4A; z?|<%{#n{Kbg^@%hltPIasZ=T|NhKqx6rqg@GnecX?OHxesq~?;CHkahh$xD7vdo~O zY-1nH^1I*P>*Y0ndGXx2&-0vf-pe@*-cg%yGI4VpQjQ4TC465pBe!a5MQmTMax2|o z^1uV`o;OPCKSOSXrUhp+Va^BQl}2WC;X`yFwSKhr*gtj{p)2VIUT-d#ED^KkUqk-g zBYU_K$S#^}C2=$f{11KlPh5CVhVS^E8QZ?@$e_uBW!>chdk|h0=Izyz9{FeX2jK+) zc2quQtc7m(td*vnGjb$u~hDG4UQq10JsGTYVNR-iE0dG6J=y<-w_k2@DIeggmfQufu{ zvxe}{2>tI`?-0{V5s_jE2T_;JHu(B@!NYgoe6IN&lx4H}Sa3!0;f zHTxQR@LGhK({3B0+Wz?mf?EN25{R*j9EW4kxVj5j@o124l~zt@{xANw_xJK@a@ zFGU2C^6ZYgd<1kl0Ud2PvAt1us^G%!>R7fv?@g)tu?^xEkH0B@7ybzC>nBAjIv}8h zhNJ!y`!JyCsH%aOd?$lC`iVPEY~Bo_va9acfM+&GPPcx3wao@Un*K zm(`~ev3aK!sIMA*OHF2B2M(U2BJaDHMJvRnZs>tYHhLM-01iJy=gi2z#}>8PRsR0a z&|xUtKg~SzZEx4NSI4`pH^*g_;kE7g^_eeY?RY;<8qaFg$aIpGDAUg{qB^@>-lpL? z@#vz(py!67quu*|)jcs8owc&OJNAB0gdH`}p=s@KXW*S>%;d?UImu!Z;Hf3cMxv9o zephQw2m08G{%M^fsjBz)&e8S95sV_jF8q`CYV$N-1%wYhH1JD%Dz{>=Dm$3|vL_ z4~?!uR)6PiIcUeQ014I8fOcAu{Pm{d;6Bc`e9VAN7oP2p``&rJ<9+Nx6HZrCK%XRM zc-^_H2!ni-G;qkhC$d!$Cs0^+8zFo&C~GN8oPE?{HrN2E+QR^9f3I5E1*cy>)hSMmH z#OI0*34*!RB0im|R5}dMGRr(S%GPG^j)VKdl>1DMY z=z5 z?MuV)(pzt3O$hyb`>(CzBbL^{laG#8qCdd(J=kjpV5|zF&fb=Xr3xTciIH&$5KW-^ zsI|$C+eokLBv#$l)%X%`98Ef{BHEOW9DIm2i|AfdY5kL^y68~af0bllPfh=$z_o8> z-H=S~7yh-R#K_hws6^PbVml{Q9P`l7 zI@XQnC!}T(y}wV~&ABLYjYjWCD0eePkP?}yP^t+63c#$`t=WEs_I>s;R~mTkVf-1% zowg3sX5Xe}4H8C-@7AY{a5wA995E(+f4*)w-6iK=1uPi5MSyNbhDILuvpZ`+A#hB^ z->Qd&L0j-0cn%9NgK+%|v?_vyP@9}jjy<7MLqAZwx5$XvcSIQ|MVptXh*ajIEH%Ks zVn>6_n@?;)AU)>e82!!EL4c2LBYC{F$F_|>Mi|6-9WjfHTCe1IAsK99^$=}aymqXA> zYJ~#f)UBMZCS;F3r@NqF~@DtP0&S_hoP0ThIOZXl;V0sI9blqWrnv_WA2pOkb8lm#tc(g)URbo)@>f0AdJrPiyk7 zOC}v^qy{xIZno++napWi7I{a;_6g7(&;b8NY16@(e@Wpj@kb>!H6xr7CUR>sZE?a zks>=?)A~MkYaK0fQXtA8Kt)lc^g01UCZ#NEgtzZ_X3Ux8@140R$47MTpm2eFFhD#y z5PM#YiufBZgcS(EIGWc8)Wwg{pDtk;JuWe*)!*0+!W`?kx7<~K57eHqkkD0% znAyaZ>u$*8SUb3m@y)3R(=#e+Rf*1y=VT)Q)#AwcHO$cQ*8@2}kawNz6*BgmUugCc zgr_zkldBg`(eKJUW@UN9nTW8S2sg5(X;j|Y1>kE(g(nlA*qp$GpV`N+eYzto2$h6# z35=IGC!{9D4>tk!<$-`auL2=YfJ=;x=OjOTuio6sCvzta$o=HlH)i{bo2ON-_7Hs_E7taO(r8sG8VVH8uOe`Z2LIkKmrn4v^+B*f9j){+OTajL=Mpg9JCC-VT3Z) z5MXLg8<##Lk@ij-d2gC53X`uFBj~Gnvt^_7H;1TEhoud8v%33lBvgdNo98d z`zyZwQkv_-z4qtw!?eZN@d4}oouh92wnK_gF(n(ePLd}E$>@zT$8UI;@oAV!K>X zqB8)dY(tt0n#YWRGW!g}o*qP%n&tOB$Bi#1!;CNS32DxB7E80oJpQYhOVsfMn^d~g zAyDk#a{b(tXoWta27y|<7&!eUYPxL>K%k5WBYt?U;0+uMs6D_S)AdrqQ*earbJ&m3 z@8-aHD$cm`Ugr7&nUWQHj7r(h0%w^yYbTo!+I~F2>orQD>&u?pdmiR-N_MM;9T`#N zJu+no6}Kk75B)9NEDF|+F?+BQ1Xi#nAHmi^+#W_VLwEgiIrtWwxHm{qaHPvhwU&M7 zQ)xl_!jnYgs4?#(JxX~UZ|X89&Dz{&WRP$z$KYEHUkN#`*K8Ejg?OP}BXsqzn9~cM zA<|-(^e;hS&Yh%Z$TPTdkueN{ri`=J4lrI+nBuokhR-2a00(I#QFiX8Xpsv0b)T_R z^sPUpZx+M8d>w~^+hu#5agrnJE8 z-Au#t*@mmL5^Hf{(kVIcC1FWtD8DUl668W5_Jr<3eWLFI#p@_zf+hg;NhJop_DaWk ze(D`m1mW#(4-ljA(nvg!Z5T0{Jh0^5E34X;x79JbDS;%!hvcB%fL*A4#&u2m$Z!YD zQbv1oCsyfPK+=ceW9EQB;w;$kFI#sH$a0a%U&pAxDV#Tp$D4y((-ob02%|+E!slX# zWHFOKdnaSAkr`*e;tW~JZ<*?YS}A*Q!jU&H-c%*4&-!WbmTD`v=TdvUDnx7gdF}liu$unkg5IFPi5I} zA7K3PW*|_ExKrvaSU`mI%70NBK5q4pYM^W{GbbUG6{G$Wk1%1oq%ztCiKc%9J%@6s zwa^j-+l&^p*!^P5L#?^V59yT0=?pSYb4bSK)hU|6!*1(b8VD?YFgJ(~_!@uNr&M!{ z-a1zJw4+G{ZYyw;3l0*n(nm5odG7nyK5YL;;N35;ole?2l*)DV7id$xlnG@!NdN7{ z2d$Mx5q>dv8}?w?dK9F?YSrg$cmcVX(mXAo$wBg0IRr50XNt0yO#a%|ux5p36}cO` z!Ex)5&>8_vu@4rFL~*W9^omra7i5UOim>rdMDN>Ee%AKKB_zAqq+#M zt@lIHv|w6~;yBbJi;tDV7~@Uh@|ixwlXGii79Sd9atpyfn6ZWEb5Isj$+vbLpQJaA z<4ExG)ry!fN*e*Yp|NRM913R?A>4DaJUzzgiOC0((bFGxzA?&}TSFS33~Suw{6w_e zK)PPE-z9h21HtrbWCz=7UG={LMlY5ANZ|fm%Ko>n;3EQ&HS_^5v4Nue5aROK#^1-; z%ZT`22MK$7qG0*!&N9-v9n`)Ep^|GZ|4i#6mbW2c*&DlS!aq^G>j@Xwr|zkm#6}?P z`wdD>Ll&>B_d}pQfx)M3`AuF9yjeJ0t5IY=jQY#?m6{fI90CMYS3P>ao^xCW>g3dj zI1#b@rvsG|qt+m{m>Cr{?Lui#%5HDX$^72|I6K)yx?w!~_Mzk~0xhNVB?!Edkr+Mf zS~72O->fOzJB(2IhBQR5Mos1_I$g*bDNk45msE0oq4dou?z(dBlDP2`c$al{`rTXX z@+}B&!?&DCi~g|NLXWR=#rTmg9#=i~%MMRN$nO)8wtU9NL9R}_nZKW{FP-9bK)o`t zf?JZszPF$>OggZo=~9vL6aBrOgH9sVB?Sb)mc#;iMn_ zqN;-$dh!D~w|lthBDtXK&o@N>#2LJt0_%P7fgVhz3ec2W+H!=i;F#8BEQHIg66;-!6a@)5@U9=v_hOjaviTPWO_ z5}gY%wX5&ktV{*X-%0j(>0Nym_#kTybZmv~e^0p6W~3AYIcwbqq-E8H$F9ygx! z_xk;5@DlgdF0MgOw2>k=Iw%r2Z_3M;r?B^%RBfgSspM^b62q$gM~dAK<`9=X)EE5x z621fc+tq!@C=Z{VdKrSJqFV9LL}*qZ!ZI{ezlNRsPUaY3z?nm8Om(=#+|Hv})BFoJ zB`@Ti+0J}4-OBhI@?bdenG8=GUsd`^>#lMMWT;>FwRY7UI4*Q*nag_#D{3Y9ooNp& zIS|=qsg4kZZ)!3u31nWh*E1zi;i-_DWA=xJleRQSh1Z`e2E4LPH@J$>IKZf=6xIE; zn#HI1`~sd~ZqD~#(l?6S4dbljC+6yeATjtFd00RIpsfDgisf;JrA) zSV3ud^LCNrh|YY5Ikm`C8$G&d;^S)0dpQXaxUbGowj=v}#fK8FOz;Tz_p|5!kI#?N z!V}G|b{*^8_0rb>_D>P&ZnzK4c>vZ^ITny>DIy^0JeUtN94_Bc5x}U8&tQrLA5`j{C8XqeN+EoW#8??~Ty*BZ?J#^&%3iZ&c#< z5^+N4(`SEl&WqQ$;Oe6lW}UVqLKqacQF{Lc%q%uxuq5jd?9zS4Itz!XuZp@rizZXU z^?yAPhs<3+b$;x`zE|26fuD_yTN^eKXkMTazcurX+@ZB!VIf-QZ+Qj6i81UDWWb}N z%QoYqpX+j@t7?n5e67P$@jjA&Xw5~@iAc5&{wn2uU+fxw(dHyO4uUOj`7e&M%VJpO z)y%v;JnZIG4;$$|B`WhLOeEX7y zFfyD%1>trK$V?cfXZJ>37&(z%-J>@7-M;53F=$L`coTRVFxOho)A|3*4Bn^Da~$Do z$N9Y-%#g5&*h_VL?$zfR*2irgiSOt1rgjalqp)3*{ZNK&4Zq+x`&Q-Viy8>ChsdYLEcH&NW&sPX*5~&yYmWcQEZi55ph3O zp)Pt|Am@cR?8+)3Voz5o1QHO@v2`CLb($Kq`S%_!D2}~S$ZXGi8EI^OZl^wASvfUw z4~KIu-p6MW&#MW9X#)A_UPL6(1T+anot=yiu$eal*ikDQJL5!LgV~FN78;<3!wQx~ zdhl#>S5UjPHcgfBZ!Ukec!vf@|E4iEB`MHkTTbLveh@tdj`R|HP=!rblwGpz8V0OP z;2K8X9bd6fE2cd6hG(;vZbf3>kx9ON<(BQg7NW0W!InBNu>pl0=p%rIFB(1*27dXd zI*BlKODJM3*4mPll;GA?5}dODoWb6cqC2^`p^;XCCE(TZDlN8 zb1-8U_s&9Ce6HFzxllb(896YwVw-+yE)dTd+lDR?vBGc z{IJCjjY3un4*Q={hbg4*H+??|=y^nq%7ABXSU}=8p7NS|_a7S9a2FY?`rdiC{~*5o z8l~@NWu<>n+CcCAMF@_clwTewEFAi{ghs@!rsT9_lVOh0PZeYw=3G%@r|->6W5w^` zpMBI+*=6lJyfx>9_4}9$i{M1=^`SIw5}WUW(tE!8S;d@e&lu)Oj|^k=G&{lDQgSb7dmo1ye_Y zy~Mk2jnqk5#aksYIHnO;?9mV?QSp^bD00j1@-2$lpAUYdSUfCWDWDQxmB$$+cOYw% z`_JYhJ32IYy*SIQj#7wg>_T6voUU(ep*>Mf9PWtUfZ5x~V^;weT-0pFE)`(KHs|Gr zs^ayGM&IWCX<8WYlT;DAkrABAMTmD}dXs|2H3B^Dv(0AdDHD22W!sN>Fsz@dCbkTx z&4EcCF7GW`Qo9oxZlM-Z2j|o9l^mabyqo)2L9*##_ci*TA-8~14fgK8+dFn}AB;~K zmVqV+OMVXHv#0$p@Qtg76Z#_KI~{F;{7&(H^IOj>IN{Y=Ngsi+0w_i8ch6iKjZkDZH)(^0$&<6!>RQ&oK$ z;U)GQJ4#DxVhBz{V`mIFTEO9i1z?=8MwPLgymWLfM90|WBfJZjCJJKh(UP|=>ayHz z#yI%#UA25r83Zr%V00_1%Gx70Zk??$%1e3jx94~p$HldU@-nNVPxmd4nVxI5Oq)WYvg8_exe z==t@1&ao*5_Pf#3Skl}D*$0Fy#WhiiH5IFf#J3ur0jTCq?fa`sXG!(7Z!Xu-qG}7By3g39v5*KEtR@>;<~)`wT5+R?2cVrLRaL{ z58oe#@E3m^POfANc$;bAKq%iRejI~pe4p?li1a$C2c(CI$YVpBZQJApG`#(DVnZjD zm$N7V&*7c|c69?wgJWaA9)!Q9E4pqAFZkM8c89AhY|h*?v~}*rQt9>C@?Bzg8ZTgh zr}tqJ$6nCZgR7mxnyR%;u{SyHdyw4G`M|ZS7``b|)zy~ZGsrPhL*2Hw7lWx1uL5aD z7Q%ap-}Ae;dt#)2H%cZbT<%)Ffy?>j3&ipy9E?(?wC^6VPKcEk%5U=hh9ZA-7sCG?4mFvjF-s?5|;~!O1xX-3! z>a4=`1+te9!Lv|g@7N!*b2|?aD4ynv+nQ*=JvdVTI+H`sB$ZqsckWV~7 z-}B2l&<1yxVpNVdp=c8`^aO2EY%hrFdl?VQCO>|V(e^~?23niX2xi?{^Ki@R^n2HQ zYRHWTit`}w4hd_(CIey%B4!=e0`3pMx;@l>-H6&!!_8Lyq$enRi##Jjk%}|ss*5x( z1fIFl*|L1zMF8>2;es#L($QoOd1#FP&=vI}tbw0KgfMsM#39Mh)O3f2^k6D0N-?HK zz}rg;A0PfXdek75J40@|8j1%X`>QmXp`jNIFX(9Rg5G?tr3V6{j0%nLl=CU#nbXVv zGPnLbwtKmM{A1UzUA1fIds5?9Kqaew(q9vAqwk3`1|xw*ghGC4_7VNmZMwRyq0-^ zchIkxzt|rWuxR*WJDk0m47pA zi4X0G&2xgvZG!gfr37}8utTvrrUSjTx(&{^?%u#tvDd<2Q?O{0RUe(uiLhxXpM1;0 z0ikq5Tvf(hMTn*@CcxY=ercA3zqN+>=E924Ir0nTu#*sUovTBYxR=*(aW&Fd2`g-7 z_Pn~}Ldz^c$u|L6#nJpze^%M(L7%^UO=PkGrSG%qF2G4>$YJb$irCncMwYB>3D@?q zB+SfAnmGDpeX64zyM!~QZVI+2)R1|T)3eSSReqcFePXh#Y7|Bg{prhFsh;6Cm4bj{ zg)9YZ!ED_nS@vcwyFnXHem4GZ=J14dV|;fiQ^6+osRk^|=QG2;vD@PLidDmuv$h|K2Zjhf;n~&TR z-EyO-1|UZ~(gxYG;m)Ic1iFo!vlW>Kd&z$m3RnS|Due?FE`@YZ`-(MVy3Y8Ye-x3q4s@l~B!&xlJjl9)N~7kD{g)!q3{N^^my-b|P@x6RE$2F!n5%^DwME z?7_)zIlGZ!|62%VFOiOR^B+CpdsOr1e8gP{Ogocyj9|>eQ-ccA)+YqU5xi=4ozy^W zUz-iWshFZ8N?(-Xs_XO4lTip+Lv9(>l!HpGv0r=BSGVZ~Em@ZwNs16YQ3}e?BhpeA zw`tcmCB1MFTPtRE72JqK>}!8|5eLB!{ugQ;_lhSeesKB9nUR} zW2QB8Lq>uQ)^e)(gOSO2@oE6cxbmPTv5H{h7jc4Ch8{d!zz9`dmPjHBq8f+xDP>0B zW%r{^6KWtB<#>eUE>lQueRW@y)^cG)vNWnVR-NLIKFRFe^PWcOX2w^1tYIQpB?@i< z!AutfOE-h~mRnKmfJHsWmBSDJVTai@syYc)y4`G03O7xn%qS)re?KdJyU!48YOL62 zx-xD-*#a!U5yfmvb7rmBvICN@itLxIEL$RfaaGKc;iEXW)J{f!4eM-_7AIWT6eXHN zfc5gQzSeDd7az;`>b&YE;i$F3%6PbK!%>X&Br)JLvLAD_**rPcut5d7gEDv2lU!PL zhhED2lo5O`WXSvd6nARZo4a0&UK&m6_84Mq7T3Pzr*C*#Y#6@ub77Us2QNJV*fg@V zp-AB}M5%RvMWK9m?|M%QQQNz_#_Fsy+S_WhFM1cXml$_QtjlF(CQxA_{k2g#6exK- zU)-7YN~3tHplBA+)CZzY;mTcX0{G(skatoFcy{L*pq?a~S=8>>W}ER}hOUfDim&wl z%wrndj{8=H_wxH6{uyT;%iR1~eXcJ#&>VLr<-hsN({ufGb6B6(ltC-;S0`H3&km^? z%*^73jNUWLrp;W$2o&+HYv~4Mb^`}CJz<|X%tU#Ik7M9}bQ=Q6ag)KW{}L&Gu?wbFoA!xDs1`1FyfpFBGLYTYd1wWX#_~Z zCWwJE)cd$6jOM3@hTj*SowNVSPOUZmJCL<3Lq-5e6-B_()eHm3hZmk^7C4X!l(wg* z*M}IyFSILDY3Atm{C8@ew;X}!J2-uRarkYe2LPcJqJF?0a5YRmCD}oEkiNwB9GWU) z-XUcsEUdbtMs9QyJ6%zpOyd}$eOebJKs zPGPzAy-bMP$Y_2$6N4hfYV%F}4c`eV$R{0tc_iQj$B-C4Ql5l;8E|@ra=)jgI8SCT zgdQ)X#Y5$KyIRp+i+dNeKd2mjoH}lK2cS&`rcE zgRyh%Uk=H^T^T#zCV1K-cSY{tqaF#9?}M?;T)WjSTc($^4QBV*&J(eog>hZN+Mf_*u7ZAa0)Pd7_dFQmT^Uhq+AXh%)rT!Olrj=0K(fA%@S0Wh zznL&@6)hG%*vEdD_tyXWhNV97{`k=*LU_Binty^rWzr{c@!aNRV|0T+Iodmsb3rQG zi;re)1;OxX`S52scsL$F;paPGxJ=Bce+kVyTa)NE;+F?@V0=n=mgF@P6<4|a=v|BN zkCh{lFZjdPM6)iU4sFRbO|e^P+1fP3MNij(-TE)OZ2aigyQdcHLGdS2j*XyVT10gg zq-uuhq#t9)dXv_N;~5si9fw_PugJKSXX;gYhg%O$-@*sjZby#$4=aFljaAyW>*H0-44WX ztSA2P{Ug{+3WUcSoHR-Kkg@=wvMI~Hsa6c{oE2HLNcLHsK4zywbTvw*ACzq*(K4u* z9zB&i_csZFx{V|T%2N~9wWY{5c%eKS5(+~I4=>5wG4BSfPi#GOUijpX z%r6MGQKkTznm{|3L)|-9S7e;I9VJ~B^0 zmE%m7+%q754L-NzcBxX|3nW|)YV4w4IYoM;vfEHRr{!g=e+T`8`kzsh+zGd>0k4CzE4lObBLhHB4V2nxUsam5apSO zS#F*r3xGMdYMA=esU@FvZpoglrBWJ&%CU|PH!tdjIzsq-so(hQty>fJ?jnQvGm9;R zo00f8Jl={mbvgn=@YT;@s?c!HtBP^YPq?7^g8@&JIyEn91GX3*L1nf8glTw}gjF-d zbsHm)khD*)UTL4sd`3NzcW&eaN{Vq6Ac3NXIk<{C^Vbd_LUQwtC)yOz^x+@`Fc%UQ z^N<3xq7gtCE~#7;zaT>{G2x04G<;;3d`YOm|I5$XfIB5XW35t@?WI88MyDvb0QIj8G?KQavlYv114CBY^#YB zPX=vJjI||eA=S0!ko=`pQ1d%uXy^gn%AsR1B40*J5PtDBg+C=nem_os=!PXUo-w`eg6e zt0Lp9d_o_|AzM-75$D*5Po*{ODPwszMQT%y{r4%ZvTqB+fPj5XdoYldLnvh~mPmge z!a+gy_7c2Ou_r6EC&UIPe{i8)=X-|$N1gcSPL=`qdyb)0A~uUh>ck*K_y^?S<$xaW zdK2U)2WyE9&j0uu*jCnYw;nd3MypQ9?kJqUA-U)yw`p}oE4@#P?AN^3p&3l(ns7W$ zBQraI7ar;?8MVX}e*XHfsss-a4?Y@2s27vL^2Kmd;&Y)nE?(gAQW?p1(>H7*ZJ}}p z2#Rit+}vaF*j?*KPGuWaB1vH8_;i6VTf6P3A7VpbEbeCh)7tdn5mzgot9F;K7Ljp> zXQ*58hk?yPQ2+rBn7|xKFnQ$rb<*3?>W5ks^j3fZl*5yz&h5BZW_J9@P07rWRIT45 zKm0Rqi7jb72R*9bgpA{U8ya^x&Os7-Z)1M#P@Q`mlZlf~{WV zk79m%#rtP28oA?oPTa$->e~~H;9RRHjQZzaBNQ8728=I$VpYov!~>khLV3E_J}0%4 zu1}0qcO{SHalMi|?EHxLP>kKkdcSz;yQw%kaMrMj``9L&@G%yycU~jG7Zf?pWV|im z=&^QXJ{wE%#@ko`)dm1RtIFX^Gxh{+6`g_>n=<67H*CQzU$lebB-+rfpzHfRa?q(C z^T=gu85Nd6I)xNmq~rDKI}tv3{9vCkvWk=AX~P#O{hDkLXs%jV=6q?zm86<%E8~fN z#W>H(k>FHwfrB$oD&H+*QXMm=dyMdX@T*4V<7ITb+B|&jS&3%%iAN6pg|(JaA~Y*7 zdUBS*vo=kPh?B#zz2|E9?;r7x;;!_&vUc+>cUm8VaT)RddwkcKYdZ*tcSD>oIJk7b zzxtfKggOrFh`2OAt;vvCDIN=(4W$RqnQlnE2HBN$w9EgA zoGXt+VWyL(2>2H<52BwEpbN1OEaxV_loK#g>4ELuiqRM1eKd3hQx+f`#vLh_7vsxU zoBbp!F4;k458n=-HKhdIpc~37q|U-O;H1mS%wXAWaex2R_sbLIPlV-(yt=(gGD2LM zO>->Zb!O^U6nTnwYQU5qQo=`cwxdgOt!Zo5xu`%PzJGC%0FHFXA-Bwq#v+4WG2av* z5DUf0IjX-LJYR^=Lge7LA@mU}TFwCN^BdfwU^9E*?)@Z81*g(0{{+w!@GuLbut^c9 zMz9DRQYV7l$}tb z0#xZFjkg>glF<2A%BoziOk5T*omMp}7DD=cLt85O$feN@70Oc?h zOXM9@+ha$~yfE6feD=W^*`q8a2P%DCENi8(wCmnpJ$N|8R|}LW;zk%M#=mm@DrA3XTV8j5cvz zWz%lrX1RHIH-alTwz!#%bCYs;Ke|WomFkz&9>w2m2TI$NA3!i1U5?d8I?+a=QvXMWq4};H@-#J zFy)mjma~Bt-g$BCFJHJ~ha%Ck?mgIS*omu4LrlO4q4w*;cne@E$YB%d&kdzQLgv4! zyW#I#j#4e&i1qGTSjUsxbAm6G@}93^qBR@YHwRx7qnHK}c1e>Q>~_K?;O&9=BYBHr z_gU;uR*vnivnj2U$7D4N;FmTp8!*$?@?ESSQyzJVo4dKL59n4ihqs4Z|1cYz%kEa7 z6~fbxx|};<->4&D(Qo;W-^Z}tP2O58YPzXej!@>rG4f6Sd?hf-s7067fX!;&!Yk69 zIN6J1OzQYxLOj=O7}*Hak_p24$3NokE&E4TZA@yS45#^Q#+dv-W4D#z9;m<`a?g%0 z`1V=3o4t>W^|T_cBph2kZQ{9C^%25IpIR(h%5gdBBs7{_s_DQ`I* zlqB1gw$3z`*OSkvQ}r|jpVbWY*A(PGk%0*=I8p&E8pOxE2e|wUcd99DTBL)H=u#Cb zzx{V%%T0T0o=~}5Y0y3@S zpkysLBSP}eHU-GnlttoZ;?fcvt>F*f9nKcbUqzwE{ZIW*CR#a`_!)pM4QgaDzGNNN z&FIuUBw%=vAd2F1&KuVN^U_BACcOyQ2KDg4kRl)B%6<80%Vx6wvy96SJ(8hb_hJ1OL%Jbb`{wt`Ars$b2O7 zWz%qpFv@I3sy_Am-0CzQqc^?CbcEgfGVoZ=8gJwzV0fW-I*bIR8X*S`ZaR#6EsNq{ z8NrGGmbejai(HEvZnx?STTrDcf6N*)B!JM3C$xO4EQCB%NntMD>Z`}%iq(-Vr)YHYJl(sU^{VXkr^b@bx$c#*^=9U z#mr1QtW)E3NdNd$I72e|2L&Ej8<cWt#i^eJg%#!CE1K9ST7hQ7=kkC)v{p^k#fMKQI97_p~dIn1wRPzVDAP@(5FokvC(I2%-{MoTC*Gayb0P>kiEv&^7WBFX$LMiH zwYn2 zEe^=4NJ}2L1uRZz>rTx%Inon%j+v8Ag*mPn1Vmuyg+mRf{q&vZ9{TbhS5-J^d@{T; ze_vrOfs=hGeoAMM0v~GlZ`0X5jV$H&GfrA8>$UWcDJ$>rCP1q7=E^mVb_w&BIeb^xCDB#7q-UeDAydE_`!S9v$BtyL2Po5CNh__Pu@&tty@c z$gKz#T&x_@n*!e|3yv)P4}e5!mngTqkp> z3*$$%W`&tODPWtU{ku;#sjuuEPhU)%EdRi_Pd;OxEW2+xoNLb@VUM1p{`)B6(ZDPT z2?QFzYuF0dh$BD@8TVZ#Yy@8D8+O|lBb#zU#G{@EF=r3?AQevCVcGp*2cL7b*2oUu zikJCw9!&D47bBRv)KW-BJI@tapA&I)iisdJ%13^ZsMI2Lm#*Ie_!%If+_I#y(Hm%;F30`Hb7^j9a@~JVN?i3d2{cc<%a%S&9Dyyxemj zQ?x&a6r?ys7JR{BucsHIo*jXpYohXv-FAZ0mk9G!uK$+;j zt|@^}LEPnsNIqLOdF}umsioUjo98kT)w0Kj7CrnQ0>Zk))?4*9QL9vbJ88h9TUK)U zJK%&PLr>9i^;2n0onJsvfsL*3AMeR+e{lRX%L$^hRI4lmPLs1~`zB{Pb6Q>$<5P{! z868=0yLp<)B87W6NT=|aWQbSDZka#jVngy_?;r|N4cW{_>1K7j*-Q7>rDVs{LkCQ^ zo>=wR`IW2hYTrnV^U+2?A&!|GNpSVae0{HzYuie5EU3yrvZpPVq@eI{YBLCN8M-P}MkhjL(n1Cq1U~;yLL5BCf2Jlm`Z*A`XoWlk+A6|> zWV~LnyaAlAHT23^TlQ_8HCE$D1fwb>0&H`hG8}Ju(DGwIcTZYIngppdED9YZFcbyoLKrL+yrlr4TQ}2zGLIq91t^uV zR}XCIIM4tf_c_uXXvRIC$$u2w*byNt-=V#o3yf=V=7^iZ?KNZXoE4ArLJM9F($fEp z2-aApO5Vs2i0u$;JH_Yb4ZNgI!Z!4b9Z|WYr#tw%&^q8MhloC?SLL91K5XhG@1w@T z2fqNzm{)Z+WQ{Hnmk4Nr=V`kQ5N<<%cb#m{WtBwCHoJhjWE zv3>o($st|?VI&dzV{`-=FoiXu<99Sx!aOIUM5|D`1H}eafC|gDiJ9dUqU%eh%6@fe zcNtWiL09)iv^(oQlp`w1CPPCFz#$g9-;zZ!C0c$M>D5l1jS`Ja36~$1nlfx$cqL1X z>U*+PkgZOQmk|f$qH9^SW6XaN_yiE(x(0;TTCIujUJiXYN>+)EiNLSyp2=W+J*bK1=-jb=&A7ohQ!gExHbgEEgDJ$CfPd7c^g{OW+ z_hwSaX+(+6Myr1LVx8!|wB!$y! zszL{vR*UVgN)X4Rl}hX7unVH^nG?7~mV(wEbitTBPApy#y#wKYo1&Uk(9YU7xP})O zx#L6W^#c|x+0`xYb5o-YE#6D5Tg%Y+shC+D5QhT^eRzLJ58=O9r0V~Xbmf6e|NsB> ze(x}I%`Id@xvwa6usNc1ARQ{PQc)DDPbtUV=ui}WR8-2+K~#FsDWVe{wkRpe z<{X>7f3Lp3zy0IA*Y$ipo{!@hj9D4_eIUC5hg!3ehl}ZVJXunN9#Ebo-hu!dxlagWjBAj_sj5S<3nVXzxCf4!(8=!k(@4vPRz7 zxwuut)Z(g73yC*mM5OI#>Jb+IDX;4CdIfB^F?M8{A=^pG2CTl6uD%mPhKCTF&scXuxK$^SOY6(LzxWJ-~SGnh6BT_4L~tyxJF27gVXB`j(}gql$5thzF{`-<9n zAT0T9q#W=JPhM%AB7Km37jA>+#mO{?oyN){^8oe%VLot??aD}MP(Nm?MCqz*k-O|X z(RFKF61Mp_N>gyww~$vfo#a_RV-6lT?5_Z8Ito+VqT6wbB{ZgZZ<-BCj$?DGWgeK| ztZ{6nkId>fg%!m3Lx?w@K-rsxZs%pg8&PZlQFNa_T(J$yAxfmHPjux}xKCGPG30tt zg*&(&m;>QfCaohBWM?S^HTN3vE2_1zBFssg-GgH76%TO5qqr`>vixC1Tsw>Ra!oHI zU?at$@xp&fTu+dsmI?OG^|3-&H?9EgIv51HkvClqbvf#&$z1?WA4Swy!h|T4HS|Un zUHnlKY)q=z;qfb&F-iXH#Nt;{Hfeb$pnpcyJ)#vDjAazsX7nZ7WOs z1MB(C$9C;T?(Bw6e{46hY1(~Ln`2~8OqGPb`s5|ke7ZK-jP4U%Qo z1#y*C0Bh*`8L(#qrl)(u)Z>6TzuHcZP^wl&0zz#qX^Ktl6R_eVdqK%s}1 zHaFxr?~0yX7%82U0UzsRbv$>KF23pD_}jlWD<5d0I(LdlVU9$RxDq#evI!Q15h-4j zW$y1*V!IhM_GUg?+4~1C{>*uOf;NRE_SZiaHWN$$Z`-g*3V5wPvF#LrFTWmlkP5Ps z9B;2uxpQd~Qu@F!-HU%o9lf`AE|IknPo(=vXWbo&fz$zlHaJ*5M{VPjy3BAslAIQd zd~LSk|5{r{2AloNh977{W2Sfwz2tT(7Tc`~0_yq%5IO1oDZu{a9DBY5e(wp)I>wk= z!R|1mmER@XJa7iC7`8B8r_l`R>vEw%B4xfl;Wxta1lTjf0!n;t`Eg*)gz~J18cp4! zw~bM(FCu7&2-q>npHXo9fERklxVXTDF)>(*9rWCiFtcG z%eK?&k3&T;sSsZSpr;c!xYP3y)X^w$9{D*X#1V^A6&-XQ*Z)@g(Wn6;)x?J^2O+QA z2@LFA0^*^78cDe@g7Bl^XUTkN1H`fV9T9#d3K2}XkR2ew)kgXkvmY=0zreY$N# zZ755kuDUDFi+_c zJz8#ivYI?9+_Eb4FCkFpDrDSh9`--p zMR%o2;`3}_Ro4{AS9pIKULsz%vXnUUzv^xPv+tog%;YDhu|?qXUh$;GU=dAiNENIl zV{0FR4nvkmjpBNT?7fKs?zOff<}{JB`#MPT6@R*TPVhCjAVkX>d5_8#IA~Z*CwkD< zsd#gZ15$xN%XR5sYHr*PhpUvU1-Hs<+g4Y0W>2+esdOY^sc^h>-2(+Xf6st;U-km` zd-=-FS5@{}QlQ(y=3f5njZpym zOof3fr;$u;%+nE4j~6#crEc3ap!4I3Mx$d#=-aZ;O_X^UN=wqoxyS4MqqKkG9kJvI6s*=Dyk zSU3-ZurAX#4Lhn1T7A>XO<1(-wdTY>_m@VR+pJXcA~9N2uR_C>CF~Pf=1WQJye6dz zylgfN`czmb02o6TmAY9K-FGs;^|JC8uE-P!Q|O7DB~ENLFt1*XQIvSK0$?GD7zEm8 zxVd$YCrv-)J$`x6BYw z-4MX5zm|x%HqwMfwE1d88~Lh74O>8bTUh;_a&YI;PoQ<+y&6_nR;GN_`M0v(%@L?3 z@AASRHo46n+*1k7RIZ57M4XvM+nZzP70+H;d^)BG4ljydT4Pkb^Aa@}aScW|{8N9u z>n&}^GEx>%j><^LVHyX37MWKo0Aj|;BksGEt)xnEoo0mX4*`kUq>`oqXp}j?5D9>8 zJ;9f2UrrcJSUn+1##u&*zl5}pG~YHdWY}dfgknI zgI`03VmUpL3PrMLc}nNus)>(f`Y5CCL@QljaErhatyqUS2o8rT@Ko#yps#kKcsw}6Nj z0f62zIQa(yM~CAcXn~PSVt>W6Gf?z>8E_@h=|K2ABpa{~_o{xH3n8*F1wb)0*IG;# z?H`@PZ)(l{pjv(v-bBcoYNI?I;w&KippOVPp)6lnsTK~8y#m&0=t&hBG=Ye5iaNva?~P5ZCLwHLK?d2LA?NTrrSeEd9%LRrd%xl zO<~+4`2el~LIu%F({g_k!23LT{_sTEf4_$b;q zM(<*ke+d-;Dg9moT&>c5o_?zaSR4`S_h2Cn^wiq`p1XI}z;}NanKG-Ppak_4W~~I+ zP2BY6WERuzbPD~4*Cqpi2yOL{s#^(Nfj6?25FvbBhwQe-5>TIj);*g6j2mO`J;!3H zbJ9CjDS+L&_@zpYT7w|%e;ba#pIi^HX*Ty7Blc@JsBY*y@Q@}tqIz|yQ_fA`Q1l2j z328C@)O`Nwg&ZW_A$>K)e~k_GEn~JuAw5V{xh-vwEVGUB zMz0GgieU69yl|fNbzX^SZDkwweyb>*#+T#cNuIt`#>`h2K+ zdp2N5WeCFW>66Y?NV{PD0P#CS$o#2GIIoi8NCVC};nB>6t*XY|(2eNh#XW$S|LVjm z1mr>L3uiN+LCgL5`jJ=3w2N~0%)zfzHkAY%-)QN*g#lrM!K7fGnh$^3Z8f$4g+U*} zfVG6!C)_Q=1$2`Bwq1{@k+JfZLt#Ghg9OZfT7K6OYBh=#(f&N6-+*v6jeoPffw^s5 zzl?+yPpWOrQ_kz_=U17u(zvJaLW(vTxq`iK(?*>e_mp)YYjZD1bTnf7o2NpD_|2o5 ze?MkV182)cT8A%T_~dJr6A)Q}M@WE7ZJF#umq#kSJ+1oVxCZ*l=-5a^z$s>m1%={z z4~0}xHdH=R0U_FcUc*H1R>+YecuMxmNAe92|9_FqrrfW! z=VrFk)6<-DGX^lpSrQ)dITQ?}bKH9Ejk^B&69T)_G~xV@3dG%#$$^Z<9ZI84TjN&= zy6QfZo&b${>8Wa?c%>D07IJM37vbk8yT`xQ(m(@bx^dd&l{U+cVRIi(H>bIuIfIE~ z(pACN1VwPWAuoAB$34iVd1QAq%o8YJTX(o%7)I3PB=X38skbG118>vG;J^}SNA>B7 zO2X*pDT(X5@vngcWCnJ{gxD$`;mrnybNvv8nh6~B9JZW@R|&Wa@-kE3P$VyN%I~Uo zr~=j2oh*yN@EFdDJXwI`dnH$kXs2eCGh=G=`SWU!9{PCCbQH9T1*|V z`WbWSanmeU=sst5IobfFc!Au7ZWK!7-oMAgd*{Lb49);n+#_5n}XU)Mn^|* zkSFn3035N)voG5>LW-4-l{z*i8B~vV-sF|yRQS2z@^5Q7Crxf}i1#Bc`52m!O176wlL`LIy4H65KvB{@1^sN^o zZbqbi|0&cC(gqudqXyvlT&2|Q*6ejD*s4{sg@*cdeWY{|+`)Zet|vc5glGvn8``invI-LS52cGcSxskbzdWNP zR$%MB5cq)Dmt&nxu-L;s0`AMrAkv*Se-^Y87*WxXQyLy7UV%!h^Ch5cPZO#F=>#5H z$&IeX0$O<_fkNS0!ekXJIn_lJwWO}y8g&0q>xM0@9fnb%Eb;)LT~8UXd_@=^qjR~`qn25c_k^90?OfA&_5kEP*n8wrJ8>}%7P%%4HN`MftHO!M2n4DbDX6Y zUC?{0d)_C4??4y;`14kpL|gar!Y$$Py~m=6DMd!nN=G|)PRe+#gQ+{lvH1x|Y?3H5c~P)Rh{c$G^3dy&&(Od>Ff!6z-FESvBbb@i?5n)k*)1Nbeo0Y;g7;^(d{JO?O^>u|KJuzQgubd~5hh-yZ7dzO1!_K2!SO2z~}|66ZFSa=w8#Op;mQrJs{hTE=vc>h$iX)rYy>+D?! zPi5!bPc#v^a=gTttrG+IH-cmyiXiels5Q;j%1DW|$7-lxMw%;%YCCKA%Toc%Z}Wrf?h!k_$Gdn_zG1;NGzEwv^57H6vghmQA(PS5@{uTo0|(Lx zVD)V7AX8;y?YfBqpV!T{SeKQD(mJsJ8cVAeqd5X@I236 z1;DSfRM3SB%tq<$a&_RiJkuc_efG!T9k0&lbQBORqm$MmkoJMSrnq{aNZIEYJ1;wr z5>N^>sAr;~YqKoeu|;n65d_OQLC4hC1uHRGRM+H&cESqK1>*eMQYC$4R=!s39697T zrK@a2`6VjA<}$)IdTt?Kfr9*We6fKz?0~q%zchn);+eh4VUl`9M)TFC5VzOwOdYd? zJS4SysRD7y32?b1Ea-dLt|MwlzcYVu`%&J8tNy=&xwFuorhh;@ELJp78ffk287hQi zW|kKPdD$p}Sdp?WSlT7e*#!}`E!vopw3-ZFE~nIzxT_R0JAS-8U>y7JG``Ev$G7Y1 z9e5`86npmF_&8zX0($L0O)~vK=jGd3S^m#Al&A-MBC&hOks?G{zZv;)f7aMND9|x4;P9CYQUsYP1I?b7#A-#rAx*GyJpr7OWW+j2 zt2FSr&AnlpC&HoxJX863dT$SP`4`Vagx6oZ!GM(lIN$5cXE~K3<6itb5ww7|WDu>= zg|gN#8AtnBgEOG-vvTA-jdT=;bFA0id2Sa$8$sN>tlj?<$pcWUZD~%)nna0b z-k!lU5F*dcIAB$T;(}{iMuTnvB2o5f|BR#1dJI`v><*;P*kN=2g)T~U%0*_nyN_Rk z*@Z1Fgn%6rFcYg7oZE>{&W^x`2V3Mb>!*j)Ii?MQe^Oq$)KFu$s1fJuQ1^x{;9Amz z)5%!+X*&CvgTBOPv)Yoq8%e_7n4&8B0M%2*X!h4@aa@PRLixKBR}x@?v&6{z^J8iN z6%Y|uZEQ2Rt?#gsg#qk4=iP{Xo%DEdq~iH6L;3cb<~row6<+Ii170&5UEU)Jkb~KUBP#w#D6s=);=soxlI0_ zP;f=*>MUrrzEop>J!N3efpfM?^tS$2!B{cZ+(UNV=7}g^GtJw$UQhzB?QBe$ZbclJ zl^ukQS_ zJQvLehBKcotGuo>vR31(&zhR<&TaM@8n*ZC8U~29kl-*(;D&9Mj{p7yj%YJ1rtxWt z@B(e-obk$w%g=Tig2`xZF{`;PG(S@H$?92FIx~xsxO?Y$Ic}xj^VfJHX&V zrQ;)QtP^NEG4zQ)&+zkW=hD8OeOGWI+?WY(fcVc7YA~@YOf&w(QLpX0-<38e(RkA z?L#wizMhHl>%R=~mi`nNHg`BmJ`F;<1!yn0O9}#Hga-ySTEMRqj2R`j+rB0*%c#L0 zw8(>?80#7URQHFf{Q2q9g4dEN)#1*6HC5W>mp4vNmJEl@EDt->AmDA}6%`JfgY@~Z zeN98~6xMwPcULkno*3CKin75=HPLu`V9Z_}qI~20({M7SW0Jt0!i^GUV)NYe)jV+~ zOf0Eb$Ps7&(LP~qur@tzf@dlLSuhmO1W{al4k`~0*tcx*Cc&oEUFuD{qXhhL|Cb~Y{9gAw)jw( zq?Gn-+x-IbHWW;572*-YZI62W{{8365u#h_-VdA3TPmj79Z4N9pfDnr`gcrHc33@m z(P~w)yX;|mS6}9J-;VD!?@R|qLVl^o6OEp>$MpYkN_>^)r2i4rWNjr-jJ?e~WCa&z zcJ4J6+8GO-bIIaC{Vy8D&E!=S3zvH;w~~2+Er>GlqWZkc`dZ&x490L@L%_Y7yuZI} z6G9u=@pwnMMOrNqRie#akIW~wSc0F5+?)TLP!aPyLr@SZznQBBgmW8kjdhrHl^VO_ zJ=xV_ey_#kEo&gr_VGpm6~$nBYH$RrUUWzqwb4yy#9r1Abu9?@Y>cn8A`~LC6Yy z@a_N>svf-_`Buq2`U`JfCM#2WVOnr7!}OWKRzlf zff64!v>$?(WPbNR*wUo2#c`^1zBDuQt#fJ0t})TO{garEY!3-+J}!xQv-#ZiU*9c! z6i!_7!RLeMe&4<4XY2)5Qh(;M3EUwG57oqra>fN~#*QGseVhtD+bS>7qI`BE9za3W zG3@d*a!H4Ht&HjK#-VPXnIjurI!oyRrKK1ZMKUfDYPNI))7~XUvGb(3=Dcp){Ob=n z-cYOopB>2&L}>4T@M~?ehpF6VeNCjmC8GlRTsDSXeVHQZR4>@_7QASU&7|@VH{Dm_ zu8IWl8{%#IpW(*q=c&f0$MFyS>gRn}jD#-y&ans4CijbmlFb)$Zvp+A_z}xzog{^m zOag1(#~l-RY&hr8mT4ZZ9JXIaVWh(NcF0_w=9zY_YVS!>`fnmrJe(m78puoF1= zyWt_NQ%*lV3jdu?TC;+$23lsA3X>etAN*oqW`&o`p7ubo95UDZ`D-$)z(bITaA|d$ zZ0FiRD=T~?|Nl1`8n?ra=(vH%8w5bkcZH3dpomT9j}@tePZ1w=o_CYI3r;~PocSB*`y=*tU_6`DvVXe z!iLvkulo)mz#FDIQPBX!y!Na`-#KpD!qC}w8en+u7#?*!Eq~_if_mK&oL4wOxKq5; zW5w=G{3@QlF!p=}x7foBCz^Gx4{nfE0N z3MGP^@+}k3qMipq<_D4zdKB{&6Nu};zLRDk){|$X56TA!U~~ZA&{v6b0#?P5gprQg zv3o@6_Qa0Q%mjogZl~eXVMFuG6f4W1;avNH4SdB_irJTWB#_@93hiKLD?qwtH*7Jh z0FAGV~CPEWmFLuP_t7noxKmey@LJE1h6U{xAfpkoFWy+G>8Yj zP=!5sUS1miRq1AQM}sI>|Lm^g*uT(}N#1FT^;-eJR{qY(#82RP|j-X`@hMVHH&!HhH%RZ@`6eUGdmj|vBV?0L5C7Ubk_r9Fx#er z!onJBbvWr$hOJ35{q?)g$$!Qv&;~}8!cRA{j=hHe<23Gc-79}a^RIKjSvOzN>CJ?s z60o3j=u4)C&NTCI9XR0**k=fD{DhFBG z4m6S#fu%06L4i(yykMnrx(OsYC$7!+kon(o8o3H&pB`9seIj@0Dj46pTX^V`YD;a& zmUtg@1_+nnO$g?eE)RM88#AIf{FyC$|6D6_uAz=+FD2$sU|e#@yCH}OSJ4b`yEz|`*rs0mc$LCFwqcxoXP(q$Qr-!xjs60Iow@^? zt{z-;-6~scbU<(fn;O0yfoBCy^P=7sXyZi+z`u>smGK_?#NvOO?ZvxSNSs>T` zjLDxnONSqi39OTFsRlk0q=knPu&j1PuvZmrpN)Pb041I(3RIBCDy&%HG0ZbH?0IcH zcJMriSnAM_bm1j#d=8gh8DUO=+A5N>2iQV9rveA+$@@X|m?lSKh>)qcWZr@n5w0!mC;_HiqEWYMTc?8{9 zimz|vH4{s60e8X>5zZ??vXqdjNkXgV&fbWvC+_-#Vd_kD@oqI|L5vUT- zyH%&OgZOR_#%Vv3j(Zq-j0p;<0lo%o&QQX8$N29W_#|z5)C|P`MzOR+<}GwOq!$hn z$g{;U0DPI4pV;go|Kcp=T)K(>Fq6C4_~q7(9l?fwyzu0|O~s^|tQ;F=n}1H;J3T^{sK0u9mBRm^ZvyJ5LV;OmHq| z)EpwTsCz^ap{m?{a5gCN=`LUk8J#~I(NMj0;0sw&XEjU?UTPq89iPp)qV&)2;ftDU zRpQ%p(Zj}=8CtB(Wx_eV%Zwl1{Z$oV7cZA>cw8+Wo>z$%g-loV;zL0AcE194Ifb9Z zVpS9wGh*j=5P%A0*%OtNV$}{G{=&zG#U#)%z({(vc99*m!uTrH_3-At;HNxa6F~`; z{fN-BW-&Lrx@mJ1d59&mZ(WQ?LV?CM3Z4?E;yM8nW9`rPKS2(5kX^@zKu0R)XngMZ zD)ed|5ZGX43K)wJ}$4pC$RU@c%BLGmTr zwCfu^ZW*O=!xCah^IgzOcd?-Hi0Qb|z2Hbe35be&fV`r+TK=q=c%oZ3^tvKov6 z!u2fY{!p5?ndh{jx5{vCq47K;15EN`;VS0fH2f$8uh7i{vBFs;=(;$1rtdwVCrutN=uHT#MP<>dt6{AoL0J16}DNxSb* z-b_}~{%(A~I&N;p%B`E2#)f}>gq!XtmLMkZy9L`R-ZjvhC4Xjco(BPq|G`;kyYuBp zC@9hhFwDtCzsAkNC`+8r>u((u>^A-b`1|VP0z1|?f1D$Gxw_gH?bcgnKzKR!_$qZ{ zV+pi#SWNGm=Omi1-GrRSAPii zIV;iZ!7!Ay5(TS}a~j)@73SBz9}sM5oe{u-_S_2Gd644>L{pmy*bg{E0yQA>+<=_9f7+R^h9OW(O&Hp$FJ!!3GNNlEj|l;>FYczgIm%Sjh|v}f z+!~HX@?NCl13x7A&QphF6)$3Z^@x9-$nd+{apzI^P;uq9C~J}-D$hZ(-cvjNK5#|o zIrZ2JeiArg}L>_1xXQgmyndSn!z2HS_SEjq@5} z7DIk=mWODI$33!~v-5c4EI&M>7`-!Bp3zAhwBnyFqQ({~rxg8aKch@>c#8d@auL#N(8}K{|E}GuVLaG!$1d za}+47*5*5)M~pFN!mF?QXG*~RV-JdV_|B8kn}#@R4Yv*rN5=U@uexy=Fj|!Odk}ri zZ!1zwLgX$*$OZ8cRk5!0yJ$i0_bO|7W6Fx7A`~>vGFoh__UITE#9Q|?{pC*&F(hE% zTV}ICu=iZH`FntAKBNq+9iOn*0*21(kRA~c8%?e+aN9)edV_Du;U*Pcuc{2u} zqisOo1>wG$u^&G#$a#w5;T@2Qf0w|oLXic!K*9nm6aLKg*y#MHO}`J*KtE5CI}Wm( zk@YeFI4(>xvw6~jES1q!9P|Bi!B)Lu1VGBpHj+do#e?bSR&JxXk`!95!n zpC6}7rom^x3T#*L3yhgJ?b|B|K_a)~rNy+wjBfn-Ec}jy=>;eo!N3+ubr67hue*@O zF{HtHu57WH@aGCRevZQF1WF=(pqj5Zv*gpWz3KCmrVj4qRPo*Pg=SfxW8QeVoU3ds+T`?z_y?0zq9 zh(MKukt%xR8r9)#A(a&l^G&%3?SQ>!qo z@B(#lKW}^_GJHAh#gtO9yNz)F0?L6kB!)Qbgk;pVGQDn}Ch-Ow%P!+g{xq4n;%Yyp zq=5RVP&$l&3iwQQ_(;40!1yHRvK|{qT(-@dF4(5JlW&`cOgdFkxw^<~$+?;FaH$cc z!=sZJMTykF55jZ}mV~%BLjHtjfE!s$huhxZF6Qz&RiFz`uRHO96VcCBxkDqA1{V-k}`Op z(osyT9Ty`Vk60*nv)3X*K1Z$(E|r3>)(sgk9VCk7xxTtkb@D zVg5|n`F~-hx#FDoaxU@q-ITQ?p*t^2m*vGb*(vX~*qe^8zg;71?`;PTCaQj>?T6Nh zmR8_{2^9Q^C-ed?Krq62Y&3+fP=mLNDilGe8Wv{FPt3e0cWeDGD#^Y?J#NZ*uDm9j z_C(@T^7q<2b(WQq z{k<(8QAH{&{`C#WqAlvmPdi`hfEV3O=byIp7wx-VnEsks0t{FG2$ogNoBr{54rLb8 zmip#v40$aLo5wnGH?%bK)R#(|so@JZB~my;M6kiMH=@za+A^<&2d3)Z*#o3^b_R!; z#$Vc>5O?;1ST*kI-aY7&ppEj|YQ+3ecdV(ffPfJ__ywbi4-lUQK}n?wD;e99N&J%l zgh%@pD9v2dNUTyX_g~sqaV45(02a|8-gSpI@8b}@d)MQeiLh&n*h`9?4}@39KqFxfaZVaH>+5jKq1g}w zJFE!kCC_`qUMQ6#nhX!V&&c9?=zNAy#U5X{Aw1>Bs@`EWs6FZOwq_Cd`hfi~V~D5j zKz`E}7@P0@Z9otn0pDF)&}GFBI*v&li9G;x3{D;c@L634^wsiTy@b0T!U=oV7`Clz zF!I5d$$ngEWj-&ASx>yAL%2rra3NJrW$jfa65olZIar1G`4DJMj;mGy%KHs39A~;K ziE#&aQE#)j0}8PL7O)PRqzynriU@!Ok{FbL-V#AQP|yHfyCT0p;fCZ&^^J|VPC4Toh;5Z@n#-j9DcFKh}S+(|n6WTZs=I)+q zi3P?za4%Q1pGPl5cR7Ej5B`E z{W5k20Cs9)saBv?zw7=;CtShL(hc3bR4i@Cx~Ksk zm0&?6ZW8!o#lLLl0}$<+WSDhN00)1T2#2_)a_c>fJOsS7SYT^FD?ge=FO>G)4f4R!Qw>Ux)1k zl2-s6(iVmcley#k{z`h5rRaeo(2vXKJcq=bP~~^_rC?qOTDxx@GO>BP{GJu?g~UT* zv>vU)UD}*;Dv%X>-6=P=G(tx6p+&p#tc!!uBeNPN)3pyLB5Z=qxi9@(BoU zb$~Yes7lJqwWM@N+%mjr1(Nosn`zrG{G4>&H?&J;KR*t`=6!w^V z5U8DTUqW2sqs`Vo?)I3D9Ym|cVlE-<55cpRM65@Y0e6V!)6Gc?_+~Ow7`n;{a$JGV zSK*;1c`AdIwE8-U;l$AdkCere3K*2N>UZPm2H=IE{}UC_%WqtkHf#PvB>QX?j0T{w zf{^Xxu@Kd7Bv@A?zU~0p!I}1QjFZK;(em<~INID~&S+SeFJM)ipFMO(=Km6NAV;2J zHm-61pbkDC9Bcny7rF>^t>=&4Ls#b1WbKrVJF~`q?ed`cTApax=i1MFtkt4`?v#`W z0?nlbH-?xR8tZD^OvJv?t1}mztfK?T6a2a=u_G!TioPb4UQmKZF5tecBe)ZS-F{m3 z_x&sd@5l^OM(^ySH5J+cwxra!EXQPL{QLK0a9=-mn*>pPCn>0ZGkHaI$-!S6Cq@Sw z7+!#sfSBMXt@x86B!)dOke%|vuzD6nbU{A(4)&s64VJxmO@#AZWu9GWU+}FPZ~+2J z&~t0D_R8;f^GF!1q3wUK<&WM$WBZ}-q|X1cG+1`|uPo$bQXv9%dogU49Pi`N)%3!G zonof<1dnxDKJbz4Kf$DblTD_8>3CC@&qS~&^**ldE6che$;cVe6et29X;_Dy?6<;< z6yL2#dDY7Og2VmeG-$sRhuXcjGsDNQ{RFg_D)LWfHZGP)oxb8kitPOsQe+8u0B_|P zC(|?U`sPf$R3$!u&l`cSk%(lR(dAaPa-5}Apd*cz{U{Z0WpLMEB6Ape#9~#>1$pnm z1W~kudEG{SZ-}E+xHx<}K1Ys#j^)}|PgvXA54_?o8~>J0&>mAo*NdoZJMzE1_EK4jCgLahemePY$H?t{2Y?1{>TfO7T?=DhEoP2G`Y@%KVQT32_eo5t{c$n9cHnH6m#)}g@3m_th7C_I-7x$ zRcOd}j>f()82dB-c?^qndj0eo2?n0vx91AmFM7aW}n^Vd!_4pl{x|VTmt-@nK1eHg@zkRdXZryZX33z7P`=MHRXWuVJ{Qh{|G=pSI$M_WzU9UAAQy$CmQKjxm9a^dxf}-(rfn1O9RTc8Fl^ znTSES&BnqU?U^5eB=#tsz;dis3GlvE;P1MD6!cS|guc`0fH8}lS1oc&=O#;O*5XM* zz=JewsTytB4;Uu?XhECY76ely>lwl`fSVtKVwV)a&XMUC z!gvBI*~Z8+2U(o~a{I;;pSwA>?Y;i1DnHIj_E+7otr z`?bbS&~<7e_fM1^BDfl1&hD@)8QZjaP*B(FUs1G@BOSmYZ*qhHoTT(ARLq&m{c$Au z5~U7+caUTrNRmJ=jSo=HsYnYTVetj$$c>6JwAo>>rfFtm-wHu;GJh8-|6Z#K zx_fBItLDaSBPtAGLdAt6(EGD?qul+8^!ri|1&9$VBi6?yE+#__o5%7EDXapH;(dHf z?=*PPgLhqdj2B#=f|sfe_{ic%NJkSt!a4$7eO`1aEi=rEKmRu&wh8QCV#zgTe=r{q z|6IB9oW^sLj1heOg1#5>B!ru`3;SoTd=cybViA_9bTSi9@<@aprT|%pw9hBk_ebN^w?Eq_A6)&wh2HQ9{;Lq07XCsk(p51!rkCy29t820xr76Jb(o~;v_6@~u)DpPo z_L*2ndA4AzWUBoX9OQjc9e~p%ffcJc0qH;);t0Sh<6WTd89_y%09KpJEN)!_Tyk6i zYog%v>GWM|H79gSwsGpM)|V{p(SW&q19u%2?6!v+0X^jA7`a*IfpZ?Ffh*K)z_rL5+A%>vhTSYPI=-at~Gp$?v(ff60+Q-%r{ z>>YQ)qQ5KL9hJOksP2mTlly&% zzF4U0Hw9!rZKg(6{6Z!Up4Z6WBHS~FIR|IF23#r$oiw?9dRDp;U>#vD7CkGGxEZE% zD1mAGFcUy7=9t6=e~EHd{`M28v2+1E%KCrTb((qYZ40}QiNc#;0O@#$_bX@ ztX%nj5|@;#%=2>H?10&=8ydcbEWdLus8&CZ!L~E#AhZ5qyASRneaqRPG}VyX7&5f) ziPqg1t!KMRTk6)#YpI7L!eyrJQNVSqdz4ec5E>5S?(Ph`|CAgE<`KWn7bEC}HA&kR zzZHBVbuBRFx66u35a}vFQ&sMqV47Zdx9=pWU5i!r|2M7hApty2DXpX}(->jNSyo=k?9Pr;2%Sy@c2kJifE|Xu(C0K(J6_IwJZ}2saqju`h zpla5=j6pMYd$}!Vg<++J4p}I_xl6GgA{N? z)^06#)B;Psj^Mw2@ju)C2>u?;ODy5d1(cp7Yhh>!_|68g3>E?GQJvGtJ3F)C-#3!U zNAs;z8FH-^@tju<_~GY7>{v)E71un5J-&m^CqSHVKbV`PEcB|KmaLnPj8G%Jy+iHx{oY>-_= z)QNXw`4h6mG5c(6MqVS;_z6R?;OZ-vy^7HUK*R0VFE4=aagE8fqD%;LpExIu*wOF* z6mW_1Nz08SNw}F+e<4TPshAj@S+B8pS0;I9YJ=c27`TU49|FsshW(@h5z(dyj&PlN zHJ%MOCZPIenG~sK} z5Qh-X(FZLG4nH1SxjvmFLR0$a3A_Q}-a8h0!c_i^#G1m3EoS59=helBAgU4WZue@1 z@jOkreT9Z}^}BW2+y$UI?N4gxM7R9M>6?(;mA3*)qsnL>w(QPwn;2_I5pf>x(&k=w z27c0L;+&^NDwxBcRUTvitTgcGW!`O;XV1LdY%3?Cf1*+YZ7m4;FE-|?2YB$!r8!AP zG|_t^oJ;D1f!<@+O-_%ioGiH2*SpWzN`Bgp?_7x=PQ@A~w=JIeSVZm62dytTvdzGw zmmatGf}=IKI)X7f=q82FvwHw`Y(PnBJbrj2LVEpm9YH2DB|qSWf|8U zuFaV}$qB#AJvq`>og12VY{@)71p|A|*7VvI}wCq!XNR!$8{n~Wku4O|%-knaKW%{n3 zvq_xbX#j`FG|pP`EMO5iNPmd-Jfl@F*om^jhhmyd<-U~1iZ7x-;>+GLJ78s&WRL&U z02V_z?~D?L4l9c}Db3wzLxSkelMNQ$tMM*;-4-@I&(^ zicUikCYHFE5t$CFAhmO z?rtVKw^DAr=0E&q{Q&KmzXN~ZFrXKc^cbROf8$)ED)Rr}MF-JxL`Bb#ndlW0SXe{?-Y*?#86B7;_p0DOl4EratPfmvmYAki4G#Xs~e&@lGvLkxb>pa602@s%x^*9!YXsux6rG?Y^|7o$UZ>#!$K zDj+M`D(`!QC~@9tkKTN+a5AQ#`0sx5O6Sb1K8UV8bw&~-NPe+-dU6-C(h+RG1U+R{ z<+~S|{2xhY8VKe0zVY+S!q~^YGZM;PDwSo%lJ-!RLXD`j2`PlkgNP_fq9l_N6-mjG zWu_u3g(PGdi70!v!OT4W^Zotbc;OAt%z5r}&V60i=l)k>bKTDAPfi`f?-6+8SONP2 z>)#MXkYIlb?JDkvK08eo8_Swi-o)D(TMO9fFua-?*wQc>i4|@KT>l+qxMSWVFV%}i zWz)kJ8Z(?;gjT2}m;%*sg3mRgWr@`%hVi&#A6!Y5)M=CDJAyN!AZ=v$)PFM0J|*OC z%#@@40>jAsFJP~$6rA`?P5ep?$CC2k{gtktf42yIiL^cweV&@c?-mT^bVko$vP;-N z#oCpUKaq_TDQH?aKy(@?eusxVV+$im09L3q92bMl0PA=+ARcWYL>S}Z*hb&6C;zCg za4`@w;D`6_70iy8G~A0IGcpAqJ9=T$v8z@QH&RAD^2;A#%X4`?Uyc)3*2%qK==1%2 z-sRp^mXA2sZc@hx)*n7itX>0JhS`4WE$+QE`ksH5!ZSI*lZT%6hRlVnW22(z2A~f* z=V+>Y5LIs$L0y`XX6S`^iWW-v{kLO}S7FIR_c=MQ+p z1=CZ)Wz>8K@87<0Ie%LP^opeA93Yz3vkXYg9Y7h6AhNd~HtK4xNK3f#xl5#kN|%nv zT+A7^K+XsQOD#%A0sV*Hn|%oSwM9=RLb4PV^}vrE*!(7P;C{7PBJ8rvR%15TJRm}7 zO4X4VdD)E}>LU@)HgY9SiOKNi#!@GjI75$GslFIB07-uJS%3%VJSJP>L3$2r=mcyl z^~^?r1HU!T+q&W9fD#}3f>1tKgFE5{zs+(@Z;^dVOzmH<3i;izX_>Ce~U3l+4!NM!W zk8YXc5?X%~921JIgV`MkCJACkgiOZGI`0ifS%yk(Y=uSV&xQ=b?!6f7i9R7^y29qc zjaveWZqkWYV&0`X!08V|hV1m^q5Wo!pI@=>U+;F>EV8V#7ZO-caH+~Iz9jKNocCUs zSc4fQ`o7WmuI`yGjIBcfw=r^(RT4|`5Wjm8u!F4Yx-k;r8cFEpGTj~LrHWDh^+0Dl zujHn@!DH)qRHqd8_fIV#kv5f#ttD(|#>(+1gn;Tjju2(TZ=pEspS2Q!)=yt?^6!lt z6Z7iD$uuGoaqsa6{aoNEg}v-KIQCkot>r{-p5p{ehl?`O zeDZGinA|GDStgD#0DP|X>HNo{fw<@e9D93-GqO=%;${EjQ*|ILii<=*5L&$8XQUs+ z$dYG$^YIZXLhrPS2HMdY<+BQe&aFkBk02;>jG@6=7rd(I6#{ZQBC5>1PgZBTl?yI9 zc=b;FrDyC@a*e)`=O1w|iA#TS>Q+Gb(MiR*^(Un+cxv0%Mk#g>++PXP7Qw@1>S#BX z->}-_3iT*G)+x+3Qt}HtdpmC^6nTfo`^x;7jUGwRg5n> zbsRt60g|bWE4rx^x1mL}69?8&m|B8veYb3VS_C-mF35r4>X&B#vtsTV`e)jf!owGB#~+UNWB~PCdny3WEy<{TiLmlS>92$JC?CsiV zFA>8?{;mu^RsxSFMR?XEUl%ylhxM(TTiyhN zlwuJ02XEIK$?Q|nTd&qKcFD=S^AXR(bZ!p9l9x#qYxjY?omvOBSMK;aw*H|9S%7&O zNBgF$Qxnpc$HHRB!)nEK$^+|WRC2BQ0UPO?I4oe~6wkG1EM0Pu_jWoudQ~5M=B9DY zK#LK>RQ}$`4$vR-6eMWs4!VG7w#VPAv-ti~>RNl`0E8!bglv3{d;ZOVS3@>+KLY^W z@3D^?aqdMm+3D$^^_S$~(F8~Gaxv-WRSg30u=Y_5>YQ-_{imc77#&5il)yh_qE2y% z#aaRf<`~`Rx2ivAP*V~I`;JByosGtf(*-*p99Cc~y;OHyqUSUT_ehQi!?l8#-{c01 zF-PW+RnzX$7f$5-u{vwKmXmKn;pxK9CN&DiT-WlZ6=>(B$#}HuSM$0BL1>sIc_{K- z&(@r(;y%5f9b_7gx;+ZrxQG;lw=};k{sq$G{2RoT0uiUA8w`l0dt^#uK+J zXLzYjOVD>{Q&W1pQDn~t0?iXN)<5KGf6_BSh0p^g|Mk|@6R}=AZEhY=ptyKXLlIN7 z+PlPR8&QH1`fnBlPIv}bpdo;;1Z2FHa+udyN)CdS1$x%Um)w0#iu>**MXQi$8GOT` z+_2B;(b&mt#z#NJ)vxtgqZr-a5a|h$xE9@g|EQO7=wAD=TPr%M@}6I`3Ll%X``r#n zQ!QZX3w-0Ms4Y+l!=5RENQ!quG#cL@uoI8F``ZVYzSHH0Kt#AY_}jdjXEBes z-0YIl5x9~23klz4k)aiS+~`A~eZeGmGAB`dDza9A@egFab&R1ltGZ~Dm!wNFRQGoA z$oj=HFvietpsAd(9uFRdaUhv2y_n$-t zm%GFp0dp6ly0_VjpA|GI-~`2e5!m@WCFvWAZ&z~avbsR}^3l#xnnnpcq-<-@Zy^-j z-h{>gb5QtBIM5U9XX>5YatsT$8N!5d=!eUA;2H8VKLhEXDHj1pGrDtfjR=7{6zwPS zmC{aU#Ae3`%rVUw!ORC_jr0X=x3zWy9bFH(rdcvo`%WCns7*e7QC6f~_t3tM^Mel? zPfdp+v}Xt|>u+<}bg0`d;Is@z=FY#e)ZYMbs(BR!89s&r2|%KE`|*8`VZZPuoD`{q zq<^sa2}L=|e_XxQy2x?ERDDqT-*5?X#=jbQRNGgPCJR!ZYBHK11G0F_AV`kw=H8Lh zgi*cx7}S3rA1O>_pGp>)|G)=Apk<3qFOJ3(W~zdb3XJm-c$)`xwS5AC{LeUAJrs2j ze~9Y$K>mjIMd*PF!~-N}e>Bf8`JSryQK6RX$Qn)%jEM^ggm3K{>Wa8?`4CxwFOnd<8I z8KJnA5sh)s0yEv6--MV!Ec|-pdD&Gq=v)2;(*|E9IB8R_u6lqpWD-t$5cxf}m*%(L-`cgfwe8G={bryEyYJ~eON zAKmP@FZPdAS}cxCft@WgN`Dg(fg98!a$K6cTS%?7o(8vkwVME0H92{|iE)YfCt5wh zB7-=JR&6jn_Vt5k|8F8~twMFm z&+Qhf)x;^7zb53EOid9!PxM`!cMLj_!=Im}!1Ts<;Iw)%iKU23=40jv(C0fcC#mU5 zWSs;?jp^Il)I`iAZEwrt)2zex>M8!J5R{*T|8s$;Q%+vY`7VvS0>>r zW>|Agp^heY7E z`*n=sbpxjfJ+@=5zqm8_^@4K$x@!%c<;4n^6K~_10U8YF+OT)crtjZ5w}}kLn}eir zH|YKWR?(ljyr)?vcxDcFv_NhG4&yRD6A>;AYGLbk>Vx3v;GoG7*CS!oj@P(DgA)w> z=}>Xx$z<^@{*eb-B^x)aii)1XY}Q;htd{Y6LpQ_wu!N{=6)$WM>HJP+uaIi}B2{&X z?uWgwCI9N!*)htT3BNrg%2sJ&c<@47X3UX(rp$FwBUFu@gV+lGASLc{!SAbI@ zGoON^0rK8V1ZS5k_SVBOFtN@}n10-EAxo=x#JXHOWBVA21_LfOmHJ{Ok%~IryyLUZSAcz zem88$)@PHGVECY{6Kq_8pDZ4F=acQl4_qjAci`7+PAkX)iD|$8y24xQw4gW8sUhw$#m|&PPkKkHF(ba)cwCDGf=ZRqESKKv((q{_h_Y(V~;C7V9mw29kxsJ;B z`p7e!ZJb}^LpR-k1^{xhVG<;GuS=x<<{S7rADT^bDBLqt-{o}+0S*=k7do>SwMXe& zq!@5&Vacr&7;Ml(P%pu$S--edb}{s+1WgZQCS4p)+s%1$C8hlHS9t6BItL}fE@;8} zmDuRF>^a3A?&!Ln!TOTyz3xbYR{vHBA^S+PITq>B}K!c1S7d^x%2C^o)?g<2y{#-#-G zFHFPq3WPe_>_FgRA)s9rL1t^agxJfjOX^Yt)CDgSrbmN`E~lkt-TJW2x-X^=U(ekc zr7PwW@yu}k_YQK`s*JhNnxl-5aF^R3*I4{`JKT^wrrzbq1}M9T^oIaTd=?u0>a1-c zWrj0*OOVc;{PPvQ&V(13h!BJWcutXq%e$uhTyTU}IO|=1+|WyCD2fKhqc*}gC9va| z0W=DT@De>prNtpQwcrY*KeZhDPzAU$TAAR(x@|$El7(2*w@q%N@-Y({@%ODCBNRLK z0KBt2XpFZO3QC8eLfRL}5C=f2{$ zEk6xwLI4|B)ux{c0~=f;m_{J=aH`#z<+R%jSeLWMa&};{&Ou&R4{5qmsAVGS7s=?Y zV`^wDWDb2Xc=tloG>g3aYrC^JOL}NqPNuTDjM!~cJ2tGvj9m-3Kvm|pbf~I~%YYwy zz|9%=eR(`$?gKpwJVLv_gG>W01f(1Xx`W|a2=xEeh+Fj7t<;iUtJTa}-fJb8-cx%y z1Qz8B$FNT)drb14@wRX@S>0b&qMTLCO0Cp7&5Mk`3u)Z80XSvz%2ki+Nc8)EWt7<$ z%^WgA{pl)xF^%WGGiYMG&G$Sm-Zs;0`PByS+*woxD`5P|@$ou_kr_dj%CEXGZ95vN z2p!E)r?ON3;mHK1jFa-98@p;ub=vp=%!2;j__94 zJ$$&}u_#RzH{4!Pk@?|DFd;pIgwt=ehH2+xr2S6hmK{7k47Y-ogvvsTQB zsYT+Nt{11m<#0_CIFBhRlCjX+9Rqi@Xb37A4}4dk$FRp3V`V)i`apx8`iHUUV_Yqb zC#|J^bqAoJH1=K6#=+Y#(N*%+M;XVk-1HuHm2af1B_uc4isDvC5)4CD39@l8K-;U5 z>G)caF4u@;ogxCd^!;u@B{10UrMow7|&TCcNiU1Y_Kkgr!|^i1(?ox-9P98ZczZC^Ta<3>C-x z##%>NF;k2KM!H_eH0Dvn91j_J=8bI>28*cv|Lx$CoX}UE1rFU zL(|m?g7n8}P$RtZ(^>@a)rQaA0~H_7XLlyv7$*xMAQ8)+FH^!z5I_a*_|Aa&S*Y&! z1yxedZRK=)Iujxr_!|yrNUemENzmxHL*ocL2f|y+?_!~KRC(85Q69X8O}n7(&UAfq zT&M8Z5SQcdy=F>yT7Hrm2 z<<+O@mqH)^+1w9@(_BtktWX6Hx%6NBjQIn{n;)pB{z*z7YwTDb<@4GGJ;P5M;K11> z%MYyOZP*ZkcuvK4ZK91cAh^ileV)bcKn*BMJE>%1_MAgStqcPqfWf2GCvN&|+cJ)TsAX6tP$-O)-(WFXl`KTDE z2u#K7`oD5Bd$JEE9_(AdOcE}RNJ1gt8mDt6b3pTN+&zXc$M3c4B+Cdm5ospEwl1+R zpcQ!J6`Md_HI&Bn^EvySQBTHPoA~B*(QgSS>TLIXSqxZL2d8sK&%?O07xYT(54ptA z1gkcD+Af5enGljClaww03|o~WeM(@~$UPxlP24DQz3%7MYY9cX=bcaMwsBWKla^ZX zgv@g^p4J-q&oa1qo{|XNjf|j!1b)Ve%U%HR~>>7+& zJtFN`Ol~aLLM=TX@X4Lm3!Fab?B}|>U<{+-QZT0U+^Xh$NN_A>qU;LCOQji2MV7cH3 zoIW<@(MHBi1{4FR)VcwdyV7;Q^ube}+xeQLcYeCeV+c)W971=LJbmgQ0iNGRtx`ll zc@5+a#zwJ*2K2J)g*WkZ#E)F0F{gv{09zhUbJ%2|@6LNWYi{m*WSYt@#H89p`UcHY z40CRz#r;#NvwC}qQ7-Y5{`rGx2KLP#a`#VT~T{N1CM_!8J&fvf~ zvs=deNjxD$+8Rvtn|s*sPF)l3KL%)rhtG~f_1BS_{tqHrE59Z6Z^9zqN%hCXo@={? zx&f!WF>03F(5|oUyz-Mo=VtS|90V;}mVXdwRV;-1U;OF#T>^}Tw+DoD_Oboo|H9U@ zS}MOE38KJrIRJi(ddHw?8_1>{A}IXH=@y`j#lz`m{;2nK zenGb%(<%i3*kPJg;+PY79~FVI?-iF<)~OT?e@@xRD0^JTP;ZakO~bj1oaGIdE%`YH+>L$P(((RZ4x&L6hrtv8 zOnrsPfyvfgt=)I<2$4o>$0JaHf6qD%38+iqQScKKXd}Rmme;9-7z$6yhq~jvAe~&O zba)NuFEa<$S5O-^i|dAXD1*ZS-!$g*ekM1i9$(K;xTgbQw~y?jtPJ#Skbv6`6eMr6 zNC)yR>dXz%dZ*`_?=@J#Aeh)|-LJ0@Q|j>^q(vO;^atpR{G%&gQ7?(o_BA(59#hB1maI{pjM=c^d38cK*$=N|6NXYxV9xChXW_ zv6Uo(w7lFi=B*;HcH$??QmnkvU0JOPDY}Wj*k7H#%hdxgAFm zoy_zOR)wNa;=CmV^n8%`&#S-k@5%SnK#|l#DlUziArlD8RKN<0x$q8uoBwx@ytjR! zZ~{D$^slLs?1A6c*4wovUh~k;8dP@zuU0|v2EGP%aGKYkG>>@&>a`e@jNI96N*a8a`zvPiWDlW%_TpB3rU<0nhR`4y$ z2WZrPEgBgG%t}=evyz3x+-r$P6R>mK^suZj?G5j3eay^I`ict}%FzHa7lnZ+#Xo@> z7ZsW0iP%XkeHt{x{gW5$%_^ZOg3jxd2n`u9@mGkwgim4=bpK;9l6U-9x*oiPtGYc# z1{1O4?Hk+au|AHIIaxfKN6iGxPU!3=-Zx*yp0R)RJz*c)bf~;}TZG1tn%~k_2@q%p z{Cj_WjxZ%?w0Oe&wh*0s1V?L91l68c!PP0d3w1qE^9@cFL6^=Pt$MfZyI1-Sy&Y1*m@YczTX%Ts<3=SwMKA$d}0`i zL3c3GAjVG^>;xp(hdYO6<6aE6!gZuo?TW_spzH9PrH=+c^50dFXMe}h#wNv`j~_L0 z0>({_&PNClc&gJDG;fJqsw64%T8+Za$5Fz#4X$Is;?ojwYr7YOk(2oT8F*?4RVF7= zZv*}@L3fexnLgqF6(FrT>bI4G&1ZxVsrhPk6UApdI1Xb0H+Zq0N>w>J-K_i882fXsU$J3gaGAY{3g$Te$AGQsS*0SsjUN4$!U{(*%cYd39Tckt5!bn2|ji=j%7_%RZz=hlhLHKTdky6foKmmn!_kWu zY2LM&2uR)zD5;GfYXq!sD&)X~d78W{z#(_YotI3!oAF4*COmBBxA@MkFE?0I;x zQKk0;#=g!zL*{X`yw6eqc|I?-fS2vG_klhLZ3D1;O|QxWMKQ-c^B(E!2=lLjX|~e2`@#Y+>BU zhtGUQ^0?9OBdXr&F}qpfl7MMN1J6mc&VKq6n|Zv`7*l2H{w*@=bADZMlJfMK%^%37 zHnO;BYiD7ov{Sr2BM|t+N?HhT)j7VFX2b5Nibqf8f$5B3>=E+(=V^m1P6D%bxiu?)@J9UwSJYZ?_OTYmBU+kRByuz zs>SkOe^OU`7j<4}vK}8d-?8wc-jU(Jd@g>U{xbsUk;NH=_3%{AYaq!gw;4?a0=fSl zxHI_H_;NB?m!LMJ^*2w5u<;Rl0~wBre$8`a?aJyxB+vfKaz)E8D0!cKckNApa7ohq z+dVqQ#=qANWaQx*9L1FHkuz}w!@9nN&o?gt5s9_GiigGbQLK(B5jzl^gHHeVakPCe zj~E`4qwB0u_;!EY0Y*JQSkkn~1{H$-7Al_ECZtpjkE|0AaS#AjI(M}0;B~7KC*^Qw zRtQ|8du(sPv^iZxoxHC#Y7G%oD5YGXrgJf;oi9vJP3KEnPjIpTI82{8&1qK+xy%{ggdIZEhl}{b_Bbg$!6d@rS7%tl8hh=#wD+RhE zL%}x&JwRh`rg-aFQSQHiYvGsw%k}8Tm>2-w zOkLf1nvAU*I4RVg^uebL0dhk=W`KK;@NH=W&Amm(;ECh%SL>bQmG7EZUzV{NTTKGN zMk6}JI(qND4RgJsK8rs%yw3bHm*@e1J)5`Gg@^Aq)D?2tJ|_EeI+c5Lt+7DlC#D`0 zEvntylV6|nB23|oK9-SpysbOk_FH<_sbOkjc_E+__)m~3uLuTI77<>3X1f9^Au@}JEDVn;FLs8yNgiZOox`&{Z?}&k*8aZir-|}$Ev-S zc&@!WLXCi*r2WP2sLEqv_rMixCCPkm@xuD0x)n{)2LEk{lmOk8tsG~gXsccoQ;N6x zO;-Hcp3Sbrvv%$YVv&con(X0vyt?W$C{|qTzjyR*w%fpTHutuk@-J7VZmILM%3O=@vw9@y@N7+pm3m;rmm0Mq`k?}`p z-x0w~m_+W=hR1`fR5dbVYZ(K6I>G~mDj6N~n+G(H{-nFb7$cNFB+F%-OJdFw#PbP} zhGTg#TaP(nInawp2#24}%6hseqe(|#vDSFp?EAgT2>N#$|De{&FP=2C5GQV44JwgJ zd05o>5OleYF!pcc(62QyF%A)w!rx0r#}u}zP5T8iV`5}8@sDv+lgc8<@Da&NiGFGn z)1>e0e@Z_jU+X~l;OU&ciSQq|bFgAr7b0ke$n@~A5JruROe`ptUvZ*(cd_5)lYdK^ z6HXnlNl4nh%Y5TNvCj>+#BhYt@<$T@c};JUaq*$Y(?;DI&+}z}7He5sSn=JI`RWMT zQ_orXW#wQ->6_xKBP^F8!PBx&7n4&?WMDpSqSG=v1aRt+^LEK3+vG!<@ta0F1{iBJ ze(4Wg5mgA$>D*hIW+;IE3Z%p@g|=zp$oy=;Gj1k0x<%_tvAQ8oU*$Hnj@7eL;8QO1 zs+%@`Av1W5vf!MvhTP;oevCfiCrOyaMaMms7V!vB_B3n&5xBhnp3KjexJ3v$3_&@$foKH?OO#psme$CJ8g!%6?JI= zp58UI0qWZ<;)hpNS=vsip2YQXOskf#oE4`8T?v^V>LEwb4JjZxIkf>BV+;g)C#`>z zeVYE#6^V%a$TQLUl96D*iMnXs6=?ycE*uhqJh#lt@42-U>#qx&ZaeZl4~_-5KYq$l z#=B71p_gbQ99y1W%cRKz4*lAL-5amVF}~#O=l+qr;VaWOMv6#R0ynvz^l9Q|*!CmX z8}Qs?2V|OeKoig1Aoj*bh>rs_FRsP>8Pkdp>6z=1?itfz{yFa7+|4Wc6yay{MV&N~ zK}I*mOliK*a*Fd185@H$Kmx^>5`;{^wgxwcLO+%`6nteh{T4n*4g}JIpcVR2L* zME|Q(3P`voQ^dln^GW_RAie(c=-)CnyrdXoZAZW|o@9EYaQNJ|`}7A9#9bs)nzxps zzYus$^;*E%S9#C8=GZQzA-(>-ZLZN7^UD0)VvK{YuBd27L^W%(^l2@@Me=}9D)9pW zViiBWbo^PXspk|Fd{z7Fq-d0|`C4wThN?_(BKeT)l9P^jqS@>+!~Fm@xeC*4895YU zFLZTsXeH_NiobG=ov)?>4P5rHug-DUC7!sjg+{Znp;@ooBZjB^J{)xEg~jKg zeLAQDLH&d#TCy54D7}FbxgU?)DFm7nZ&cH(M1YgDX&guVpQxJaTS(h)o=Gru$zL7t$FS+?Cj(dUlw`|LlznW!3{FLP)!MS>3sWLaNH2r$B zoF}}JhTp9_^for)BvaL9@>o7=0p=&4agOy5G$iEQo1_y-Jmd{_Sq96|lbs^Osox*2 zpI!)H; zBh_*6nDt1y#Sm+=Mk~*%un2o+L$#`92r7Jr{-5IMnr<1<#yQ7R(T@kFW_ICmUvyU} zO`u0)}jHP&GMy;%MsFB`uFQE+ZPAO-_8EnUGqm!4{DNk!pQ|nG8Dn#{8nt$RO=)>y8denfq zh5xabbAbHN}(8Pd3)j5BD%Oel%)p!3Q;y-b`ZB# z$UM(*KV~V4$0=kPi@Y`Qv;Nxn@6{R;gKYlnrX>|`<3GQq!`(z9)N#CD=c-#}Q0KrC zyUGZ<06+6!oeo-Tw{p+zi`E1BbCb$swqiskh1O-eXNdrBZ;O^!1wglDPp3o6aMJ@7 z+S`Emc<_3A;h%Zd4%lHg8txl=p7$pk zpRgZ=%xi!7DaP$RE;7c{fs^aWq^6Zi^}0-N1MOn@?SlLhM zetlph@kZ>^qbKB4mbbErg3W)ycH|LzM@gOtgpqo4#-D}s>OQ|0-KWUEgDW3pM@8yU zMiv$}-<~sH8k=Ykd!zh%k>4^?^+%;|{+ht;E%o;9D;I9^+@1dA#E&RmpKE?F=qPqE zDrO41!j+ieTcF(VFLd6CV{|e8i`SLSa*VpIb$+D_&$jowKh^=|P&f|P`T3Hk_tWBc z#|~a-&21{2D!n(6DwCDwk$Z-LAFEuL`Q-CY%!VzD#~H^^m}{FJ(PB{TSNfe^ies5L z2|#hTE(dGz9ZC(0;?N@W@be_{R)Cv!h5d(Lcbe;aK&rbxuFm)<=VSlYB=p);z#XIL znGXd8I_(ACg^q=XcX*ra9w)38RH^Ol4=wGF@X^z&XS_Qu<y|(-_q8NVV2<$9;r7O`J+#dM$jiy{8F1J}xg9Db6-Hlun+!F6|VuhioceUqC^< zW%uE`PX?u~9veMblP`kv+~jLZyC|7~gKhNTZ!xBb_ic$70ibnU36VhLH?7zfdavs3 zB(ZXNeNoXsl8n}{ zLH_LA6@mG(^J9i&`&*rx-{I17)Ed>yFJzocHD5gO6O-0q+z}iw6OZ~`$ZFz21JT2n zT@1OfF@xOkV2$*~jN@Y8$ePm@Sh_wT>)d$p-{s&*j<%rh9`!bVUbkm}v2N%HY-SGX zEq5Poj?L!W+M5G#XGrL#sf$;WqYsft57g0uRlo@$al@U|7D;rN4fYK`*jR7ufuL|p zO8fAs;HfqLLzq2|eOfdiZGfzqriVU7f(}S9>up>l_RrAJiJ?uPUkP{g^o-G=HN(Lj zXRn5hp1?y2&u?4G>-{8?J84D9PTAGooa!y!;ld=6v433u9y8t-A}f7$vs@@EYk4#I z-?f(*`n7f^H}+mc5kPDA(6YQ0SlCYGSDkvR{g%bfC27{p1sLc|mk-5oB?d4JW9({m z&y4ee5|0O9R>{M+4-?a1T{jo9!W0wMUBYChk9zJFJDu+PCZ3wXZAjUo4#ahKc)$gL{kG+n*Q81`;y5ItT_sySu z?G7-eHWL4)dcb&`&AYYvHNIa) z9Ng@^nRK<51z}3h2=9ldR%!Xsq7LkvzYniiN0}0FWjk6nR~4@gTN;?1uKF<6&9@Br z+A$f@no{20?>q6}fZ!H{Cbi5-L>ZKR(q!crSoSH(=#Vf%-#uH?O{4V21diL4%sI!` z4ohVou=gmF9J^953PszQ(IhQ_Rsu%6gqaO^h+z{Io;<1D(_6)4-(qv8L%s?2bj=(m zJK+hD{0q`~>*LXicyu(h?RqnciqY6LelcXhPAceJ{|MYw$L{T5U&G-cZ~QR%8+2wN zzY2tI0$f^wkl`btu)Q=l{E;opi7>oFMR|ksjS|~{KYI}EnGP7Y4ETvmc@peR9;%31 zW&qb#Gk+r3^Zk69)%b#}il}kBQ832^+sikHBQA3zN+utLLfyJr`++(_d-LIK)}@}} zoXO#_;m?s5+;yAB^<8Ty-s3L34?ABb7UtYq8aYHwq$)3#wodAuzb2BiOYoG#Dv4HTeOustgn>%nPg(S9?-)gR3-sgr$OLN3WxJqHe(F;z^JC;i9;?;Bm@WJdqk2s|vF z#4r&wjvRU zdfVDAY5fq3v^(@T)y;X|@{Gxam-l#mwSC)lIs7B>So3#-kB4Z-7pkY8jm??$T@Nfb zO%=L5$E(rbB%*ADeJM!Zwbzg`nzM&JxHW?JxPu;O!CO6=Klk%yeR0I4+C%Jfl2P>4 zJes%ND?%A0;4tS{^YR@qKr05UZ-eLc$^xcrsZ5w%*6i_`fT&CLQ7LY++9APO31<_^ zh3?E?=YCP#@xL9iPlBT}UJ|sX`zp_`l(BVp#XJ>PJbaV)pw^1vPg$2Q1goB+P=UTv zT;{0heF_{B)5pl~y0==Uh6u`|Y3hNU%1xgKG44rK9z6bAeWzuw6$}ZsUA=a9{^6Tnr+-AcenXSjy*|D^c z^bL{n;H@56vwY=LiWU4b^j&Zc@}iCVnD?Mxm5jS@xZRjUXY;&;4cYq&OHzfijI^Uv zNI|sY#c#aLtUg~DgDWP(6t8vxISLk%zO5e*ZT{8Y_ELP^Yh|_<3XNhdQB|t ze%hMXS-1Xtu-}R+h}pU>;B`$&TAYcx{ASjgH7i=6y7X?=RyPw=bqB+D1? zf0O#Dd3h?ME$I4Ahr0de_77?a0uJ+*436KXG9(4!=hJb>9ta9A*-}{jL?$@x_9x7)oHRP$E z*Ht5mZJF}%{j!9c()*@F44FpHca@9(y+V6-L72Vnpv*Tduq}-a9dZlAK~ELLv-WuC zX8rG1_xH_ls+tRH(xhKAPuiMe>P2U24311nf*(eZxUkWN-Qts7JmaN5WD(W(>KQJ1 z^N?WeM}Ayufl|`dgx1E?0$t&}4*}Lj*50Klb(nZC|689~@3Lyt#f#cW0eZai!b&%I z-+5~PTWnTDAT0v)e{+jE#9;Ws=%mQtkx6<(84jrTon+toGV-D2@{YGehqH~UQ)UM> zMadgC{J_bV5>HMEw-`qcmA0xl0ZIojcSHzwv?rBB<|n=i?#e+N`t$XfO5zts!`DV} z!WYLIaOk6YGxJsBSu47=2&KKj&GOO4fSHwE-J6NWNA0osZTZZnRQ|m7$SA>0&LxMM zMy_|+_3+nW1;M#pdzyKd^m{JH6>@_fuk33+S{JhVhvn=1+=(^(l0*~Aa&d$i!$@#* zvC7)76NkcBzY3t#;e-K2Tg&@RyJ%oD7Vu_{^?3qAs;OW=^BjU+dtGAS{TT&UdZ#ai zu~LUmU!tHsZXD5e`-sTX{-E)f; z)_|?e$a}ak^GkxJ;@N@W#Wj5!=c7tieV@kJnthNhAbq<@?f3VHVM8*5ECNzRM;1q4 zFn;V)RtmmK2oVEclEU(H`}{8~mMln~`!`wsdUIDm>4Z>v^0gPfEK!s*wA-dVea}bC zyt1=la@bx&<_mA9byCdx9~OLd5U9#4XwKRDmU>-9I={{L-|Iu|qr+zz&f_B{r9$ZL zoK_a3BEM5!oVmhypICaa{Z+%gdk8w!Q672zxe!n$AWv^g3js-gp(8*kWNG>7H>&-H zH!)w1)!c`!pm*egIu`kt_$B=0KF|JAm0$Y6Prq^Rm*E*7Wtt7y^nP+oFo;e(x69>a z5D_=A0~8~_&qkq|G-bXTWtC!b9If*A29S~6u=$io>1rgD01Q`M_##0kH}P1 z(U~Wzy|_yYZ&jB9zCWrxgRrIEdKd+q9C98hBeqP;1o)_kpAJoa{6x0a*t;yIVaLeK zuokZi;zt~8bK^^DWoH*Lv>&sp4|uhb-uIwGroMLW;CKfW zJ*@5;dmefaVoO&36OIh?v|ms8M4Qr1jyWQ3=+O6kD@9RLVaUZknif-Re3Z1W+e*c;buHzlEAL%|@pN+EB$BWkbsF!B3hqpj$S3TB`4A|x7mT|{# zYF~Sq!&}dK*URv@b-QXISA+VKE@28b;~XC_7PpOxf;nvG*}E0@rmtY+I3j?RG(9*~ zBoR;I)k;Uyl3NOJryq}ouim%+Q3OkRypFNk6_ou^SaXC_Dzv|$?4!au@v%3WQ@$|q zLtDm2xjMW#fYS8WCImgQA>y1-S`UIK_bvmTkL~T3VL}!;wFg9A%eHxm1*0UcP|;$NANn;UNWonGb-;K_bJ9Jj*_nAy(Eg?E@U@xS z=@KE_3>&FA4H#iBn3wQ{VWP_!ZH*$r*#yL{II{}-`IkM+N) zHJYiTOERrzbfeMg?@Z5KQ7mILtyE8=)Sq3iw}UJYA|+SQn0Lm7AoLz}_L(FBk5`C? z3-aTRxCM;6{lN7exnI7TE(|F^PcHUre~red26<&hXc<=2hiQffL{+&~>3$1JKUTQd zyq^53TiJVJK7Tl^nN_5R+qJRPO0YJZx46<_X)4lWi*Vhp6)X3QEZ}1thXhhnt*P7` zm&fvo^UzX6q+R^-NNe7IdHG~fK(MNJh4k&ibCp4rE4Llvd;I@$c60?--VHEK4V4Hu zeFW|y*YHT}wbf2p{IZ|;0`~#6?zrcjkBGKmo+b_45~G|kj_U86Nkn1K`v(K>lB=^% zM?7o#i9_t&qS+g1-Yc|im_HNZ&Tl+ct5*?>kJS$vG_~!>G9&w$goD!Clnx0=F)-iR2B}Gj33h-j{RF&Lfnk--8Cz7Cuzw_- zE4=ls&eY~#<{i9ePg6#9SN7Krl8T~>x^Cc+?qGS8)% ze=Z&B_yLSDk`-(_J;EDNbd9b2RWys`!d1^b)s|KzQL!Kx#&JVeb}qu=itt4H+x~~- zc5c9AJb(lY_LL>v0_~^BG2^J;Lk9YD?U=pGZw2a+;UoM-+~c0~^u7?@{zC2hvD7ye ziJyU>4VG$CEiWJxVmxu0seVsH=s6fvrL;x@QU*BuBk&n+3dMg}{U}AV{k=CtwSD1G zUBP&X#bkGLikTW}<-Ag`Qu)HA?u*OapSKr*Pu>d1n?hG+wK)LqnQ^sFUmicN??bo6 zd1`m1B&x^Do_;t06*;R#jx)ce2NgVft$)|}4aY+sPyWHOK3?)q#ro$Pc9#aRbu9cb zR?K->sxZE*qi_zvj5_CHd%;K%;kh)t{l=zM6!&&a<4>bEk$se~Qv%m@6g5nSvP_}X zHpO<)!WIW`QT{e>JLGZ`jsrWjvuowX$Uf@9&vB{CfGH{oH3vmm9?oJG3UHKYhmV^H z;$P8lM!CrcJM{1z~>#JoXVW6+bz_$nZiCvV!6+C&tSnOpNUiI6TyP?FS zQIz4SFEX=KhdFw+EJny%3ugvbbJq#0i@vo*>8s!VzO;Qj;V<6o14#hN{ys$d?_GY(EIKtyK@$}I&>?<3Hzsmcs;RB3TF0L?^A zPp`OJ1~gj?OkTd>2P-hsnuv=edOkCB#>Z6vzqRU*ld)lwh>4n7Pwx4m)x+~Wc4{M# zT9b+JAkps?%gTLSajYDHMi?M96RrP!)L*PfGcu%Bioexz90K85a%83B6)i2TCuyVg zt$1JHDUfw=iW*jSV|2mQad$T)cUP;XV76lOe;bt>sTpD)c8Vmf_Ai*5_WNHh@d=RJNiNaN_fjA2}jHt2QyR#(uNNoCJ+D?YpI5)qQ4f0B6TR? zNjM?rmHr>e@I8bFN0#HtVueCs3u60|4K z!SDiaFeGxq{jV#2vJ`0wH{b{BYFOUSxZbudgF?-eY~&75<>si__EN0$Esw+PD^mGR zLUxPQh?9rs@AzP17e}aq%T5{P?7xWV2tG#YPfj*G>3c@p8cpU;>At+JO1=7%_r&R& zUq&rO;K<9FdNxI-UJM59AnGkFM-8yQ&js)MdeTx3NeF4WGwFxXXrbk`Fylg!zV&?| zcyBOt$D*)&^<7bd?2~+q1^s}2e-aZ&31vnnS0;ep0bDTo0LaGQ$)*>jERI#xF1*rE zU&~mL(Tk+y7y;#?ovk_U5wLzs$r6h`^RwtRG{lb(*)KB7`}Meh>c_{64!J1p9y(C` zG12^1)YbsP+#KFUn4uu-(BC6I1d>}MjOk~P`mk&Tc+}RjEe7gwtIrF+?39;QY~VH7 z%BiPR9H?8mEljWZX!tMYk*=d3uo#BYi`4zH@p~jBId9eTZ|23FjnypbAge^ol2EA1NrWr-juX0EodIYB7>cvz53 z_};j}kcv^5%9_Q~MC$9LX(kc?!G_w}-SG5z`=04!JS4c2P6Mn7P=5o!?YIh4a>S_y z1?$4wfT0{4c9wCXU8RX?-)gk<7n;$|2npV}VHaT9eZ z{t546gtyEDU(=JcrkO%93S;wPRWT27VF`Zis(0^(gwrOTacm_+a5F>F6oYcTqWFq=ut#)+G&KIckoh3oBPcz|m z6?FQ^N@>c7cDURe%X|b(IXbl9$ z{TH-b&h~9m!xK6H>5rJ6_(e}9&Xq3@Xtyuk2RE=~s;~Xt1BD)aI`Fc416U9=&SwqA zONRJBqMoCtQF=+{SFt_PrAhDome~{FQikj~f5GaOczF};o6HlS9#YP|hrg&K2sGqv zp-t1%!=*cOrbrgAz2~CfKFcVCD{uH4lFNJ=LaFe91fas`holZ|E)&BmN@~o*6sjPy z#FAC|?8WA88F!GKnd17#I{>vIh-L3{(Rp1R-{D)uOvixz!gezMkI=F|*It?RkI}jd zE%yEX>Eq66Nc*hL+sbC6`?tGhNBSp6UI{Pqv`#)Zm+L6&Q%jo^jbBfn1{GkS{Fkr! zLWy%P=jsT~z+gbZWxEL$3kB@nJws0Y!j~hhy!mu0%Pfv9Nsyt`Mp*^S(r9#92M5i3w;*i&gzxQZ}^nwK5lJ(${SkM z9C2SS%j(dF3<>a*b4aku`ex)1DBBXip;tilBBpID*``WFnl(Cdr~0Hi05+(2A^ti6 zJFkXDgn+-||DA!EYnj)@QgMGs79JYnp$oP);aYnuMO0M}aK^Li;c$(`q1xCn1?+Jl{kZB4&7eXfl%%?59*;+ zGK~p{H3GoVTQ7%CCQwa&jcrnKe((e8r|FXm#?OjX5v}<7$Y%k$c{wkwLK1*g+wVjr zjkDF^H{S?FLmvZ%zMYKVl~!f|BSy1-kcR@p3EIwgwxmi{&>b~W>t~d57Bh75N1ZLn ziM_Sj^W^NgjSm>UO3nJPcq|=iY6SLykY1K}5yOM4HCCw8(np90gPR~*mA7n>p<}2y zJ{_szCgob1p4a4yL$PTk?-aH!6fciRGWa?24OV8W$0Q0(w>d z{(2mc6yEs1LGN1myv|k0CVkST5_MQApN7p3COd^SL_f850`@pO0Ef#Dm8%>f1`wUj zf}j;|M1TARety~$%VHxgzyjPtfCStL9A>W-Fx^L7#jBJ|Z~;zOxg2i9P-OiPw7h^F zDJot-cnLn-R%XYtB1!}NMRi3$Eb062=p8R|O?V=mp*yqOz?QG0EPY7TcBKg)hp7c+ z0hotC=rO6twLX5Og_7#d*8^CIn_pCM=BB%waeF1o&8hB@^N8a;dDu%uKVuWGjwmJb zBCK2{v|^26Hi+I`{8wrnQuYWclCliosSmJPT^N)7`ayhcN%k;;Dzvh)Xj~-mQK+s+ zZ*P&6v0$X%$@BrwuJ@dJKMX#eS-dnlP@((#{lzcAmDXFqCZk>S+C*Vx;fl7T9{6bl z`hLsZD&Dmb=S@L|$U-991En!jn3vBU|0!auWLtu)$fOD;%4zqRpXQ`2Ke+3j_{uEQ zBk3Q|L4u9AOQv47h=K!jnS<|)r|i|=KPX-P(_*_{5fXJ4ctECl6$>AOa-w^;#amRcd zoah6Mw&-}>+(8O1xuZl70xmH!6(W2i_n!lM?ccG5z-|7*8&CScfznwS> z8b(Ug#utWBLDhg0Q_Q!A`!}kEtnWi>qWfXbD7%+<)zzrAeD|~q*f)!4#AP#(WMApG zHPOekodbj0in|K-fsDz7(JIz)Nh-M^CtDK2<tM z4RDG_AWrpBM~@2XY!NA~sY)*yScD?!hpiLA;ZWDq5BNLoWyPE~&3A!9z@c8DhI&?a zB?(3!GQ@C!o)`~7n_ac}lV+dvZyrCHHtTsB@TxGv4S0-Ne0WFj)ki!274#Q^i+g+@ zX{V-&10A$E|N4hyLf*Gt_~h%tD*8Gr7QXYw_qh^cYt@t<%IQ)(jOGK_;FE?@1Dv*-*5IW9aoD(Y7nRX7sCX`mHb&grPi{L3A^y3OW+Ms%^gqGC+6A*SI|I*5ZE3PkmGZJ3R0I|JV z|GQ#opT4Qiv+hhtXBNbfX}kh_B3643Y$>c?{MK2JYifr7`1m&`?&MMoW{uWMM-sxFW;%e;g zKJC|PLbQ!$=qpQX4hP09|E7CH%~9^ev_Rz%Izco|MTea~k}LAWOGOy7y08ec_r>+F z*WC&gpSl$Ws#X5rt1aM|-bI{XWn9(_sJ?bLmJy7I>&Xj64u&WdVir(DU?mNnm!ssz zNjQ~~$stFZ;7|PD_Vv&9B|k3oMgsLdO#S?3Y0e0?&&nRThB_&F8q`waSwr!h{&Jb- z{37`0%^kG4R$?L59Hu!x}nOE5T(f?!aa5SNBUPkg!*2 z?IE!D`={COdV`Cv+ARuis;%3uj1a-N=s-9dpUI4F$XI5LU;%yv1x|OLDyN4SHDgRj zDM2r_&EbYOWd44a<`sOn4O}AzGrZSynzpHoEB${&q5CdkCHi2@Nl$?Ks&GaBLa2H8 zvJLqc_dC23(o*4ow2>C|!BaxMfMt|KZSQD5 z!Ql(1JwqbC|);Vs$qIn6m^F# zF1q4&$ej#=sAo zBeEw3%$#=`0^D!!n>-5U$EJLkl1;ETe?fR{v_7+{-G;pxrAvTWQ)3HxB7w+mw9osZ zbzza16C**AfH%7;e+Dpf<|P!AX&74+-X_dh6K)UDOagw8F8&OtxK#V6c~#}rCGetZ zq-L`5emFZB%j0Rn=j283wqoY*vD5$H}-&33CX zVevyIz+-buic1MGStK!&jdABY(#@9v52jCL$98PTjh_a{olg(rPJsm!naR~V zmLjB-i7KgLrzPrTy&|X_|Hg_F!Cowf*}1CD5JIu5!7F3wv&x^=0?i>ar=lmn8BN-A z@=kwnV4HTn8Np=_v6I<6i~gwLh*dAMiT=!CzFF@+>xK!t=ivzO&D z8cMj59#y-m=hM>XpPiQ#zEX@^oz1*v`fH~A=IX>|63NPBM$3b(9OG#WV0K8j3QVJ{ zeMxpyf{EzzlVC`OJxCc&qHi?Q`JuxJrNJW#p&|*wpDzFs`mwsJfBScql-f8jZXQs+Eq3e1QKfr-YO7om>Z?rP@nCNb1+qEwIwemkDs8V{jD5-!s zoh}Pn|1)1vLQ{pMiG3lpH58(F73@s(CGk30pij_VWK)w7aZ%6tJ2}_ZxOeU+(4JFP zDZNv>?@b!7;6@Pb4V(P7a`g+Qr{8^r2Gp_wCs0g4b&v*X`ZmE9u}AhzKiaD%tO zoV1NsTacB1XdUFM0!*15v%#S5s-`yegQnYb|5a%BU9LO*v&9(%0^eo($;7Q07GZ8o z#X9Sv+)ryS&p`U)ITR2WE8&3E^B+jGAF+RmC)l#?e(|Z?g5YF{LQa2*Fcg8V@d5`g!C)t%%=b zG;A79>e4X~qVjWN-~ETW`t1HsBQ@z;x_Y56%c8y=-9*pEuaZOgx{Nsru!Rq&kxe+D z`@<6z1j}1uO@EKm9&NRk)(*}mTKJ-Mp1KY)c0H+Y`28PU7#@( zcC^J62aFh>Q+VVdENR5S!d=lB-|bAM(hcx4g!V3$EFb-R+LQm0CnaSz%PWGB73fbAmBZf>q2_p~D$b+w{h};U6$_)xmFnrfLGxmEuHn*DnMkL?eMd zy%yvIF((4YpFu!l6D|%tNPr-VCGgw*o*cG6=HG_ggRm$WN-=e0A@1C!d!^u8$M za->#(W4OoR(eV88=cI$1w)vGssNx0fu39m5v#@y*qqqi?luE*+yv-(G$R+zom+CA)1L;D%H3o>$@ zI5Y+@AZ09pbEt>TB&A)e)Oaa>peK~T$0#-SSe;K^RD@zt$N1nstHbbMkJ6@=10vwA z23&%{C8J^aG?PHos`MIiK8ty8#c%5trg0nrDd^HVMMRxP3{L88bHVP7NT2)5Jp#OM zZ2MfdKaGjX7A!XFkoq_2p@rJY>s9(b)b#~7KR>F;9{2uR)~vQLruP+QfO!azauU?< zetqWeF4}BWBf61rPwAWG{VLf}3$p+G?9)co{y?9R3P>K)$h}(y zFJE+CM1THn8a`F-g#@EMA-Jo4uq9W1k6V>yWj3U&<(S(V9t~0;nIM8B7zT}5oSwW~ z5Z>ROpz=ECJ*c5vJr3LBxJ}}?ZaMcU{Qj8h-`BHYM zaBR3~(x2e@-cN+oe$OItT2wLCgE*72RK%4WI0xiCevt71^?4b%QmHg20`tD}4yp}C z6wfl{@u~}d{ry7Yb*6q)ofVnAyJu=7;moD_P za)9eC`?zp_oW}-RqzjYES*Qb5!3E#>B(cMvUU7qPEv=h}`Dw<|52p!M5RT@JYlFII zS`D7RN~;y zhjb@h=~im!Q%M|x2UD8s;FeRBtMGBfNDw2jJMCddt@45GiOnf(Os~TOrVbmUa{I4? z14&J-$3#Fy;o4^;Kwg3rAvI1YNn8PAZEVjgo2eUE^>IKP<0yw}0mb z&BMIusiJ;-d+GSVj6cAGUe&j@H0L3w_r7#<*w^guE}-J)zw1N+uIL&{z=5RUwg!F= z6dNlFPsVb|C2I%af|r`_bU;yIYK>G`%C1MZ+!{0F5aRw8U;B0rv>38-Kd{V|+ul&g zCMnTlqwD$1U1}d9LgOa*j@$j7*wvd}ToQ!VZjKts8%GIJlXP&# zFWmjy<0~Kx&0MDe;&SF3e1#?C(Vdrk6nm~@&Gm0ZA&cbCGF60;9%6Wo`mkACi2CgU{`&ZY4_grc&i|dXW^WzW*1-j}2Mu_jp8QMJ za&f~gtUBnREK$r}6o+(WeR-oZ7aof;&FRpZa%k~I*>e~C2a?5okSOkMT#x#*`)5fu zi}>bc7`m1p6fJ2((>jIztGUR8v_p~lXbiz>GU%H2if8Td+@_bJr1#B?MpDQ@v7X}} zSDV7|T)w6KV$c*}Bqm5Cwirj-?%k!U3m_%$mj!NxI)`x;DSZ$(Q>cA>_3>`?QkoQr z{abLDK84!5mZ)me!Nzi-%<{Pm_0I5+Ejn1U^e&sh+i)e_^uN{kF}=ZN!R?*lA%-3g z-IjM;9_vW1Ipb56eD2wGdbVchW?JrxP<$7o=)e^K0k&{JGE!|pu5UAMX8Y-Bs2W8c zj-9s9U&=ui%WrMRLN#VzEv&S7F#-o|)Ajiy&stMn!bzO~p3#V=Z>Is?O0$kdJ)b?k zr7(?|KHXW}Y66`avA-x>8H$Xk+Rrg_d-Ga)pxWW@c8>ofWadn zh(b_UZa#>9jIQ;^0Kt7gWXr9(t6Qpn2}P;&Gi%!oau6JiH~Oz{6LC!mD&Bx7QmZSn z5NxuuCy0)lQW{*A7^9r`zShvDVQGdi@$$Ru^xMv%SWCBF?VA_ZmcHhyW^`yoMAEta zMDDZx>QP?eL%@INNWo5Czl^%YG0MfcZ`z?C#SuNyV9O{t!vt@%nmLC60aAqnTzR;U z=~bLKpwh+Hgd0J?WgitNlLgXTl`3pE8)XHM z5^S8T`*RmzW`>edxJ>W zUq^cGUZ13QW**0wbRW%;4#EC@mU!iiK-~#OYTO77cpKhkob#R_2IQT81l>m-*j+}~ z(BMAF1gT%mqby6(M+Z7WHQ1SRwBZtfMuG%iaSnma-f4)U+H0zNh~*nt5A4cx9;Ser z>%k>#r;i^v-OEBeH^4xNG%@KP$jXu89-(i=LgBZrqE#||d z_4KF8RdpTionLsc%$l2L>ZX?+0iEM%vWh-Ow+YI|L5V{>6EC%u*v+kRB%kn?G3VWa zQ%2;R5&ny)^sb$0Y>ygw`qAl=XdJrhp(nqDV;2RJ#9u<9%uT|h8Lk*O3Bq|nZ9ZO= zkvF@LO_gcpDHqq4Fjg&S$};ONFG;>-tCLR+Th3-n87ZTfbONyZhZ^)kr``5|) zi2H3I2l~iSV+HwCQ$<7})E=y*O(Gq}kxIK7dAX11&J%>6dxw{I6X>y#Ik?f06cYxBk(h&MG(q)SiPtS%WnEv>y-)=b8&s?cVn?U zSf=-Hk$Ikq2Cr=yw_+Qh#(xahiP97ULWiJFeWkyuy;RC;omV%AbO{XPz=-taYUxtX@wtf2~U)KR@Eg2R%;&PSw`813MqX3aqH)hL?2W4rG5+A z|BNC`k`o`WGHmdgPV)N4_vo|-poRo8Z|~zEu4wg}yWHkY=>#1@@>k+R-o

WQOwr zeybucG!3XCdRd1EJn0|b#2%%0pu#pI`20&SEYG-n2`JB(o%EJ?*x4Z;PhCr2iT*x$ zx4-sn!tk!z{!W4L1eJKc8d_d-mX6WZxG?n0C4H^0jNCZCN zYE?@tzEwPA&R?2#aTsZ+B9?FdO^4sM{fFFYYp>9-RUpMr4-6iNB2R;PBBuo*3ZgG2 zGhrN0%^A4K^#too4U<9{KSe^jN%PTjX_qJ&;f!kg8oNr@Nt>|T5S4ik6<;gXmb}tW zp?(>5;GY!SHv;u7+xuY~K_}*@P3*gA=>!--IZUe@%lYUA>EW#vu4MIXO&Ao@du2aL z5Uy>rcNMh*J1~#*5It{vO{a0Nr##7R9aFky$%q)~%V#=yqwJFxo{$`xavJsBLC2P` z#n51+fYAIQr% z3+l{p@FY8wl21JT8Al#>!>8Nlw zNKC$`qOy+;+$>O=IOs%@(vd($P57!xKjsSIM9`}s{pt{E;4x4y%tW(u&lRUSxuDoI zv)-jg3Dr6^(NszPXM6vWQss(v6gOQY*F~9s&D)-tic34NtrnWmY=H}W#IFZGCyEJ1 zDIfCdG_JCy-(#260ZS1dy5s_$*TRHofqSNTemfFbh+%#*32-?Rf21)#Lmig#K9*m9i(#UlPOV{meOsHNpsIO z;%L7s=IX_9@RDYFIM3u96-OBRB*}FA(FIbY|3vr$Zf(Xu4ZQnvzh)~u=-l7?W7^&F zh1pY@&-_ZrlO&KRDH>TEwu++V|NBAh8z)=BN3+mhj7s5OEZBnDZ&xiSGK$oeVj*^! z9eFz!69vG9uk&E?AX?LV-Ma-!?bNT$C;txva{4WTuJpt+A0a*@EHO}{Gp zD|>R^g(s#xtkUZVTfY)J65j7PcyhMC=wD$%>AQNF&X6D^&+ zyXc&YshEH5Db$@YJJJhB)ox)CcO4yN+pa2u{uhim-}{DzA7-^fPIM1N1QJuflrn7z zX+`mplK1riHqS>;?HgdFx2+~ygsjX8d%ZdeeSz&xC<`nr$F0|zW=&$GgwT^IM|3&e`@VyTUD!j1+6VE?fAJIS-p*Y-{P*s{-&FUk`rZHDNe|E#4vdAo&fgN# zQqy6uiST?E=|Pq4dhY&3_j$*+8=CN&7ytRT^9`IN z2jY_uU0t)h=Ut=19jLrfG3L!qPqAJ{xBx>@-ogLH_{Oa*s^DTHiAX)Zwa^5>)#V@_ z7svbct`KRz=MF3?U)eL#51+TIkNA6WB^0&n;W8Bj8{2qJru==^RrMg6WfitH7uoW0 zM4Cc3Mqo546W=Q@awoMjMXc!NKRfqA04uR=rvi^?Y_$Jt6~RUkd45_qg>C`iMy}D! ze|I>by%PcVCQs1e|NW6*`vCdqk@2eFM*M1X9A{Tt#7PG-O=wJUi`2wD|KBbD|4Z^} aJeJ~~sNZ&L7?B?U_tVuf)cmUM5dD7*zoIh$ diff --git a/tex/final-presentation/slides.tex b/tex/final-presentation/slides.tex deleted file mode 100644 index c5e1f51ba19c6..0000000000000 --- a/tex/final-presentation/slides.tex +++ /dev/null @@ -1,444 +0,0 @@ -\documentclass{beamer} -\usecolortheme{beaver} -\beamertemplatenavigationsymbolsempty - -% Fonts -\usepackage{fontspec} -\setmainfont{Source Serif Pro}[Ligatures=TeX] -\setsansfont{Source Sans Pro}[Ligatures=TeX] -\setmonofont{Source Code Pro}[ - BoldFont={* Medium}, - BoldItalicFont={* Medium Italic}, -] - -\usepackage[outputdir=out]{minted} -\usepackage{tikz} -\usetikzlibrary{positioning, fit} - -\tikzset{ - invisible/.style={opacity=0,text opacity=0}, - highlight/.style={color=red}, - intro/.code args={<#1>}{% - \only<#1>{\pgfkeysalso{highlight}} - \alt<#1->{}{\pgfkeysalso{invisible}} - }, -} - -\title{Miri} -\subtitle{An interpreter for Rust's mid-level intermediate representation} -\author{ - Scott Olson - \texorpdfstring{\\ \scriptsize{Supervisor: Christopher Dutchyn}}{} -} -\institute{ - CMPT 400 \\ - University of Saskatchewan -} -\date{} -\titlegraphic{ - \includegraphics[width=64px,height=64px]{rust-logo-512x512.png} \\ - \scriptsize{\url{https://www.rust-lang.org}} -} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Intro slides -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{document} - -\maketitle - -\begin{frame}[fragile] - \frametitle{What is Rust? \small{[review]}} - - According to the website\dots - - \begin{quote} - \textbf{Rust} is a systems programming language that runs blazingly fast, - prevents nearly all segfaults, and guarantees thread safety. - \end{quote} - - It's a new programming language from Mozilla, and it looks like this: - - \begin{minted}[ - autogobble, - fontsize=\footnotesize, - mathescape, - xleftmargin=.3in, - ]{rust} - fn factorial(n: u64) -> u64 { - (1..n).fold(1, |a, b| a * b) - } - - fn main() { - for x in 1..6 { - println!("{}", factorial(x)); - } - // $\Rightarrow$ 1 - // $\Rightarrow$ 1 - // $\Rightarrow$ 2 - // $\Rightarrow$ 6 - // $\Rightarrow$ 24 - } - \end{minted} -\end{frame} - -\begin{frame} - \frametitle{How does Rust compile code? \onslide<-6>{\small{[review]}}} - - \begin{center} - \begin{tikzpicture}[x=4cm, y=3.5cm, auto, rounded corners] - \tikzstyle{basic-stage}=[rectangle, draw, thick, align=center] - \tikzstyle{stage}=[basic-stage, font=\tiny] - \tikzstyle{pass}=[thick, -stealth] - \tikzstyle{pass-label}=[font=\footnotesize] - - \node[basic-stage] (src) at (0,0) {Source\\Code}; - \node[basic-stage] (mach) at (2,-1) {Machine\\Code}; - - \draw<1>[pass, out=0, in=180] - (src.east) to node[font=\Huge] {?} (mach.west); - - \node[stage, intro=<2>] (ast) at (1,0) - {\normalsize{AST} \\ Abstract Syntax Tree}; - \draw[pass, intro=<2>] - (src) -- node[pass-label] {Parse} (ast); - - \node[stage, intro=<3>] (hir) at (2,0) - {\normalsize{HIR} \\ High-level Intermediate\\Representation}; - \draw[pass, intro=<3>] - (ast) -- node[pass-label] {Simplify} (hir); - - \node[stage, intro=<4>] (mir) at (0,-1) - {\normalsize{MIR} \\ Mid-level Intermediate\\Representation}; - \path (hir.south) -- coordinate (middle) (mir.north); - \draw[pass, intro=<4>] - (hir.south) |- (middle) -| (mir.north); - \node[pass-label, above, intro=<4>] at (middle) {Lower}; - - \node[stage, intro=<5>] (llvm) at (1,-1) - {\normalsize{LLVM IR} \\ Low-level Intermediate\\Representation}; - \draw[pass, intro=<5>] - (mir) -- node[pass-label] {Translate} (llvm); - - \draw<6->[pass, intro=<6>] - (llvm) -- node[pass-label] {Magic} (mach); - - \node[stage, intro=<7>] (exec) at (1,-1.75) - {\normalsize{Execution}}; - \draw[pass, intro=<7>] - (mach) -- node[pass-label] {CPU} (exec); - - \draw[pass, intro=<8>] - (mir) -- node[pass-label] {Miri} (exec); - \end{tikzpicture} - \end{center} -\end{frame} - -\begin{frame} - \frametitle{Why build Miri?} - \begin{itemize} - \item For fun and learning. - - \item I originally planned to use it for testing the compiler and execution - of unsafe code, but shifted my goals along the way. \pause - - \item Now it serves as an experimental implementation of the upcoming - compile-time function evaluation feature in Rust. \pause - - \begin{itemize} - \item Similar to C++14's \mintinline{cpp}{constexpr} feature. - - \item You can do complicated calculations at compile time and compile - their \emph{results} into the executable. \pause - - \item For example, you can compute a ``perfect hash function'' for a - statically-known map at compile-time and have guaranteed no-collision - lookup at runtime. \pause - - \item Miri actually supports far more of Rust than C++14's - \mintinline{cpp}{constexpr} does of C++ --- even heap allocation and - unsafe code. - \end{itemize} - \end{itemize} -\end{frame} - -\begin{frame} - \frametitle{How was it built?} - - At first I wrote a naive version with a number of downsides: - - \begin{itemize} - \item represented values in a traditional dynamic language format, where - every value was the same size. - - \item didn't work well for aggregates (structs, enums, arrays, etc.). - - \item made unsafe programming tricks that make assumptions about low-level - value layout essentially impossible. - \end{itemize} -\end{frame} - -\begin{frame} - \frametitle{How was it built?} - \begin{itemize} - \item Later, a Rust compiler team member proposed a ``Rust abstract - machine'' with specialized value layout which solved my previous problems. - \pause - - \item His proposal was intended for a compile-time function evaluator in the - Rust compiler, so I effectively implemented an experimental version of - that. \pause - - \item After this point, making Miri work well was primarily a software - engineering problem. - \end{itemize} -\end{frame} - -\begin{frame} - \frametitle{Data layout} - \begin{itemize} - \item Memory in Miri is literally a HashMap from ``allocation IDs'' to - ``abstract allocations''. - - \item Allocations are represented by: \pause - \begin{enumerate} - \item An array of \textbf{raw bytes} with a size based on the type of - the value \pause - \item A set of \textbf{relocations} --- pointers into other abstract - allocations \pause - \item A mask determining which bytes are \textbf{undefined} - \end{enumerate} - \end{itemize} -\end{frame} - -\begin{frame}[fragile] - \frametitle{\texttt{square} example} - \begin{center} - \begin{minted}[autogobble,fontsize=\scriptsize]{rust} - // Rust - fn square(n: u64) -> u64 { - n * n - } - - // Generated MIR - fn square(arg0: u64) -> u64 { - let var0: u64; // n // On function entry, Miri creates - // virtual allocations for all the - // arguments, variables, and - // temporaries. - - bb0: { - var0 = arg0; // Copy the argument into `n`. - return = Mul(var0, var0); // Multiply `n` with itself. - goto -> bb1; // Jump to basic block `bb1`. - } - - bb1: { - return; // Return from the current fn. - } - } - \end{minted} - \end{center} -\end{frame} - -\begin{frame}[fragile] - \frametitle{\texttt{sum} example} - \begin{center} - \begin{minted}[autogobble,fontsize=\tiny]{rust} - // Rust - fn sum() -> u64 { - let mut sum = 0; let mut i = 0; - while i < 10 { sum += i; i += 1; } - sum - } - - // Generated MIR - fn sum() -> u64 { - let mut var0: u64; // sum - let mut var1: u64; // i - let mut tmp0: bool; - - bb0: { - // sum = 0; i = 0; - var0 = const 0u64; var1 = const 0u64; goto -> bb1; - } - bb1: { - // if i < 10 { goto bb2; } else { goto bb3; } - tmp0 = Lt(var1, const 10u64); - if(tmp0) -> [true: bb2, false: bb3]; - } - bb2: { - var0 = Add(var0, var1); // sum = sum + i; - var1 = Add(var1, const 1u64); // i = i + 1; - goto -> bb1; - } - bb3: { - return = var0; goto -> bb4; - } - bb4: { return; } - } - \end{minted} - \end{center} -\end{frame} - -\begin{frame}[fragile] - \frametitle{Heap allocations!} - \begin{minted}[autogobble,fontsize=\scriptsize]{rust} - fn make_vec() -> Vec { - // Empty array with space for 4 bytes - allocated on the heap! - let mut vec = Vec::with_capacity(4); - // Initialize the first two slots. - vec.push(1); - vec.push(2); - vec - } - - // For reference: - // struct Vec { capacity: usize, data: *mut T, length: usize } - - // Resulting allocations (on 32-bit little-endian architectures): - // Region A: - // 04 00 00 00 00 00 00 00 02 00 00 00 - // └───(B)───┘ - // - // Region B: - // 01 02 __ __ (underscores denote undefined bytes) - \end{minted} - - \footnotesize{Evaluating the above involves a number of compiler built-ins, - ``unsafe'' code blocks, and more inside the standard library, - but Miri handles it all.} -\end{frame} - -\begin{frame}[fragile] - \frametitle{Unsafe code!} - \begin{minted}[autogobble,fontsize=\scriptsize]{rust} - fn out_of_bounds() -> u8 { - let mut vec = vec![1, 2] - unsafe { *vec.get_unchecked(5) } - } - - // test.rs:3: error: pointer offset outside bounds of allocation - // test.rs:3: unsafe { *vec.get_unchecked(5) } - // ^~~~~~~~~~~~~~~~~~~~~ - - fn undefined_bytes() -> u8 { - let mut vec = Vec::with_capacity(10); - unsafe { *vec.get_unchecked(5) } - } - - // test.rs:3: error: attempted to read undefined bytes - // test.rs:3: unsafe { *vec.get_unchecked(5) } - // ^~~~~~~~~~~~~~~~~~~~~ - \end{minted} -\end{frame} - -\begin{frame} - \frametitle{What can't Miri do?} - \begin{itemize} - \item Miri can't do all the stuff I didn't implement yet. :) - \begin{itemize} - \item non-trivial casts - \item function pointers - \item calling destructors and freeing memory - \item taking target architecture endianess and alignment information - into account when computing data layout - \item handling all constants properly (but, well, Miri might be - replacing the old constants system) - \end{itemize} - \pause - - \item Miri can't do foreign function calls (e.g. calling functions defined - in C or C++), but there is a reasonable way it could be done with libffi. - \begin{itemize} - \item On the other hand, for constant evaluation in the compiler, you - want the evaluator to be deterministic and safe, so FFI calls might be - banned anyway. - \end{itemize} - \pause - - \item Without quite some effort, Miri will probably never handle inline - assembly... - \end{itemize} -\end{frame} - -\begin{frame} - \begin{center} - \LARGE{Questions?} - \end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Extra slides -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{frame}[fragile] - \frametitle{\texttt{varN} vs. \texttt{argN}} - \begin{center} - \begin{minted}[autogobble,fontsize=\scriptsize]{rust} - // Rust - type Pair = (u64, u64); - fn swap((a, b): Pair) -> Pair { - (b, a) - } - - // Generated MIR - fn swap(arg0: (u64, u64)) -> (u64, u64) { - let var0: u64; // a - let var1: u64; // b - - bb0: { - var0 = arg0.0; // get the 1st part of the pair - var1 = arg0.1; // get the 2nd part of the pair - return = (var1, var0); // build a new pair in the result - goto -> bb1; - } - - bb1: { - return; - } - } - \end{minted} - \end{center} -\end{frame} - -\begin{frame}[fragile] - \frametitle{\texttt{factorial} example} - \begin{center} - \begin{minted}[autogobble,fontsize=\tiny]{rust} - // Rust - fn factorial(n: u64) -> u64 { - (1..n).fold(1, |a, b| a * b) - } - - // Generated MIR - fn factorial(arg0: u64) -> u64 { - let var0: u64; // n - let mut tmp0: Range; // Miri calculates sizes for generics like Range. - let mut tmp1: [closure]; - - bb0: { - var0 = arg0; - - // tmp0 = 1..n - tmp0 = Range { start: const 1u64, end: var0 }; - - // tmp1 = |a, b| a * b - tmp1 = [closure]; - - // This loads the MIR for the `fold` fn from the standard library. - // In general, MIR for any function from any library can be loaded. - // return tmp0.fold(1, tmp1) - return = Range::fold(tmp0, const 1u64, tmp1) -> bb1; - } - - bb1: { - return; - } - } - \end{minted} - \end{center} -\end{frame} - -\end{document} diff --git a/tex/report/latexmkrc b/tex/report/latexmkrc deleted file mode 100644 index 23aa1a481b3eb..0000000000000 --- a/tex/report/latexmkrc +++ /dev/null @@ -1,12 +0,0 @@ -# vim: ft=perl - -$pdf_mode = 1; -$pdflatex = 'lualatex --shell-escape %O %S'; -$out_dir = 'out'; - -# This improves latexmk's detection of source files and generated files. -$recorder = 1; - -# Ignore always-regenerated *.pyg files from the minted package when considering -# whether to run pdflatex again. -$hash_calc_ignore_pattern{'pyg'} = '.*'; diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex deleted file mode 100644 index f8bb37b911330..0000000000000 --- a/tex/report/miri-report.tex +++ /dev/null @@ -1,663 +0,0 @@ -% vim: tw=100 - -\documentclass[twocolumn]{article} -\usepackage{blindtext} -\usepackage[hypcap]{caption} -\usepackage{fontspec} -\usepackage[colorlinks, urlcolor={blue!80!black}]{hyperref} -\usepackage[outputdir=out]{minted} -\usepackage{relsize} -\usepackage{xcolor} - -\setmonofont{Source Code Pro}[ - BoldFont={* Medium}, - BoldItalicFont={* Medium Italic}, - Scale=MatchLowercase, -] - -\newcommand{\rust}[1]{\mintinline{rust}{#1}} - -\begin{document} - -\title{Miri: \\ \smaller{An interpreter for Rust's mid-level intermediate representation}} -\author{Scott Olson\footnote{\href{mailto:scott@solson.me}{scott@solson.me}} \\ - \smaller{Supervised by Christopher Dutchyn}} -\date{April 12th, 2016} -\maketitle - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Abstract} - -The increasing need for safe low-level code in contexts like operating systems and browsers is -driving the development of Rust\footnote{\url{https://www.rust-lang.org}}, a programming language -promising high performance without the risk of memory unsafety. To make programming more convenient, -it's often desirable to be able to generate code or perform some computation at compile-time. The -former is mostly covered by Rust's existing macro feature or build-time code generation, but the -latter is currently restricted to a limited form of constant evaluation capable of little beyond -simple math. - -The architecture of the compiler at the time the existing constant evaluator was built limited its -potential for future extension. However, a new intermediate representation was recently -added\footnote{\href{https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md}{Rust RFC \#1211: Mid-level IR (MIR)}} -to the Rust compiler between the abstract syntax tree and the back-end LLVM IR, called mid-level -intermediate representation, or MIR for short. This report will demonstrate that writing an -interpreter for MIR is a surprisingly effective approach for supporting a large proportion of Rust's -features in compile-time execution. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Background} - -The Rust compiler generates an instance of \rust{Mir} for each function [\autoref{fig:mir}]. Each -\rust{Mir} structure represents a control-flow graph for a given function, and contains a list of -``basic blocks'' which in turn contain a list of statements followed by a single terminator. Each -statement is of the form \rust{lvalue = rvalue}. An \rust{Lvalue} is used for referencing variables -and calculating addresses such as when dereferencing pointers, accessing fields, or indexing arrays. -An \rust{Rvalue} represents the core set of operations possible in MIR, including reading a value -from an lvalue, performing math operations, creating new pointers, structures, and arrays, and so -on. Finally, a terminator decides where control will flow next, optionally based on the value of a -boolean or integer. - -\begin{figure}[ht] - \begin{minted}[autogobble]{rust} - struct Mir { - basic_blocks: Vec, - // ... - } - - struct BasicBlockData { - statements: Vec, - terminator: Terminator, - // ... - } - - struct Statement { - lvalue: Lvalue, - rvalue: Rvalue - } - - enum Terminator { - Goto { target: BasicBlock }, - If { - cond: Operand, - targets: [BasicBlock; 2] - }, - // ... - } - \end{minted} - \caption{MIR (simplified)} - \label{fig:mir} -\end{figure} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{First implementation} - -\subsection{Basic operation} - -To investigate the possibility of executing Rust at compile-time I wrote an interpreter for MIR -called Miri\footnote{\url{https://github.com/solson/miri}}. The structure of the interpreter closely -mirrors the structure of MIR itself. It starts executing a function by iterating the statement list -in the starting basic block, translating the lvalue into a pointer and using the rvalue to decide -what to write into that pointer. Evaluating the rvalue may involve reads (such as for the two sides -of a binary operation) or construction of new values. When the terminator is reached, it is used to -decide which basic block to jump to next. Finally, Miri repeats this entire process, reading -statements from the new block. - -\subsection{Function calls} - -To handle function call terminators\footnote{Calls occur only as terminators, never as rvalues.}, -Miri is required to store some information in a virtual call stack so that it may pick up where it -left off when the callee returns. Each stack frame stores a reference to the \rust{Mir} for the -function being executed, its local variables, its return value location\footnote{Return value -pointers are passed in by callers.}, and the basic block where execution should resume. When Miri -encounters a \rust{Return} terminator in the MIR, it pops one frame off the stack and resumes the -previous function. Miri's execution ends when the function it was initially invoked with returns, -leaving the call stack empty. - -It should be noted that Miri does not itself recurse when a function is called; it merely pushes a -virtual stack frame and jumps to the top of the interpreter loop. Consequently, Miri can interpret -deeply recursive programs without overflowing its native call stack. This approach would allow Miri -to set a virtual stack depth limit and report an error when a program exceeds it. - -\subsection{Flaws} - -This version of Miri supported quite a bit of the Rust language, including booleans, integers, -if-conditions, while-loops, structures, enums, arrays, tuples, pointers, and function calls, -requiring approximately 400 lines of Rust code. However, it had a particularly naive value -representation with a number of downsides. It resembled the data layout of a dynamic language like -Ruby or Python, where every value has the same size\footnote{An \rust{enum} is a discriminated union -with a tag and space to fit the largest variant, regardless of which variant it contains.} in the -interpreter: - -\begin{minted}[autogobble]{rust} - enum Value { - Uninitialized, - Bool(bool), - Int(i64), - Pointer(Pointer), // index into stack - Aggregate { - variant: usize, - data: Pointer, - }, - } -\end{minted} - -This representation did not work well for aggregate types\footnote{That is, structures, enums, -arrays, tuples, and closures.} and required strange hacks to support them. Their contained values -were allocated elsewhere on the stack and pointed to by the aggregate value, which made it more -complicated to implement copying aggregate values from place to place. - -Moreover, while the aggregate issues could be worked around, this value representation made common -unsafe programming tricks (which make assumptions about the low-level value layout) fundamentally -impossible. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Current implementation} - -Roughly halfway through my time working on Miri, Eduard -Burtescu\footnote{\href{https://github.com/eddyb}{eddyb on GitHub}} from the Rust compiler -team\footnote{\url{https://www.rust-lang.org/team.html\#Compiler}} made a post on Rust's internal -forums about a ``Rust Abstract Machine'' -specification\footnote{\href{https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31}{Burtescu's -reply on ``MIR constant evaluation''}} which could be used to implement more powerful compile-time -function execution, similar to what is supported by C++14's \mintinline{cpp}{constexpr} feature. -After clarifying some of the details of the data layout with Burtescu via IRC, I started -implementing it in Miri. - -\subsection{Raw value representation} - -The main difference in the new value representation was to represent values by ``abstract -allocations'' containing arrays of raw bytes with different sizes depending on their types. This -mimics how Rust values are represented when compiled for physical machines. In addition to the raw -bytes, allocations carry information about pointers and undefined bytes. - -\begin{minted}[autogobble]{rust} - struct Memory { - map: HashMap, - next_id: AllocId, - } - - struct Allocation { - bytes: Vec, - relocations: BTreeMap, - undef_mask: UndefMask, - } -\end{minted} - -\subsubsection{Relocations} - -The abstract machine represents pointers through ``relocations'', which are analogous to relocations -in linkers\footnote{\href{https://en.wikipedia.org/wiki/Relocation_(computing)}{Relocation -(computing) - Wikipedia}}. Instead of storing a global memory address in the raw byte representation -like on a physical machine, we store an offset from the start of the target allocation and add an -entry to the relocation table which maps the index of the offset bytes to the target allocation. - -In \autoref{fig:reloc}, the relocation stored at offset 0 in \rust{y} points to offset 2 in \rust{x} -(the 2nd 16-bit integer). Thus, the relocation table for \rust{y} is \texttt{\{0 => -x\}}, meaning the next $N$ bytes after offset 0 denote an offset into allocation \rust{x} where $N$ -is the size of a pointer (4 in this example). The example shows this as a labelled line beneath the -offset bytes. - -In effect, the abstract machine represents pointers as \rust{(allocation_id, offset)} pairs. This -makes it easy to detect when pointer accesses go out of bounds. - -\begin{figure}[hb] - \begin{minted}[autogobble]{rust} - let x: [i16; 3] = [0xAABB, 0xCCDD, 0xEEFF]; - let y = &x[1]; - // x: BB AA DD CC FF EE (6 bytes) - // y: 02 00 00 00 (4 bytes) - // └───(x)───┘ - \end{minted} - \caption{Example relocation on 32-bit little-endian} - \label{fig:reloc} -\end{figure} - -\subsubsection{Undefined byte mask} - -The final piece of an abstract allocation is the undefined byte mask. Logically, we store a boolean -for the definedness of every byte in the allocation, but there are multiple ways to make the storage -more compact. I tried two implementations: one based on the endpoints of alternating ranges of -defined and undefined bytes and the other based on a bitmask. The former is more compact but I found -it surprisingly difficult to update cleanly. I currently use the much simpler bitmask system. - -See \autoref{fig:undef} for an example of an undefined byte in a value, represented by underscores. -Note that there is a value for the second byte in the byte array, but it doesn't matter what it is. -The bitmask would be $10_2$, i.e.\ \rust{[true, false]}. - -\begin{figure}[hb] - \begin{minted}[autogobble]{rust} - let x: [u8; 2] = unsafe { - [1, std::mem::uninitialized()] - }; - // x: 01 __ (2 bytes) - \end{minted} - \caption{Example undefined byte} - \label{fig:undef} -\end{figure} - -\subsection{Computing data layout} - -Currently, the Rust compiler's data layouts for types are hidden from Miri, so it does its own data -layout computation which will not always match what the compiler does, since Miri doesn't take -target type alignments into account. In the future, the Rust compiler may be modified so that Miri -can use the exact same data layout. - -Miri's data layout calculation is a relatively simple transformation from Rust types to a structure -with constant size values for primitives and sets of fields with offsets for aggregate types. These -layouts are cached for performance. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Deterministic execution} -\label{sec:deterministic} - -In order to be effective as a compile-time evaluator, Miri must have \emph{deterministic execution}, -as explained by Burtescu in the ``Rust Abstract Machine'' post. That is, given a function and -arguments to that function, Miri should always produce identical results. This is important for -coherence in the type checker when constant evaluations are involved in types, such as for sizes of -array types: - -\begin{minted}[autogobble,mathescape]{rust} - const fn get_size() -> usize { /* $\ldots$ */ } - let array: [i32; get_size()]; -\end{minted} - -Since Miri allows execution of unsafe code\footnote{In fact, the distinction between safe and unsafe -doesn't exist at the MIR level.}, it is specifically designed to remain safe while interpreting -potentially unsafe code. When Miri encounters an unrecoverable error, it reports it via the Rust -compiler's usual error reporting mechanism, pointing to the part of the original code where the -error occurred. Below is an example from Miri's -repository.\footnote{\href{https://github.com/solson/miri/blob/master/test/errors.rs}{miri/test/errors.rs}} - -\begin{minted}[autogobble]{rust} - let b = Box::new(42); - let p: *const i32 = &*b; - drop(b); - unsafe { *p } - // ~~ error: dangling pointer - // was dereferenced -\end{minted} -\label{dangling-pointer} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Language support} - -In its current state, Miri supports a large proportion of the Rust language, detailed below. The -major exception is a lack of support for FFI\footnote{Foreign Function Interface, e.g.\ calling -functions defined in Assembly, C, or C++.}, which eliminates possibilities like reading and writing -files, user input, graphics, and more. However, for compile-time evaluation in Rust, this limitation -is desired. - -\subsection{Primitives} - -Miri supports booleans, integers of various sizes and signed-ness (i.e.\ \rust{i8}, \rust{i16}, -\rust{i32}, \rust{i64}, \rust{isize}, \rust{u8}, \rust{u16}, \rust{u32}, \rust{u64}, \rust{usize}), -and unary and binary operations over these types. The \rust{isize} and \rust{usize} types will be -sized according to the target machine's pointer size just like in compiled Rust. The \rust{char} and -float types (\rust{f32}, \rust{f64}) are not supported yet, but there are no known barriers to doing -so. - -When examining a boolean in an \rust{if} condition, Miri will report an error if its byte -representation is not precisely 0 or 1, since having any other value for a boolean is undefined -behaviour in Rust. The \rust{char} type will have similar restrictions once it is implemented. - -\subsection{Pointers} - -Both references and raw pointers are supported, with essentially no difference between them in Miri. -It is also possible to do pointer comparisons and math. However, a few operations are considered -errors and a few require special support. - -Firstly, pointers into the same allocations may be compared for ordering, but pointers into -different allocations are considered unordered and Miri will complain if you attempt this. The -reasoning is that different allocations may have different orderings in the global address space at -runtime, making this non-deterministic. However, pointers into different allocations \emph{may} be -compared for direct equality (they are always unequal). - -Secondly, pointers represented using relocations may be compared against pointers casted from -integers (e.g.\ \rust{0 as *const i32}) for things like null pointer checks. To handle these cases, -Miri has a concept of ``integer pointers'' which are always unequal to abstract pointers. Integer -pointers can be compared and operated upon freely. However, note that it is impossible to go from an -integer pointer to an abstract pointer backed by a relocation. It is not valid to dereference an -integer pointer. - -\subsubsection{Slice pointers} - -Rust supports pointers to ``dynamically-sized types'' such as \rust{[T]} and \rust{str} which -represent arrays of indeterminate size. Pointers to such types contain an address \emph{and} the -length of the referenced array. Miri supports these fully. - -\subsubsection{Trait objects} - -Rust also supports pointers to ``trait objects'' which represent some type that implements a trait, -with the specific type unknown at compile-time. These are implemented using virtual dispatch with a -vtable, similar to virtual methods in C++. Miri does not currently support these at all. - -\subsection{Aggregates} - -Aggregates include types declared with \rust{struct} or \rust{enum} as well as tuples, arrays, and -closures. Miri supports all common usage of all of these types. The main missing piece is to handle -\texttt{\#[repr(..)]} annotations which adjust the layout of a \rust{struct} or \rust{enum}. - -\subsection{Lvalue projections} - -This category includes field accesses, dereferencing, accessing data in an \rust{enum} variant, and -indexing arrays. Miri supports all of these, including nested projections such as -\rust{*foo.bar[2]}. - -\subsection{Control flow} - -All of Rust's standard control flow features, including \rust{loop}, \rust{while}, \rust{for}, -\rust{if}, \rust{if let}, \rust{while let}, \rust{match}, \rust{break}, \rust{continue}, and -\rust{return} are supported. In fact, supporting these was quite easy since the Rust compiler -reduces them all down to a small set of control-flow graph primitives in MIR. - -\subsection{Function calls} - -As previously described, Miri supports arbitrary function calls without growing the native stack -(only its virtual call stack). It is somewhat limited by the fact that cross-crate\footnote{A crate -is a single Rust library (or executable).} calls only work for functions whose MIR is stored in -crate metadata. This is currently true for \rust{const}, generic, and inline functions. -A branch of the compiler could be made that stores MIR for all functions. This would be a non-issue -for a compile-time evaluator based on Miri, since it would only call \rust{const fn}s. - -\subsubsection{Method calls} - -Miri supports trait method calls, including invoking all the compiler-internal lookup needed to find -the correct implementation of the method. - -\subsubsection{Closures} - -Calls to closures are also supported with the exception of one edge case\footnote{Calling a closure -that takes a reference to its captures via a closure interface that passes the captures by value is -not yet supported.}. The value part of a closure that holds the captured variables is handled as an -aggregate and the function call part is mostly the same as a trait method call, but with the added -complication that closures use a separate calling convention within the compiler. - -\subsubsection{Function pointers} - -Function pointers are not currently supported by Miri, but there is a relatively simple way they -could be encoded using a relocation with a special reserved allocation identifier. The offset of the -relocation would determine which function it points to in a special array of functions in the -interpreter. - -\subsubsection{Intrinsics} - -To support unsafe code, and in particular to support Rust's standard library, it became clear that -Miri would have to support calls to compiler -intrinsics\footnote{\url{https://doc.rust-lang.org/stable/std/intrinsics/index.html}}. Intrinsics -are function calls which cause the Rust compiler to produce special-purpose code instead of a -regular function call. Miri simply recognizes intrinsic calls by their unique -ABI\footnote{Application Binary Interface, which defines calling conventions. Includes ``C'', -``Rust'', and ``rust-intrinsic''.} and name and runs special-purpose code to handle them. - -An example of an important intrinsic is \rust{size_of} which will cause Miri to write the size of -the type in question to the return value location. The Rust standard library uses intrinsics heavily -to implement various data structures, so this was a major step toward supporting them. Intrinsics -have been implemented on a case-by-case basis as tests which required them were written, and not all -intrinsics are supported yet. - -\subsubsection{Generic function calls} - -Miri needs special support for generic function calls since Rust is a \emph{monomorphizing} -compiler, meaning it generates a special version of each function for each distinct set of type -parameters it gets called with. Since functions in MIR are still polymorphic, Miri has to do the -same thing and substitute function type parameters into all types it encounters to get fully -concrete, monomorphized types. For example, in\ldots - -\begin{minted}[autogobble]{rust} - fn some(t: T) -> Option { Some(t) } -\end{minted} - -\ldots{}Miri needs to know the size of \rust{T} to copy the right amount of bytes from the argument -to the return value. If we call \rust{some(10i32)} Miri will execute \rust{some} knowing that -\rust{T = i32} and generate a representation for \rust{Option}. - -Miri currently does this monomorphization lazily on-demand unlike the Rust back-end which does it -all ahead of time. - -\subsection{Heap allocations} - -The next piece of the puzzle for supporting interesting programs (and the standard library) was heap -allocations. There are two main interfaces for heap allocation in Rust: the built-in \rust{Box} -rvalue in MIR and a set of C ABI foreign functions including \rust{__rust_allocate}, -\rust{__rust_reallocate}, and \rust{__rust_deallocate}. These correspond approximately to -\mintinline{c}{malloc}, \mintinline{c}{realloc}, and \mintinline{c}{free} in C. - -The \rust{Box} rvalue allocates enough space for a single value of a given type. This was easy to -support in Miri. It simply creates a new abstract allocation in the same manner as for -stack-allocated values, since there's no major difference between them in Miri. - -The allocator functions, which are used to implement things like Rust's standard \rust{Vec} type, -were a bit trickier. Rust declares them as \rust{extern "C" fn} so that different allocator -libraries can be linked in at the user's option. Since Miri doesn't actually support FFI and wants -full control of allocations for safety, it ``cheats'' and recognizes these allocator functions in -essentially the same way it recognizes compiler intrinsics. Then, a call to \rust{__rust_allocate} -simply creates another abstract allocation with the requested size and \rust{__rust_reallocate} -grows one. - -In the future, Miri should also track which allocations came from \rust{__rust_allocate} so it can -reject reallocate or deallocate calls on stack allocations. - -\subsection{Destructors} - -When a value which ``owns'' some resource (like a heap allocation or file handle) goes out of scope, -Rust inserts \emph{drop glue} that calls the user-defined destructor for the type if it has one, and -then drops all of the subfields. Destructors for types like \rust{Box} and \rust{Vec} -deallocate heap memory. - -Miri doesn't yet support calling user-defined destructors, but it has most of the machinery in place -to do so already. There \emph{is} support for dropping \rust{Box} types, including deallocating -their associated allocations. This is enough to properly execute the dangling pointer example in -\autoref{sec:deterministic}. - -\subsection{Constants} - -Only basic integer, boolean, string, and byte-string literals are currently supported. Evaluating -more complicated constant expressions in their current form would be a somewhat pointless exercise -for Miri. Instead, we should lower constant expressions to MIR so Miri can run them directly, which -is precisely what would need be done to use Miri as the compiler's constant evaluator. - -\subsection{Static variables} - -Miri doesn't currently support statics, but they would need support similar to constants. Also note -that while it would be invalid to write to static (i.e.\ global) variables in Miri executions, it -would probably be fine to allow reads. - -\subsection{Standard library} - -Throughout the implementation of the above features, I often followed this process: - -\begin{enumerate} - \item Try using a feature from the standard library. - \item See where Miri runs into stuff it can't handle. - \item Fix the problem. - \item Go to 1. -\end{enumerate} - -At present, Miri supports a number of major non-trivial features from the standard library along -with tons of minor features. Smart pointer types such as \rust{Box}, \rust{Rc}\footnote{Reference -counted shared pointer} and \rust{Arc}\footnote{Atomically reference-counted thread-safe shared -pointer} all seem to work. I've also tested using the shared smart pointer types with \rust{Cell} -and \rust{RefCell}\footnote{\href{https://doc.rust-lang.org/stable/std/cell/index.html}{Rust -documentation for cell types}} for internal mutability, and that works as well, although -\rust{RefCell} can't ever be borrowed twice until I implement destructor calls, since a destructor -is what releases the borrow. - -But the standard library collection I spent the most time on was \rust{Vec}, the standard -dynamically-growable array type, similar to C++'s \texttt{std::vector} or Java's -\texttt{java.util.ArrayList}. In Rust, \rust{Vec} is an extremely pervasive collection, so -supporting it is a big win for supporting a larger swath of Rust programs in Miri. - -See \autoref{fig:vec} for an example (working in Miri today) of initializing a \rust{Vec} with a -small amount of space on the heap and then pushing enough elements to force it to reallocate its -data array. This involves cross-crate generic function calls, unsafe code using raw pointers, heap -allocation, handling of uninitialized memory, compiler intrinsics, and more. - -\begin{figure}[t] - \begin{minted}[autogobble]{rust} - struct Vec { - data: *mut T, // 4 byte pointer - capacity: usize, // 4 byte integer - length: usize, // 4 byte integer - } - - let mut v: Vec = - Vec::with_capacity(2); - // v: 00 00 00 00 02 00 00 00 00 00 00 00 - // └─(data)──┘ - // data: __ __ - - v.push(1); - // v: 00 00 00 00 02 00 00 00 01 00 00 00 - // └─(data)──┘ - // data: 01 __ - - v.push(2); - // v: 00 00 00 00 02 00 00 00 02 00 00 00 - // └─(data)──┘ - // data: 01 02 - - v.push(3); - // v: 00 00 00 00 04 00 00 00 03 00 00 00 - // └─(data)──┘ - // data: 01 02 03 __ - \end{minted} - \caption{\rust{Vec} example on 32-bit little-endian} - \label{fig:vec} -\end{figure} - -Miri supports unsafe operations on \rust{Vec} like \rust{v.set_len(10)} or -\rust{v.get_unchecked(2)}, provided that such calls do no invoke undefined behaviour. If a call -\emph{does} invoke undefined behaviour, Miri will abort with an appropriate error message (see -\autoref{fig:vec-error}). - -\begin{figure}[t] - \begin{minted}[autogobble]{rust} - fn out_of_bounds() -> u8 { - let v = vec![1, 2]; - let p = unsafe { v.get_unchecked(5) }; - *p + 10 - // ~~ error: pointer offset outside - // bounds of allocation - } - - fn undefined_bytes() -> u8 { - let v = Vec::::with_capacity(10); - let p = unsafe { v.get_unchecked(5) }; - *p + 10 - // ~~~~~~~ error: attempted to read - // undefined bytes - } - \end{minted} - \caption{\rust{Vec} examples with undefined behaviour} - \label{fig:vec-error} -\end{figure} - -\newpage - -Here is one final code sample Miri can execute that demonstrates many features at once, including -vectors, heap allocation, iterators, closures, raw pointers, and math: - -\begin{minted}[autogobble]{rust} - let x: u8 = vec![1, 2, 3, 4] - .into_iter() - .map(|x| x * x) - .fold(0, |x, y| x + y); - // x: 1e (that is, the hex value - // 0x1e = 30 = 1 + 4 + 9 + 16) -\end{minted} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Future directions} - -\subsection{Finishing the implementation} - -There are a number of pressing items on my to-do list for Miri, including: - -\begin{itemize} - \item A much more comprehensive and automated test suite. - \item User-defined destructor calls. - \item Non-trivial casts between primitive types like integers and pointers. - \item Handling statics and global memory. - \item Reporting errors for all undefined behaviour.\footnote{\href{https://doc.rust-lang.org/reference.html\#behavior-considered-undefined}{The Rust reference on what is considered undefined behaviour}} - \item Function pointers. - \item Accounting for target machine primitive type alignment and endianness. - \item Optimizations (undefined byte masks, tail-calls). - \item Benchmarking Miri vs. unoptimized Rust. - \item Various \texttt{TODO}s and \texttt{FIXME}s left in the code. - \item Integrating into the compiler proper. -\end{itemize} - -\subsection{Future projects} - -Other possible Miri-related projects include: - -\begin{itemize} - \item A read-eval-print-loop (REPL) for Rust, which may be easier to implement on top of Miri than - the usual LLVM back-end. - \item A graphical or text-mode debugger that steps through MIR execution one statement at a time, - for figuring out why some compile-time execution is raising an error or simply learning how Rust - works at a low level. - \item A less restricted version of Miri that is able to run foreign functions from C/C++ and - generally has full access to the operating system. Such an interpreter could be used to more - quickly prototype changes to the Rust language that would otherwise require changes to the LLVM - back-end. - \item Unit-testing the compiler by comparing the results of Miri's execution against the results - of LLVM-compiled machine code's execution. This would help to guarantee that compile-time - execution works the same as runtime execution. - \item Some kind of Miri-based symbolic evaluator that examines multiple possible code paths at - once to determine if undefined behaviour could be observed on any of them. -\end{itemize} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Final thoughts} - -Writing an interpreter which models values of varying sizes, stack and heap allocation, unsafe -memory operations, and more requires some unconventional techniques compared to conventional -interpreters targeting dynamically-typed languages. However, aside from the somewhat complicated -abstract memory model, making Miri work was primarily a software engineering problem, and not a -particularly tricky one. This is a testament to MIR's suitability as an intermediate representation -for Rust---removing enough unnecessary abstraction to keep it simple. For example, Miri doesn't even -need to know that there are different kinds of loops, or how to match patterns in a \rust{match} -expression. - -Another advantage to targeting MIR is that any new features at the syntax-level or type-level -generally require little to no change in Miri. For example, when the new ``question mark'' syntax -for error handling\footnote{ - \href{https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md} - {Question mark syntax RFC}} -was added to rustc, Miri required no change to support it. -When specialization\footnote{ - \href{https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md} - {Specialization RFC}} -was added, Miri supported it with just minor changes to trait method lookup. - -Of course, Miri also has limitations. The inability to execute FFI and inline assembly reduces the -amount of Rust programs Miri could ever execute. The good news is that in the constant evaluator, -FFI can be stubbed out in cases where it makes sense, like I did with \rust{__rust_allocate}. For a -version of Miri not intended for constant evaluation, it may be possible to use libffi to call C -functions from the interpreter. - -In conclusion, Miri is a surprisingly effective project, and a lot of fun to implement. Due to MIR's -tendency to collapse multiple source-level features into one, I often ended up supporting features I -hadn't explicitly intended to. I am excited to work with the compiler team going forward to try to -make Miri useful for constant evaluation in Rust. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\section{Thanks} - -A big thanks goes to Eduard Burtescu for writing the abstract machine specification and answering my -incessant questions on IRC, to Niko Matsakis for coming up with the idea for Miri and supporting my -desire to work with the Rust compiler, and to my research supervisor Christopher Dutchyn. Thanks -also to everyone else on the compiler team and on Mozilla IRC who helped me figure stuff out. -Finally, thanks to Daniel Keep and everyone else who helped fix my numerous writing mistakes. - -\end{document} diff --git a/xargo/Cargo.lock b/xargo/Cargo.lock deleted file mode 100644 index 031ad9a879549..0000000000000 --- a/xargo/Cargo.lock +++ /dev/null @@ -1,4 +0,0 @@ -[root] -name = "miri-xargo" -version = "0.0.0" - diff --git a/xargo/Cargo.toml b/xargo/Cargo.toml deleted file mode 100644 index 9129c105b112b..0000000000000 --- a/xargo/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "miri-xargo" -description = "A dummy project for building libstd with xargo." -version = "0.0.0" - -[dependencies] diff --git a/xargo/Xargo.toml b/xargo/Xargo.toml deleted file mode 100644 index 32f45c4a9816f..0000000000000 --- a/xargo/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[dependencies] -std = {features = ["panic_unwind", "jemalloc"]} diff --git a/xargo/build.sh b/xargo/build.sh deleted file mode 100755 index e744abaadfdfe..0000000000000 --- a/xargo/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd "$(readlink -e "$(dirname "$0")")" -RUSTFLAGS='-Zalways-encode-mir' xargo build diff --git a/xargo/src/lib.rs b/xargo/src/lib.rs deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 029802a1618decfe54574ee5e7407aed03efcac0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 19 Jul 2017 19:57:52 +0200 Subject: [PATCH 1096/1096] Build miri along with the compiler --- Cargo.toml | 31 --- src/Cargo.lock | 17 ++ src/librustc_mir/Cargo.toml | 2 + src/librustc_mir/lib.rs | 5 + src/librustc_mir/miri/LICENSE-APACHE | 201 ++++++++++++++++++ src/librustc_mir/miri/LICENSE-MIT | 25 +++ src/librustc_mir/miri/README.md | 88 +------- src/librustc_mir/miri/cast.rs | 8 +- src/librustc_mir/miri/const_eval.rs | 11 +- src/librustc_mir/miri/error.rs | 6 +- src/librustc_mir/miri/eval_context.rs | 23 +- src/librustc_mir/miri/lvalue.rs | 8 +- src/librustc_mir/miri/memory.rs | 6 +- src/librustc_mir/miri/{lib.rs => mod.rs} | 30 +-- src/librustc_mir/miri/operator.rs | 16 +- src/librustc_mir/miri/step.rs | 9 +- src/librustc_mir/miri/terminator/drop.rs | 9 +- src/librustc_mir/miri/terminator/intrinsic.rs | 12 +- src/librustc_mir/miri/terminator/mod.rs | 24 ++- src/librustc_mir/miri/traits.rs | 14 +- src/librustc_mir/miri/value.rs | 4 +- 21 files changed, 339 insertions(+), 210 deletions(-) delete mode 100644 Cargo.toml create mode 100644 src/librustc_mir/miri/LICENSE-APACHE create mode 100644 src/librustc_mir/miri/LICENSE-MIT rename src/librustc_mir/miri/{lib.rs => mod.rs} (51%) diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 90e078266d195..0000000000000 --- a/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -authors = ["Scott Olson "] -description = "An experimental interpreter for Rust MIR." -license = "MIT/Apache-2.0" -name = "miri" -repository = "https://github.com/solson/miri" -version = "0.1.0" - -[[bin]] -doc = false -name = "miri" -test = false - -[[bin]] -doc = false -name = "cargo-miri" -test = false - -[lib] -test = false - -[dependencies] -#byteorder = "0.4.2" -byteorder = { git = "https://github.com/BurntSushi/byteorder", features = ["i128"]} -env_logger = "0.3.3" -log = "0.3.6" -log_settings = "0.1.1" -cargo_metadata = "0.2" - -[dev-dependencies] -compiletest_rs = "0.2.6" diff --git a/src/Cargo.lock b/src/Cargo.lock index 3c8d1164863de..f2c7fae182d5d 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -164,6 +164,11 @@ dependencies = [ "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "byteorder" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cargo" version = "0.21.0" @@ -686,6 +691,14 @@ name = "log" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log_settings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lzma-sys" version = "0.1.7" @@ -1349,8 +1362,10 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_bitflags 0.0.0", "rustc_const_eval 0.0.0", @@ -2060,6 +2075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32" +"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum cargo 0.21.0 (git+https://github.com/rust-lang/cargo)" = "" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771" @@ -2099,6 +2115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" "checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" +"checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum lzma-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "013fa6506eb7d26040c46dab9ecb7ccb4e2896b5bf24a9d65932501ea9f67af8" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum mdbook 0.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "22911d86cde6f80fa9f0fb2a68bbbde85d97af4fe0ce267141c83a4187d28700" diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 6e42e02d5109b..32ec2669044a8 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -18,3 +18,5 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_bitflags = { path = "../librustc_bitflags" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +byteorder = { version = "1.1", features = ["i128"]} +log_settings = "0.1.1" diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index bb1767773327c..ef8ef8757ba83 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -30,6 +30,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![cfg_attr(stage0, feature(associated_consts))] #[macro_use] extern crate log; +extern crate log_settings; extern crate graphviz as dot; #[macro_use] extern crate rustc; @@ -44,6 +45,9 @@ extern crate rustc_const_math; extern crate rustc_const_eval; extern crate core; // for NonZero +extern crate byteorder; + + pub mod diagnostics; mod build; @@ -52,6 +56,7 @@ mod hair; mod shim; pub mod transform; pub mod util; +pub mod miri; use rustc::ty::maps::Providers; diff --git a/src/librustc_mir/miri/LICENSE-APACHE b/src/librustc_mir/miri/LICENSE-APACHE new file mode 100644 index 0000000000000..a32595fa70bc1 --- /dev/null +++ b/src/librustc_mir/miri/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2016 The Miri Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/librustc_mir/miri/LICENSE-MIT b/src/librustc_mir/miri/LICENSE-MIT new file mode 100644 index 0000000000000..1f9d89a5862b5 --- /dev/null +++ b/src/librustc_mir/miri/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Miri Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/librustc_mir/miri/README.md b/src/librustc_mir/miri/README.md index 8edaba77fb3fa..3423f1d9d810d 100644 --- a/src/librustc_mir/miri/README.md +++ b/src/librustc_mir/miri/README.md @@ -1,88 +1,13 @@ -# Miri [[slides](https://solson.me/miri-slides.pdf)] [[report](https://solson.me/miri-report.pdf)] [![Build Status](https://travis-ci.org/solson/miri.svg?branch=master)](https://travis-ci.org/solson/miri) - - -An experimental interpreter for [Rust][rust]'s [mid-level intermediate -representation][mir] (MIR). This project began as part of my work for the -undergraduate research course at the [University of Saskatchewan][usask]. - -## Installing Rust - -I recommend that you install [rustup][rustup] and then use it to install the -current Rust nightly version: - -```sh -rustup update nightly -``` - -You should also make `nightly` the default version for your Miri directory by -running the following command while you're in it. If you don't do this, you can -run the later `cargo` commands by using `cargo +nightly` instead. - -```sh -rustup override add nightly -``` - -## Building Miri - -```sh -cargo build -``` - -If Miri fails to build, it's likely because a change in the latest nightly -compiler broke it. You could try an older nightly with `rustup update -nightly-` where `` is a few days or weeks ago, e.g. `2016-05-20` for -May 20th. Otherwise, you could notify me in an issue or on IRC. Or, if you know -how to fix it, you could send a PR. :smile: - -## Running tests - -```sh -cargo run --bin miri tests/run-pass/vecs.rs # Or whatever test you like. -``` +An interpreter for [Rust][rust]'s [mid-level intermediate +representation][mir] (MIR). ## Debugging -You can get detailed, statement-by-statement traces by setting the `MIRI_LOG` -environment variable to `trace`. These traces are indented based on call stack +You can get detailed, statement-by-statement traces by setting the `RUST_LOG` +environment variable to `rustc_mir::miri=trace`. These traces are indented based on call stack depth. You can get a much less verbose set of information with other logging levels such as `warn`. -## Running miri on your own project('s test suite) - -Install miri as a cargo subcommand with `cargo install --debug`. -Then, inside your own project, use `cargo +nightly miri` to run your project, if it is -a bin project, or run `cargo +nightly miri test` to run all tests in your project -through miri. - -## Running miri with full libstd - -Per default libstd does not contain the MIR of non-polymorphic functions. When -miri hits a call to such a function, execution terminates. To fix this, it is -possible to compile libstd with full MIR: - -```sh -rustup component add rust-src -cargo install xargo -cd xargo/ -RUSTFLAGS='-Zalways-encode-mir' xargo build -``` - -Now you can run miri against the libstd compiled by xargo: - -```sh -MIRI_SYSROOT=~/.xargo/HOST cargo run --bin miri tests/run-pass-fullmir/vecs.rs -``` - -Notice that you will have to re-run the last step of the preparations above when -your toolchain changes (e.g., when you update the nightly). - -## Contributing and getting help - -Check out the issues on this GitHub repository for some ideas. There's lots that -needs to be done that I haven't documented in the issues yet, however. For more -ideas or help with running or hacking on Miri, you can contact me (`scott`) on -Mozilla IRC in any of the Rust IRC channels (`#rust`, `#rust-offtopic`, etc). - ## License Licensed under either of @@ -96,8 +21,3 @@ Licensed under either of Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions. - -[rust]: https://www.rust-lang.org/ -[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md -[usask]: https://www.usask.ca/ -[rustup]: https://www.rustup.rs diff --git a/src/librustc_mir/miri/cast.rs b/src/librustc_mir/miri/cast.rs index cb0b112170987..173bdee1ecc01 100644 --- a/src/librustc_mir/miri/cast.rs +++ b/src/librustc_mir/miri/cast.rs @@ -1,9 +1,9 @@ use rustc::ty::{self, Ty}; use syntax::ast::{FloatTy, IntTy, UintTy}; -use error::{EvalResult, EvalError}; -use eval_context::EvalContext; -use value::PrimVal; +use super::error::{EvalResult, EvalError}; +use super::eval_context::EvalContext; +use super::value::PrimVal; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval( @@ -14,7 +14,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, PrimVal> { let kind = self.ty_to_primval_kind(src_ty)?; - use value::PrimValKind::*; + use super::value::PrimValKind::*; match kind { F32 => self.cast_float(val.to_f32()? as f64, dest_ty), F64 => self.cast_float(val.to_f64()?, dest_ty), diff --git a/src/librustc_mir/miri/const_eval.rs b/src/librustc_mir/miri/const_eval.rs index b538d2d835786..05afacc567a1e 100644 --- a/src/librustc_mir/miri/const_eval.rs +++ b/src/librustc_mir/miri/const_eval.rs @@ -2,17 +2,18 @@ use rustc::traits::Reveal; use rustc::ty::{self, TyCtxt, Ty, Instance}; use syntax::ast::Mutability; -use error::{EvalError, EvalResult}; -use lvalue::{Global, GlobalId, Lvalue}; -use value::PrimVal; +use super::error::{EvalError, EvalResult}; +use super::lvalue::{Global, GlobalId, Lvalue}; +use super::value::PrimVal; use rustc_const_math::ConstInt; -use eval_context::{EvalContext, StackPopCleanup}; +use super::eval_context::{EvalContext, StackPopCleanup}; +use super::ResourceLimits; pub fn eval_body_as_primval<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, ) -> EvalResult<'tcx, (PrimVal, Ty<'tcx>)> { - let limits = ::ResourceLimits::default(); + let limits = ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, limits); let cid = GlobalId { instance, promoted: None }; if ecx.tcx.has_attr(instance.def_id(), "linkage") { diff --git a/src/librustc_mir/miri/error.rs b/src/librustc_mir/miri/error.rs index 403ca9539e5b0..de1779c69c2be 100644 --- a/src/librustc_mir/miri/error.rs +++ b/src/librustc_mir/miri/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt; use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; -use memory::{MemoryPointer, Kind}; +use super::memory::{MemoryPointer, Kind}; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -77,7 +77,7 @@ pub type EvalResult<'tcx, T = ()> = Result>; impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { - use EvalError::*; + use self::EvalError::*; match *self { FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", @@ -189,7 +189,7 @@ impl<'tcx> Error for EvalError<'tcx> { impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use EvalError::*; + use self::EvalError::*; match *self { PointerOutOfBounds { ptr, access, allocation_size } => { write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", diff --git a/src/librustc_mir/miri/eval_context.rs b/src/librustc_mir/miri/eval_context.rs index 2f28063ff86dd..d0772dbfa7996 100644 --- a/src/librustc_mir/miri/eval_context.rs +++ b/src/librustc_mir/miri/eval_context.rs @@ -15,11 +15,12 @@ use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast::{self, Mutability}; use syntax::abi::Abi; -use error::{EvalError, EvalResult}; -use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; -use memory::{Memory, MemoryPointer, TlsKey, HasMemory}; -use operator; -use value::{PrimVal, PrimValKind, Value, Pointer}; +use super::error::{EvalError, EvalResult}; +use super::lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; +use super::memory::{Memory, MemoryPointer, TlsKey, HasMemory}; +use super::memory::Kind as MemoryKind; +use super::operator; +use super::value::{PrimVal, PrimValKind, Value, Pointer}; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -153,7 +154,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, MemoryPointer> { let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type"); let align = self.type_align_with_substs(ty, substs)?; - self.memory.allocate(size, align, ::memory::Kind::Stack) + self.memory.allocate(size, align, MemoryKind::Stack) } pub fn memory(&self) -> &Memory<'a, 'tcx> { @@ -417,8 +418,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // for a constant like `const FOO: &i32 = &1;` the local containing // the `1` is referred to by the global. We transitively marked everything // the global refers to as static itself, so we don't free it here - ::memory::Kind::Static => {} - ::memory::Kind::Stack => self.memory.deallocate(ptr, None, ::memory::Kind::Stack)?, + MemoryKind::Static => {} + MemoryKind::Stack => self.memory.deallocate(ptr, None, MemoryKind::Stack)?, other => bug!("local contained non-stack memory: {:?}", other), } }; @@ -700,7 +701,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(align.into()), dest_ty)?; } else { let align = self.type_align(ty)?; - let ptr = self.memory.allocate(size, align, ::memory::Kind::Rust)?; + let ptr = self.memory.allocate(size, align, MemoryKind::Rust)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } } @@ -1689,7 +1690,7 @@ pub fn eval_main<'a, 'tcx: 'a>( } // Return value - let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi(), ::memory::Kind::Stack)?; + let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi(), MemoryKind::Stack)?; cleanup_ptr = Some(ret_ptr); // Push our stack frame @@ -1731,7 +1732,7 @@ pub fn eval_main<'a, 'tcx: 'a>( while ecx.step()? {} if let Some(cleanup_ptr) = cleanup_ptr { - ecx.memory.deallocate(cleanup_ptr, None, ::memory::Kind::Stack)?; + ecx.memory.deallocate(cleanup_ptr, None, MemoryKind::Stack)?; } return Ok(()); } diff --git a/src/librustc_mir/miri/lvalue.rs b/src/librustc_mir/miri/lvalue.rs index 9fc01eb0384f3..1435e75cde44f 100644 --- a/src/librustc_mir/miri/lvalue.rs +++ b/src/librustc_mir/miri/lvalue.rs @@ -4,10 +4,10 @@ use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; use syntax::ast::Mutability; -use error::{EvalError, EvalResult}; -use eval_context::EvalContext; -use memory::MemoryPointer; -use value::{PrimVal, Value, Pointer}; +use super::error::{EvalError, EvalResult}; +use super::eval_context::EvalContext; +use super::memory::MemoryPointer; +use super::value::{PrimVal, Value, Pointer}; #[derive(Copy, Clone, Debug)] pub enum Lvalue<'tcx> { diff --git a/src/librustc_mir/miri/memory.rs b/src/librustc_mir/miri/memory.rs index cf7f969be8ecd..fd07ade025e96 100644 --- a/src/librustc_mir/miri/memory.rs +++ b/src/librustc_mir/miri/memory.rs @@ -6,9 +6,9 @@ use rustc::ty; use rustc::ty::layout::{self, TargetDataLayout}; use syntax::ast::Mutability; -use error::{EvalError, EvalResult}; -use value::{PrimVal, self, Pointer}; -use eval_context::EvalContext; +use super::error::{EvalError, EvalResult}; +use super::value::{PrimVal, self, Pointer}; +use super::eval_context::EvalContext; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers diff --git a/src/librustc_mir/miri/lib.rs b/src/librustc_mir/miri/mod.rs similarity index 51% rename from src/librustc_mir/miri/lib.rs rename to src/librustc_mir/miri/mod.rs index 424affa92a8c0..37d87675d1776 100644 --- a/src/librustc_mir/miri/lib.rs +++ b/src/librustc_mir/miri/mod.rs @@ -1,21 +1,3 @@ -#![feature( - i128_type, - rustc_private, -)] - -// From rustc. -#[macro_use] -extern crate log; -extern crate log_settings; -#[macro_use] -extern crate rustc; -extern crate rustc_const_math; -extern crate rustc_data_structures; -extern crate syntax; - -// From crates.io. -extern crate byteorder; - mod cast; mod const_eval; mod error; @@ -28,12 +10,12 @@ mod terminator; mod traits; mod value; -pub use error::{ +pub use self::error::{ EvalError, EvalResult, }; -pub use eval_context::{ +pub use self::eval_context::{ EvalContext, Frame, ResourceLimits, @@ -41,24 +23,24 @@ pub use eval_context::{ eval_main, }; -pub use lvalue::{ +pub use self::lvalue::{ Lvalue, LvalueExtra, }; -pub use memory::{ +pub use self::memory::{ AllocId, Memory, MemoryPointer, }; -pub use value::{ +pub use self::value::{ PrimVal, PrimValKind, Value, Pointer, }; -pub use const_eval::{ +pub use self::const_eval::{ eval_body_as_integer, }; diff --git a/src/librustc_mir/miri/operator.rs b/src/librustc_mir/miri/operator.rs index 47bd66641b1e8..73eae71a37b5c 100644 --- a/src/librustc_mir/miri/operator.rs +++ b/src/librustc_mir/miri/operator.rs @@ -1,11 +1,11 @@ use rustc::mir; use rustc::ty::{self, Ty}; -use error::{EvalError, EvalResult}; -use eval_context::EvalContext; -use memory::MemoryPointer; -use lvalue::Lvalue; -use value::{ +use super::error::{EvalError, EvalResult}; +use super::eval_context::EvalContext; +use super::memory::MemoryPointer; +use super::lvalue::Lvalue; +use super::value::{ PrimVal, PrimValKind, Value, @@ -72,7 +72,7 @@ macro_rules! int_arithmetic { ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ let l = $l; let r = $r; - use value::PrimValKind::*; + use super::value::PrimValKind::*; match $kind { I8 => overflow!($int_op, l as i8, r as i8), I16 => overflow!($int_op, l as i16, r as i16), @@ -142,7 +142,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right_ty: Ty<'tcx>, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use value::PrimValKind::*; + use super::value::PrimValKind::*; let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; @@ -344,7 +344,7 @@ pub fn unary_op<'tcx>( val_kind: PrimValKind, ) -> EvalResult<'tcx, PrimVal> { use rustc::mir::UnOp::*; - use value::PrimValKind::*; + use super::value::PrimValKind::*; let bytes = val.to_bytes()?; diff --git a/src/librustc_mir/miri/step.rs b/src/librustc_mir/miri/step.rs index d854218d7a90d..4f725ff6a62fe 100644 --- a/src/librustc_mir/miri/step.rs +++ b/src/librustc_mir/miri/step.rs @@ -10,10 +10,11 @@ use rustc::traits::Reveal; use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; -use error::{EvalResult, EvalError}; -use eval_context::{EvalContext, StackPopCleanup}; -use lvalue::{Global, GlobalId, Lvalue}; -use value::{Value, PrimVal}; +use super::error::{EvalResult, EvalError}; +use super::eval_context::{EvalContext, StackPopCleanup}; +use super::lvalue::{Global, GlobalId, Lvalue}; +use super::value::{Value, PrimVal}; + use syntax::codemap::Span; use syntax::ast::Mutability; diff --git a/src/librustc_mir/miri/terminator/drop.rs b/src/librustc_mir/miri/terminator/drop.rs index c166980a150d3..faf891c327c71 100644 --- a/src/librustc_mir/miri/terminator/drop.rs +++ b/src/librustc_mir/miri/terminator/drop.rs @@ -2,11 +2,10 @@ use rustc::mir; use rustc::ty::{self, Ty}; use syntax::codemap::Span; -use error::EvalResult; -use eval_context::{EvalContext, StackPopCleanup}; -use lvalue::{Lvalue, LvalueExtra}; -use value::PrimVal; -use value::Value; +use miri::error::EvalResult; +use miri::eval_context::{EvalContext, StackPopCleanup}; +use miri::lvalue::{Lvalue, LvalueExtra}; +use miri::value::{Value, PrimVal}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { diff --git a/src/librustc_mir/miri/terminator/intrinsic.rs b/src/librustc_mir/miri/terminator/intrinsic.rs index da45d7b410a60..bb04ba3fd63d0 100644 --- a/src/librustc_mir/miri/terminator/intrinsic.rs +++ b/src/librustc_mir/miri/terminator/intrinsic.rs @@ -4,11 +4,11 @@ use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use error::{EvalError, EvalResult}; -use eval_context::EvalContext; -use lvalue::{Lvalue, LvalueExtra}; -use value::{PrimVal, PrimValKind, Value, Pointer}; -use memory::HasMemory; +use miri::error::{EvalError, EvalResult}; +use miri::eval_context::EvalContext; +use miri::lvalue::{Lvalue, LvalueExtra}; +use miri::value::{PrimVal, PrimValKind, Value, Pointer}; +use miri::memory::HasMemory; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -581,7 +581,7 @@ fn numeric_intrinsic<'tcx>( ) -> EvalResult<'tcx, PrimVal> { macro_rules! integer_intrinsic { ($method:ident) => ({ - use value::PrimValKind::*; + use miri::value::PrimValKind::*; let result_bytes = match kind { I8 => (bytes as i8).$method() as u128, U8 => (bytes as u8).$method() as u128, diff --git a/src/librustc_mir/miri/terminator/mod.rs b/src/librustc_mir/miri/terminator/mod.rs index c4a8d2e73c291..9ff4a877259e9 100644 --- a/src/librustc_mir/miri/terminator/mod.rs +++ b/src/librustc_mir/miri/terminator/mod.rs @@ -6,12 +6,16 @@ use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; -use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; -use lvalue::Lvalue; -use memory::{MemoryPointer, TlsKey, Kind}; -use value::{PrimVal, Value}; +use super::error::{EvalError, EvalResult}; +use super::eval_context::{ + EvalContext, IntegerExt, StackPopCleanup, is_inhabited, resolve, + resolve_drop_in_place, apply_param_substs, +}; +use super::lvalue::Lvalue; +use super::memory::{MemoryPointer, TlsKey, Kind}; +use super::value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; +use super::const_eval::eval_body_as_primval; use std::mem; @@ -86,7 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } (instance, sig) }, - ty::TyFnDef(def_id, substs) => (::eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)), + ty::TyFnDef(def_id, substs) => (resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); @@ -104,9 +108,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let lval = self.eval_lvalue(location)?; let ty = self.lvalue_ty(location); self.goto_block(target); - let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); + let ty = apply_param_substs(self.tcx, self.substs(), &ty); - let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let instance = resolve_drop_in_place(self.tcx, ty); self.drop_lvalue(lval, instance, ty, terminator.source_info.span)?; } @@ -864,12 +868,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut result = None; for &(path, path_value) in paths { if let Ok(instance) = self.resolve_path(path) { - use lvalue::GlobalId; + use super::lvalue::GlobalId; let cid = GlobalId { instance, promoted: None }; // compute global if not cached let val = match self.globals.get(&cid).map(|glob| glob.value) { Some(value) => self.value_to_primval(value, usize)?.to_u64()?, - None => ::const_eval::eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, + None => eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, }; if val == name { result = Some(path_value); diff --git a/src/librustc_mir/miri/traits.rs b/src/librustc_mir/miri/traits.rs index 68d027bc63384..51937957edee2 100644 --- a/src/librustc_mir/miri/traits.rs +++ b/src/librustc_mir/miri/traits.rs @@ -1,8 +1,10 @@ use rustc::traits::{self, Reveal}; -use eval_context::EvalContext; -use memory::{MemoryPointer, Kind}; -use value::{Value, PrimVal}; +use super::eval_context::{ + EvalContext, resolve, resolve_drop_in_place, +}; +use super::memory::{MemoryPointer, Kind}; +use super::value::{Value, PrimVal}; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; @@ -10,7 +12,7 @@ use rustc::ty::{self, Ty}; use syntax::codemap::DUMMY_SP; use syntax::ast::{self, Mutability}; -use error::{EvalResult, EvalError}; +use super::error::{EvalResult, EvalError}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -53,7 +55,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref); let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size, Kind::UninitializedStatic)?; - let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let drop = resolve_drop_in_place(self.tcx, ty); let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; @@ -62,7 +64,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { - let instance = ::eval_context::resolve(self.tcx, def_id, substs); + let instance = resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; } diff --git a/src/librustc_mir/miri/value.rs b/src/librustc_mir/miri/value.rs index 606a3516485f4..70c5d3c5140b3 100644 --- a/src/librustc_mir/miri/value.rs +++ b/src/librustc_mir/miri/value.rs @@ -3,8 +3,8 @@ use rustc::ty::layout::TargetDataLayout; -use error::{EvalError, EvalResult}; -use memory::{Memory, MemoryPointer, HasMemory}; +use super::error::{EvalError, EvalResult}; +use super::memory::{Memory, MemoryPointer, HasMemory}; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { f32::from_bits(bytes as u32)

WQOwr zeybucG!3XCdRd1EJn0|b#2%%0pu#pI`20&SEYG-n2`JB(o%EJ?*x4Z;PhCr2iT*x$ zx4-sn!tk!z{!W4L1eJKc8d_d-mX6WZxG?n0C4H^0jNCZCN zYE?@tzEwPA&R?2#aTsZ+B9?FdO^4sM{fFFYYp>9-RUpMr4-6iNB2R;PBBuo*3ZgG2 zGhrN0%^A4K^#too4U<9{KSe^jN%PTjX_qJ&;f!kg8oNr@Nt>|T5S4ik6<;gXmb}tW zp?(>5;GY!SHv;u7+xuY~K_}*@P3*gA=>!--IZUe@%lYUA>EW#vu4MIXO&Ao@du2aL z5Uy>rcNMh*J1~#*5It{vO{a0Nr##7R9aFky$%q)~%V#=yqwJFxo{$`xavJsBLC2P` z#n51+fYAIQr% z3+l{p@FY8wl21JT8Al#>!>8Nlw zNKC$`qOy+;+$>O=IOs%@(vd($P57!xKjsSIM9`}s{pt{E;4x4y%tW(u&lRUSxuDoI zv)-jg3Dr6^(NszPXM6vWQss(v6gOQY*F~9s&D)-tic34NtrnWmY=H}W#IFZGCyEJ1 zDIfCdG_JCy-(#260ZS1dy5s_$*TRHofqSNTemfFbh+%#*32-?Rf21)#Lmig#K9*m9i(#UlPOV{meOsHNpsIO z;%L7s=IX_9@RDYFIM3u96-OBRB*}FA(FIbY|3vr$Zf(Xu4ZQnvzh)~u=-l7?W7^&F zh1pY@&-_ZrlO&KRDH>TEwu++V|NBAh8z)=BN3+mhj7s5OEZBnDZ&xiSGK$oeVj*^! z9eFz!69vG9uk&E?AX?LV-Ma-!?bNT$C;txva{4WTuJpt+A0a*@EHO}{Gp zD|>R^g(s#xtkUZVTfY)J65j7PcyhMC=wD$%>AQNF&X6D^&+ zyXc&YshEH5Db$@YJJJhB)ox)CcO4yN+pa2u{uhim-}{DzA7-^fPIM1N1QJuflrn7z zX+`mplK1riHqS>;?HgdFx2+~ygsjX8d%ZdeeSz&xC<`nr$F0|zW=&$GgwT^IM|3&e`@VyTUD!j1+6VE?fAJIS-p*Y-{P*s{-&FUk`rZHDNe|E#4vdAo&fgN# zQqy6uiST?E=|Pq4dhY&3_j$*+8=CN&7ytRT^9`IN z2jY_uU0t)h=Ut=19jLrfG3L!qPqAJ{xBx>@-ogLH_{Oa*s^DTHiAX)Zwa^5>)#V@_ z7svbct`KRz=MF3?U)eL#51+TIkNA6WB^0&n;W8Bj8{2qJru==^RrMg6WfitH7uoW0 zM4Cc3Mqo546W=Q@awoMjMXc!NKRfqA04uR=rvi^?Y_$Jt6~RUkd45_qg>C`iMy}D! ze|I>by%PcVCQs1e|NW6*`vCdqk@2eFM*M1X9A{Tt#7PG-O=wJUi`2wD|KBbD|4Z^} aJeJ~~sNZ&L7?B?U_tVuf)cmUM5dD7*zoIh$ literal 0 HcmV?d00001 diff --git a/final-presentation/slides.tex b/final-presentation/slides.tex new file mode 100644 index 0000000000000..892d31602c824 --- /dev/null +++ b/final-presentation/slides.tex @@ -0,0 +1,444 @@ +\documentclass{beamer} +\usecolortheme{beaver} +\beamertemplatenavigationsymbolsempty + +% Fonts +\usepackage{fontspec} +\setmainfont{Source Serif Pro}[Ligatures=TeX] +\setsansfont{Source Sans Pro}[Ligatures=TeX] +\setmonofont{Source Code Pro}[ + BoldFont={* Medium}, + BoldItalicFont={* Medium Italic}, +] + +\usepackage[outputdir=out]{minted} +\usepackage{tikz} +\usetikzlibrary{positioning, fit} + +\tikzset{ + invisible/.style={opacity=0,text opacity=0}, + highlight/.style={color=red}, + intro/.code args={<#1>}{% + \only<#1>{\pgfkeysalso{highlight}} + \alt<#1->{}{\pgfkeysalso{invisible}} + }, +} + +\title{Miri} +\subtitle{An interpreter for Rust's mid-level intermediate representation} +\author{ + Scott Olson + \texorpdfstring{\\ \scriptsize{Supervisor: Christopher Dutchyn}}{} +} +\institute{ + CMPT 400 \\ + University of Saskatchewan +} +\date{} +\titlegraphic{ + \includegraphics[width=64px,height=64px]{rust-logo-512x512.png} \\ + \scriptsize{\url{https://www.rust-lang.org}} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Intro slides +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{document} + +\maketitle + +\begin{frame}[fragile] + \frametitle{What is Rust? \small{[review]}} + + According to the website\dots + + \begin{quote} + \textbf{Rust} is a systems programming language that runs blazingly fast, + prevents nearly all segfaults, and guarantees thread safety. + \end{quote} + + It's a new programming language from Mozilla, and it looks like this: + + \begin{minted}[ + autogobble, + fontsize=\footnotesize, + mathescape, + xleftmargin=.3in, + ]{rust} + fn factorial(n: u64) -> u64 { + (1..n).fold(1, |a, b| a * b) + } + + fn main() { + for x in 1..6 { + println!("{}", factorial(x)); + } + // $\Rightarrow$ 1 + // $\Rightarrow$ 1 + // $\Rightarrow$ 2 + // $\Rightarrow$ 6 + // $\Rightarrow$ 24 + } + \end{minted} +\end{frame} + +\begin{frame} + \frametitle{How does Rust compile code? \onslide<-6>{\small{[review]}}} + + \begin{center} + \begin{tikzpicture}[x=4cm, y=3.5cm, auto, rounded corners] + \tikzstyle{basic-stage}=[rectangle, draw, thick, align=center] + \tikzstyle{stage}=[basic-stage, font=\tiny] + \tikzstyle{pass}=[thick, -stealth] + \tikzstyle{pass-label}=[font=\footnotesize] + + \node[basic-stage] (src) at (0,0) {Source\\Code}; + \node[basic-stage] (mach) at (2,-1) {Machine\\Code}; + + \draw<1>[pass, out=0, in=180] + (src.east) to node[font=\Huge] {?} (mach.west); + + \node[stage, intro=<2>] (ast) at (1,0) + {\normalsize{AST} \\ Abstract Syntax Tree}; + \draw[pass, intro=<2>] + (src) -- node[pass-label] {Parse} (ast); + + \node[stage, intro=<3>] (hir) at (2,0) + {\normalsize{HIR} \\ High-level Intermediate\\Representation}; + \draw[pass, intro=<3>] + (ast) -- node[pass-label] {Simplify} (hir); + + \node[stage, intro=<4>] (mir) at (0,-1) + {\normalsize{MIR} \\ Mid-level Intermediate\\Representation}; + \path (hir.south) -- coordinate (middle) (mir.north); + \draw[pass, intro=<4>] + (hir.south) |- (middle) -| (mir.north); + \node[pass-label, above, intro=<4>] at (middle) {Lower}; + + \node[stage, intro=<5>] (llvm) at (1,-1) + {\normalsize{LLVM IR} \\ Low-level Intermediate\\Representation}; + \draw[pass, intro=<5>] + (mir) -- node[pass-label] {Translate} (llvm); + + \draw<6->[pass, intro=<6>] + (llvm) -- node[pass-label] {Magic} (mach); + + \node[stage, intro=<7>] (exec) at (1,-1.75) + {\normalsize{Execution}}; + \draw[pass, intro=<7>] + (mach) -- node[pass-label] {CPU} (exec); + + \draw[pass, intro=<8>] + (mir) -- node[pass-label] {Miri} (exec); + \end{tikzpicture} + \end{center} +\end{frame} + +\begin{frame} + \frametitle{Why build Miri?} + \begin{itemize} + \item For fun and learning. + + \item I originally planned to use it for testing the compiler and execution + of unsafe code, but shifted my goals along the way. \pause + + \item Now it serves as an experimental implementation of the upcoming + compile-time function evaluation feature in Rust. \pause + + \begin{itemize} + \item Similar to C++14's \mintinline{cpp}{constexpr} feature. + + \item You can do complicated calculations at compile time and compile + their \emph{results} into the executable. \pause + + \item For example, you can compute a ``perfect hash function'' for a + statically-known map at compile-time and have guaranteed no-collision + lookup at runtime. \pause + + \item Miri actually supports far more of Rust than C++14's + \mintinline{cpp}{constexpr} does of C++ --- even heap allocation and + unsafe code. + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame} + \frametitle{How was it built?} + + At first I wrote a naive version with a number of downsides: + + \begin{itemize} + \item represented values in a traditional dynamic language format, where + every value was the same size. + + \item didn't work well for aggregates (structs, enums, arrays, etc.). + + \item made unsafe programming tricks that make assumptions about low-level + value layout essentially impossible. + \end{itemize} +\end{frame} + +\begin{frame} + \frametitle{How was it built?} + \begin{itemize} + \item Later, a Rust compiler team member proposed a ``Rust abstract + machine`` with specialized value layout which solved my previous problems. + \pause + + \item His proposal was intended for a compile-time function evaluator in the + Rust compiler, so I effectively implemented an experimental version of + that. \pause + + \item After this point, making Miri work well was primarily a software + engineering problem. + \end{itemize} +\end{frame} + +\begin{frame} + \frametitle{Data layout} + \begin{itemize} + \item Memory in Miri is literally a HashMap from ``allocation IDs'' to + ``abstract allocations''. + + \item Allocations are represented by: \pause + \begin{enumerate} + \item An array of \textbf{raw bytes} with a size based on the type of + the value \pause + \item A set of \textbf{relocations} --- pointers into other abstract + allocations \pause + \item A mask determining which bytes are \textbf{undefined} + \end{enumerate} + \end{itemize} +\end{frame} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Miri example slides +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{frame}[fragile] + \frametitle{\texttt{square} example} + \begin{center} + \begin{minted}[autogobble,fontsize=\scriptsize]{rust} + // Rust + fn square(n: u64) -> u64 { + n * n + } + + // Generated MIR + fn square(arg0: u64) -> u64 { + let var0: u64; // n // On function entry, Miri creates + // virtual allocations for all the + // arguments, variables, and + // temporaries. + + bb0: { + var0 = arg0; // Copy the argument into `n`. + return = Mul(var0, var0); // Multiply `n` with itself. + goto -> bb1; // Jump to basic block `bb1`. + } + + bb1: { + return; // Return from the current fn. + } + } + \end{minted} + \end{center} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{sum} example} + \begin{center} + \begin{minted}[autogobble,fontsize=\tiny]{rust} + // Rust + fn sum() -> u64 { + let mut sum = 0; let mut i = 0; + while i < 10 { sum += i; i += 1; } + sum + } + + // Generated MIR + fn sum() -> u64 { + let mut var0: u64; // sum + let mut var1: u64; // i + let mut tmp0: bool; + + bb0: { + // sum = 0; i = 0; + var0 = const 0u64; var1 = const 0u64; goto -> bb1; + } + bb1: { + // if i < 10 { goto bb2; } else { goto bb3; } + tmp0 = Lt(var1, const 10u64); + if(tmp0) -> [true: bb2, false: bb3]; + } + bb2: { + var0 = Add(var0, var1); // sum = sum + i; + var1 = Add(var1, const 1u64); // i = i + 1; + goto -> bb1; + } + bb3: { + return = var0; goto -> bb4; + } + bb4: { return; } + } + \end{minted} + \end{center} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Heap allocations!} + \begin{minted}[autogobble,fontsize=\scriptsize]{rust} + fn make_vec() -> Vec { + // Empty array with space for 4 bytes - allocated on the heap! + let mut vec = Vec::with_capacity(4); + // Initialize the first two slots. + vec.push(1); + vec.push(2); + vec + } + + // For reference: + // struct Vec { capacity: usize, data: *mut T, length: usize } + + // Resulting allocations (on 32-bit little-endian architectures): + // Region A: + // 04 00 00 00 00 00 00 00 02 00 00 00 + // └───(B)───┘ + // + // Region B: + // 01 02 __ __ (underscores denote undefined bytes) + \end{minted} + + \footnotesize{Evaluating the above involves a number of compiler built-ins, + ``unsafe'' code blocks, and more inside the standard library, + but Miri handles it all.} +\end{frame} + +\begin{frame}[fragile] + \frametitle{Unsafe code!} + \begin{minted}[autogobble,fontsize=\scriptsize]{rust} + fn out_of_bounds() -> u8 { + let mut vec = vec![1, 2] + unsafe { *vec.get_unchecked(5) } + } + + // test.rs:3: error: pointer offset outside bounds of allocation + // test.rs:3: unsafe { *vec.get_unchecked(5) } + // ^~~~~~~~~~~~~~~~~~~~~ + + fn undefined_bytes() -> u8 { + let mut vec = Vec::with_capacity(10); + unsafe { *vec.get_unchecked(5) } + } + + // test.rs:3: error: attempted to read undefined bytes + // test.rs:3: unsafe { *vec.get_unchecked(5) } + // ^~~~~~~~~~~~~~~~~~~~~ + \end{minted} +\end{frame} + +\begin{frame} + \frametitle{What can't Miri do?} + \begin{itemize} + \item Miri can't do all the stuff I didn't implement yet. :) + \begin{itemize} + \item non-trivial casts + \item function pointers + \item calling destructors and freeing memory + \item handling all constants properly (but, well, Miri might be + replacing the old constants system) + \end{itemize} + + \item Miri can't do foreign function calls (e.g. calling functions defined + in C or C++), but there is a reasonable way it could be done with libffi. + \begin{itemize} + \item On the other hand, for constant evaluation in the compiler, you + want the evaluator to be deterministic and safe, so FFI calls might be + banned anyway. + \end{itemize} + + \item Without quite some effort, Miri will probably never handle inline + assembly... + \end{itemize} +\end{frame} + +\begin{frame} + \begin{center} + \LARGE{Questions?} + \end{center} +\end{frame} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Extra slides +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{frame}[fragile] + \frametitle{\texttt{varN} vs. \texttt{argN}} + \begin{center} + \begin{minted}[autogobble,fontsize=\scriptsize]{rust} + // Rust + type Pair = (u64, u64); + fn swap((a, b): Pair) -> Pair { + (b, a) + } + + // Generated MIR + fn swap(arg0: (u64, u64)) -> (u64, u64) { + let var0: u64; // a + let var1: u64; // b + + bb0: { + var0 = arg0.0; // get the 1st part of the pair + var1 = arg0.1; // get the 2nd part of the pair + return = (var0, var1); // build a new pair in the result + goto -> bb1; + } + + bb1: { + return; + } + } + \end{minted} + \end{center} +\end{frame} + +\begin{frame}[fragile] + \frametitle{\texttt{factorial} example} + \begin{center} + \begin{minted}[autogobble,fontsize=\tiny]{rust} + // Rust + fn factorial(n: u64) -> u64 { + (1..n).fold(1, |a, b| a * b) + } + + // Generated MIR + fn factorial(arg0: u64) -> u64 { + let var0: u64; // n + let mut tmp0: Range; // Miri calculates sizes for generics like Range. + let mut tmp1: [closure]; + + bb0: { + var0 = arg0; + + // tmp0 = 1..n + tmp0 = Range { start: const 1u64, end: var0 }; + + // tmp1 = |a, b| a * b + tmp1 = [closure]; + + // This loads the MIR for the `fold` fn from the standard library. + // In general, MIR for any function from any library can be loaded. + // return tmp0.fold(1, tmp1) + return = Range::fold(tmp0, const 1u64, tmp1) -> bb1; + } + + bb1: { + return; + } + } + \end{minted} + \end{center} +\end{frame} + +\end{document} From cb947dd005b0168dd719aa136d12c4c54a0b063c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 3 Apr 2016 23:25:30 -0600 Subject: [PATCH 0198/1096] Add note about target data layout. --- final-presentation/slides.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/final-presentation/slides.tex b/final-presentation/slides.tex index 892d31602c824..bd10c85b5a83b 100644 --- a/final-presentation/slides.tex +++ b/final-presentation/slides.tex @@ -212,10 +212,6 @@ \end{itemize} \end{frame} -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Miri example slides -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - \begin{frame}[fragile] \frametitle{\texttt{square} example} \begin{center} @@ -346,9 +342,12 @@ \item non-trivial casts \item function pointers \item calling destructors and freeing memory + \item taking target architecture endianess and alignment information + into account when computing data layout \item handling all constants properly (but, well, Miri might be replacing the old constants system) \end{itemize} + \pause \item Miri can't do foreign function calls (e.g. calling functions defined in C or C++), but there is a reasonable way it could be done with libffi. @@ -357,6 +356,7 @@ want the evaluator to be deterministic and safe, so FFI calls might be banned anyway. \end{itemize} + \pause \item Without quite some effort, Miri will probably never handle inline assembly... From 9b6567e8b32381c5eb3e44d8d1f22f36f6d7308b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 4 Apr 2016 15:46:13 -0600 Subject: [PATCH 0199/1096] Fix quote kind in slides LaTeX. --- final-presentation/slides.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final-presentation/slides.tex b/final-presentation/slides.tex index bd10c85b5a83b..76c25466e0743 100644 --- a/final-presentation/slides.tex +++ b/final-presentation/slides.tex @@ -183,7 +183,7 @@ \frametitle{How was it built?} \begin{itemize} \item Later, a Rust compiler team member proposed a ``Rust abstract - machine`` with specialized value layout which solved my previous problems. + machine'' with specialized value layout which solved my previous problems. \pause \item His proposal was intended for a compile-time function evaluator in the From 682742c223f442b883520962765fd06e33be482b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 4 Apr 2016 20:07:22 -0600 Subject: [PATCH 0200/1096] Print terminator kinds (without spans) when debugging. --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 09b380f92b7c7..94e306140e6dd 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -174,7 +174,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } let terminator = block_data.terminator(); - print_trace(terminator, "", self.stack.len() + 1); + print_trace(&terminator.kind, "", self.stack.len() + 1); let result = self.eval_terminator(terminator); match try!(self.maybe_report(terminator.span, result)) { From bdba4641ccc113a865759cc52c643a65adfd371d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 4 Apr 2016 20:33:41 -0600 Subject: [PATCH 0201/1096] Rearrange code in memory.rs. --- src/memory.rs | 80 +++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 18638b8b0321d..ccb9396e95e69 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,47 +7,9 @@ use std::ptr; use error::{EvalError, EvalResult}; use primval::PrimVal; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct AllocId(u64); - -#[derive(Debug)] -pub struct Allocation { - pub bytes: Box<[u8]>, - pub relocations: BTreeMap, - - /// Stores a list of indices `[a_0, a_1, ..., a_n]`. Bytes in the range `0..a_0` are considered - /// defined, `a_0..a_1` are undefined, `a_1..a_2` are defined and so on until - /// `a_n..bytes.len()`. These ranges are all end-exclusive. - /// - /// In general a byte's definedness can be found by binary searching this list of indices, - /// finding where the byte would fall, and taking the position of nearest index mod 2. This - /// yields 0 for defined and 1 for undefined. - /// - /// Some noteworthy cases: - /// * `[]` represents a fully-defined allocation. - /// * `[0]` represents a fully-undefined allocation. (The empty `0..0` is defined and - /// `0..bytes.len()` is undefined.) - /// * However, to avoid allocation, fully-undefined allocations can be represented as `None`. - pub undef_mask: Option>, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Pointer { - pub alloc_id: AllocId, - pub offset: usize, -} - -impl Pointer { - pub fn offset(self, i: isize) -> Self { - Pointer { offset: (self.offset as isize + i) as usize, ..self } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FieldRepr { - pub offset: usize, - pub size: usize, -} +//////////////////////////////////////////////////////////////////////////////// +// Value representations +//////////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug, Eq, PartialEq)] pub enum Repr { @@ -77,6 +39,12 @@ pub enum Repr { }, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct FieldRepr { + pub offset: usize, + pub size: usize, +} + impl Repr { pub fn size(&self) -> usize { match *self { @@ -87,6 +55,36 @@ impl Repr { } } +//////////////////////////////////////////////////////////////////////////////// +// Allocations and pointers +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct AllocId(u64); + +#[derive(Debug)] +pub struct Allocation { + pub bytes: Box<[u8]>, + pub relocations: BTreeMap, + pub undef_mask: Option>, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Pointer { + pub alloc_id: AllocId, + pub offset: usize, +} + +impl Pointer { + pub fn offset(self, i: isize) -> Self { + Pointer { offset: (self.offset as isize + i) as usize, ..self } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Top-level interpreter memory +//////////////////////////////////////////////////////////////////////////////// + pub struct Memory { alloc_map: HashMap, next_id: u64, From 8a0aa9291a423cd031d040b1d7a38d728cc6d60a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 03:45:06 -0600 Subject: [PATCH 0202/1096] Switch to bitmask-based undef mask. --- src/interpreter.rs | 35 ++--- src/memory.rs | 327 ++++++++++++++------------------------------- test/products.rs | 6 +- 3 files changed, 117 insertions(+), 251 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 94e306140e6dd..b050e042f711d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -19,7 +19,7 @@ use syntax::attr; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{self, FieldRepr, Memory, Pointer, Repr}; +use memory::{FieldRepr, Memory, Pointer, Repr}; use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = false; @@ -150,31 +150,32 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { r } - fn run(&mut self) -> EvalResult<()> { - use std::fmt::Debug; - fn print_trace(t: &T, suffix: &'static str, indent: usize) { - if !TRACE_EXECUTION { return; } - for _ in 0..indent { print!(" "); } - println!("{:?}{}", t, suffix); - } + fn log(&self, extra_indent: usize, f: F) where F: FnOnce() { + let indent = self.stack.len() - 1 + extra_indent; + if !TRACE_EXECUTION { return; } + for _ in 0..indent { print!(" "); } + f(); + println!(""); + } + fn run(&mut self) -> EvalResult<()> { 'outer: while !self.stack.is_empty() { let mut current_block = self.frame().next_block; loop { - print_trace(¤t_block, ":", self.stack.len()); + self.log(0, || print!("{:?}", current_block)); let current_mir = self.mir().clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { - print_trace(stmt, "", self.stack.len() + 1); + self.log(1, || print!("{:?}", stmt)); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.eval_assignment(lvalue, rvalue); try!(self.maybe_report(stmt.span, result)); } let terminator = block_data.terminator(); - print_trace(&terminator.kind, "", self.stack.len() + 1); + self.log(1, || print!("{:?}", terminator.kind)); let result = self.eval_terminator(terminator); match try!(self.maybe_report(terminator.span, result)) { @@ -1154,15 +1155,6 @@ pub fn get_impl_method<'tcx>( } pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { - /// Print the given allocation and all allocations it depends on. - fn print_allocation_tree(memory: &Memory, alloc_id: memory::AllocId) { - let alloc = memory.get(alloc_id).unwrap(); - println!(" {:?}: {:?}", alloc_id, alloc); - for &target_alloc in alloc.relocations.values() { - print_allocation_tree(memory, target_alloc); - } - } - for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { use syntax::attr::AttrMetaMethods; @@ -1188,8 +1180,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) tcx.sess.abort_if_errors(); if let Some(ret) = return_ptr { - println!("Result:"); - print_allocation_tree(&miri.memory, ret.alloc_id); + miri.memory.dump(ret.alloc_id); println!(""); } } diff --git a/src/memory.rs b/src/memory.rs index ccb9396e95e69..113930f986d2c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,8 +1,7 @@ use byteorder::{ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; -use std::collections::{btree_map, BTreeMap, HashMap}; use std::collections::Bound::{Included, Excluded}; -use std::mem; -use std::ptr; +use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; +use std::{iter, mem, ptr}; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -66,7 +65,7 @@ pub struct AllocId(u64); pub struct Allocation { pub bytes: Box<[u8]>, pub relocations: BTreeMap, - pub undef_mask: Option>, + pub undef_mask: UndefMask, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -107,7 +106,7 @@ impl Memory { let alloc = Allocation { bytes: vec![0; size].into_boxed_slice(), relocations: BTreeMap::new(), - undef_mask: None, + undef_mask: UndefMask::new(), }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; @@ -129,6 +128,48 @@ impl Memory { self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) } + /// Print an allocation and all allocations it points to, recursively. + pub fn dump(&self, id: AllocId) { + let mut allocs_seen = HashSet::new(); + let mut allocs_to_print = VecDeque::new(); + allocs_to_print.push_back(id); + + while let Some(id) = allocs_to_print.pop_front() { + allocs_seen.insert(id.0); + let alloc = self.get(id).unwrap(); + let prefix = format!("Alloc {:<5} ", format!("{}:", id.0)); + print!("{}", prefix); + let mut relocations = vec![]; + + for i in 0..alloc.bytes.len() { + if let Some(&target_id) = alloc.relocations.get(&i) { + if !allocs_seen.contains(&target_id.0) { + allocs_to_print.push_back(target_id); + } + relocations.push((i, target_id.0)); + } + if alloc.undef_mask.is_range_defined(i, i+1) { + print!("{:02x} ", alloc.bytes[i]); + } else { + print!("__ "); + } + } + println!(""); + + if !relocations.is_empty() { + print!("{:1$}", "", prefix.len()); // Print spaces. + let mut pos = 0; + let relocation_width = (self.pointer_size - 1) * 3; + for (i, target_id) in relocations { + print!("{:1$}", "", (i - pos) * 3); + print!("└{0:─^1$}┘ ", format!("({})", target_id), relocation_width); + pos = i + self.pointer_size; + } + println!(""); + } + } + } + //////////////////////////////////////////////////////////////////////////////// // Byte accessors //////////////////////////////////////////////////////////////////////////////// @@ -305,8 +346,8 @@ impl Memory { // Mark parts of the outermost relocations as undefined if they partially fall outside the // given range. - if first < start { alloc.mark_definedness(first, start, false); } - if last > end { alloc.mark_definedness(end, last, false); } + if first < start { alloc.undef_mask.set_range(first, start, false); } + if last > end { alloc.undef_mask.set_range(end, last, false); } // Forget all the relocations. for k in keys { alloc.relocations.remove(&k); } @@ -340,7 +381,7 @@ impl Memory { fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { let alloc = try!(self.get(ptr.alloc_id)); - if !alloc.is_range_defined(ptr.offset, ptr.offset + size) { + if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); } Ok(()) @@ -350,7 +391,7 @@ impl Memory { -> EvalResult<()> { let mut alloc = try!(self.get_mut(ptr.alloc_id)); - alloc.mark_definedness(ptr.offset, ptr.offset + size, new_state); + alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) } } @@ -359,238 +400,72 @@ impl Memory { // Undefined byte tracking //////////////////////////////////////////////////////////////////////////////// -impl Allocation { - /// Check whether the range `start..end` (end-exclusive) in this allocation is entirely - /// defined. - fn is_range_defined(&self, start: usize, end: usize) -> bool { - debug_assert!(start <= end); - debug_assert!(end <= self.bytes.len()); - - // An empty range is always fully defined. - if start == end { - return true; - } +type Block = u64; +const BLOCK_SIZE: usize = 64; - match self.undef_mask { - Some(ref undef_mask) => { - // If `start` lands directly on a boundary, it belongs to the range after the - // boundary, hence the increment in the `Ok` arm. - let i = match undef_mask.binary_search(&start) { Ok(j) => j + 1, Err(j) => j }; +#[derive(Clone, Debug)] +pub struct UndefMask { + blocks: Vec, + len: usize, +} - // The range is fully defined if and only if both: - // 1. The start value falls into a defined range (with even parity). - // 2. The end value is in the same range as the start value. - i % 2 == 0 && undef_mask.get(i).map(|&x| end <= x).unwrap_or(true) - } - None => false, +impl UndefMask { + fn new() -> Self { + UndefMask { + blocks: vec![], + len: 0, } } - /// Mark the range `start..end` (end-exclusive) as defined or undefined, depending on - /// `new_state`. - fn mark_definedness(&mut self, start: usize, end: usize, new_state: bool) { - debug_assert!(start <= end); - debug_assert!(end <= self.bytes.len()); - - // There is no need to track undef masks for zero-sized allocations. - let len = self.bytes.len(); - if len == 0 { - return; - } - - // Returns whether the new state matches the state of a given undef mask index. The way - // undef masks are represented, boundaries at even indices are undefined and those at odd - // indices are defined. - let index_matches_new_state = |i| i % 2 == new_state as usize; - - // Lookup the undef mask index where the given endpoint `i` is or should be inserted. - let lookup_endpoint = |undef_mask: &[usize], i: usize| -> (usize, bool) { - let (index, should_insert); - match undef_mask.binary_search(&i) { - // Region endpoint is on an undef mask boundary. - Ok(j) => { - // This endpoint's index must be incremented if the boundary's state matches - // the region's new state so that the boundary is: - // 1. Excluded from deletion when handling the inclusive left-hand endpoint. - // 2. Included for deletion when handling the exclusive right-hand endpoint. - index = j + index_matches_new_state(j) as usize; - - // Don't insert a new mask boundary; simply reuse or delete the matched one. - should_insert = false; - } - - // Region endpoint is not on a mask boundary. - Err(j) => { - // This is the index after the nearest mask boundary which has the same state. - index = j; - - // Insert a new boundary if this endpoint's state doesn't match the state of - // this position. - should_insert = index_matches_new_state(j); - } - } - (index, should_insert) - }; - - match self.undef_mask { - // There is an existing undef mask, with arbitrary existing boundaries. - Some(ref mut undef_mask) => { - // Determine where the new range's endpoints fall within the current undef mask. - let (start_index, insert_start) = lookup_endpoint(undef_mask, start); - let (end_index, insert_end) = lookup_endpoint(undef_mask, end); - - // Delete all the undef mask boundaries overwritten by the new range. - undef_mask.drain(start_index..end_index); - - // Insert any new boundaries deemed necessary with two exceptions: - // 1. Never insert an endpoint equal to the allocation length; it's implicit. - // 2. Never insert a start boundary equal to the end boundary. - if insert_end && end != len { - undef_mask.insert(start_index, end); - } - if insert_start && start != end { - undef_mask.insert(start_index, start); - } - } - - // There is no existing undef mask. This is taken as meaning the entire allocation is - // currently undefined. If the new state is false, meaning undefined, do nothing. - None => if new_state { - let mut mask = if start == 0 { - // 0..end is defined. - Vec::new() - } else { - // 0..0 is defined, 0..start is undefined, start..end is defined. - vec![0, start] - }; - - // Don't insert the end boundary if it's equal to the allocation length; that - // boundary is implicit. - if end != len { - mask.push(end); - } - self.undef_mask = Some(mask); - }, + /// Check whether the range `start..end` (end-exclusive) is entirely defined. + fn is_range_defined(&self, start: usize, end: usize) -> bool { + if end > self.len { return false; } + for i in start..end { + if !self.get(i) { return false; } } + true } -} -#[cfg(test)] -mod test { - use memory::Allocation; - use std::collections::BTreeMap; - - fn alloc_with_mask(len: usize, undef_mask: Option>) -> Allocation { - Allocation { - bytes: vec![0; len].into_boxed_slice(), - relocations: BTreeMap::new(), - undef_mask: undef_mask, - } + fn set_range(&mut self, start: usize, end: usize, new_state: bool) { + let len = self.len; + if end > len { self.grow(end - len, new_state); } + self.set_range_inbounds(start, end, new_state); } - #[test] - fn large_undef_mask() { - let mut alloc = alloc_with_mask(20, Some(vec![4, 8, 12, 16])); - - assert!(alloc.is_range_defined(0, 0)); - assert!(alloc.is_range_defined(0, 3)); - assert!(alloc.is_range_defined(0, 4)); - assert!(alloc.is_range_defined(1, 3)); - assert!(alloc.is_range_defined(1, 4)); - assert!(alloc.is_range_defined(4, 4)); - assert!(!alloc.is_range_defined(0, 5)); - assert!(!alloc.is_range_defined(1, 5)); - assert!(!alloc.is_range_defined(4, 5)); - assert!(!alloc.is_range_defined(4, 8)); - assert!(alloc.is_range_defined(8, 12)); - assert!(!alloc.is_range_defined(12, 16)); - assert!(alloc.is_range_defined(16, 20)); - assert!(!alloc.is_range_defined(15, 20)); - assert!(!alloc.is_range_defined(0, 20)); - - alloc.mark_definedness(8, 11, false); - assert_eq!(alloc.undef_mask, Some(vec![4, 11, 12, 16])); - - alloc.mark_definedness(8, 11, true); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); - - alloc.mark_definedness(8, 12, false); - assert_eq!(alloc.undef_mask, Some(vec![4, 16])); - - alloc.mark_definedness(8, 12, true); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); - - alloc.mark_definedness(9, 11, true); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); - - alloc.mark_definedness(9, 11, false); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 9, 11, 12, 16])); - - alloc.mark_definedness(9, 10, true); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 10, 11, 12, 16])); - - alloc.mark_definedness(8, 12, true); - assert_eq!(alloc.undef_mask, Some(vec![4, 8, 12, 16])); + fn set_range_inbounds(&mut self, start: usize, end: usize, new_state: bool) { + for i in start..end { self.set(i, new_state); } } - #[test] - fn empty_undef_mask() { - let mut alloc = alloc_with_mask(0, None); - assert!(alloc.is_range_defined(0, 0)); - - alloc.mark_definedness(0, 0, false); - assert_eq!(alloc.undef_mask, None); - assert!(alloc.is_range_defined(0, 0)); - - alloc.mark_definedness(0, 0, true); - assert_eq!(alloc.undef_mask, None); - assert!(alloc.is_range_defined(0, 0)); + fn get(&self, i: usize) -> bool { + let (block, bit) = bit_index(i); + (self.blocks[block] & 1 << bit) != 0 } - #[test] - fn small_undef_mask() { - let mut alloc = alloc_with_mask(8, None); - - alloc.mark_definedness(0, 4, false); - assert_eq!(alloc.undef_mask, None); - - alloc.mark_definedness(0, 4, true); - assert_eq!(alloc.undef_mask, Some(vec![4])); - - alloc.mark_definedness(4, 8, false); - assert_eq!(alloc.undef_mask, Some(vec![4])); - - alloc.mark_definedness(4, 8, true); - assert_eq!(alloc.undef_mask, Some(vec![])); - - alloc.mark_definedness(0, 8, true); - assert_eq!(alloc.undef_mask, Some(vec![])); - - alloc.mark_definedness(0, 8, false); - assert_eq!(alloc.undef_mask, Some(vec![0])); - - alloc.mark_definedness(0, 8, true); - assert_eq!(alloc.undef_mask, Some(vec![])); - - alloc.mark_definedness(4, 8, false); - assert_eq!(alloc.undef_mask, Some(vec![4])); - - alloc.mark_definedness(0, 8, false); - assert_eq!(alloc.undef_mask, Some(vec![0])); - - alloc.mark_definedness(2, 5, true); - assert_eq!(alloc.undef_mask, Some(vec![0, 2, 5])); - - alloc.mark_definedness(4, 6, false); - assert_eq!(alloc.undef_mask, Some(vec![0, 2, 4])); + fn set(&mut self, i: usize, new_state: bool) { + let (block, bit) = bit_index(i); + if new_state { + self.blocks[block] |= 1 << bit; + } else { + self.blocks[block] &= !(1 << bit); + } + } - alloc.mark_definedness(0, 3, true); - assert_eq!(alloc.undef_mask, Some(vec![4])); + fn grow(&mut self, amount: usize, new_state: bool) { + let unused_trailing_bits = self.blocks.len() * BLOCK_SIZE - self.len; + if amount > unused_trailing_bits { + let additional_blocks = amount / BLOCK_SIZE + 1; + self.blocks.extend(iter::repeat(0).take(additional_blocks)); + } + let start = self.len; + self.len += amount; + self.set_range_inbounds(start, start + amount, new_state); + } +} - alloc.mark_definedness(2, 6, true); - assert_eq!(alloc.undef_mask, Some(vec![6])); +// fn uniform_block(state: bool) -> Block { +// if state { !0 } else { 0 } +// } - alloc.mark_definedness(3, 7, false); - assert_eq!(alloc.undef_mask, Some(vec![3])); - } +fn bit_index(bits: usize) -> (usize, usize) { + (bits / BLOCK_SIZE, bits % BLOCK_SIZE) } diff --git a/test/products.rs b/test/products.rs index 4028670e6f2be..ba72bfb52a704 100644 --- a/test/products.rs +++ b/test/products.rs @@ -2,17 +2,17 @@ #![allow(dead_code, unused_attributes)] #[miri_run] -fn tuple() -> (i64,) { +fn tuple() -> (i16,) { (1,) } #[miri_run] -fn tuple_2() -> (i64, i64) { +fn tuple_2() -> (i16, i16) { (1, 2) } #[miri_run] -fn tuple_5() -> (i64, i64, i64, i64, i64) { +fn tuple_5() -> (i16, i16, i16, i16, i16) { (1, 2, 3, 4, 5) } From c08ddaaa48074040eeecf75e243cb5cbc19f4eaf Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:08:52 -0600 Subject: [PATCH 0203/1096] Implement a naive, slow version of undef mask copying. --- src/memory.rs | 20 ++++++++++++++++++-- test/sums.rs | 5 +++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 113930f986d2c..c8f19fa165fa5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -225,8 +225,10 @@ impl Memory { } } - // TODO(tsion): Copy undef ranges from src to dest. - self.copy_relocations(src, dest, size) + try!(self.copy_undef_mask(src, dest, size)); + try!(self.copy_relocations(src, dest, size)); + + Ok(()) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { @@ -379,6 +381,20 @@ impl Memory { // Undefined bytes //////////////////////////////////////////////////////////////////////////////// + // FIXME(tsino): This is a very naive, slow version. + fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + // The bits have to be saved locally before writing to dest in case src and dest overlap. + let mut v = Vec::with_capacity(size); + for i in 0..size { + let defined = try!(self.get(src.alloc_id)).undef_mask.get(src.offset + i); + v.push(defined); + } + for (i, defined) in v.into_iter().enumerate() { + try!(self.get_mut(dest.alloc_id)).undef_mask.set(dest.offset + i, defined); + } + Ok(()) + } + fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { let alloc = try!(self.get(ptr.alloc_id)); if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { diff --git a/test/sums.rs b/test/sums.rs index c63d3e6804501..67257050364c9 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -47,3 +47,8 @@ fn match_opt_some() -> i8 { None => 20, } } + +#[miri_run] +fn two_nones() -> (Option, Option) { + (None, None) +} From dbd8a826431b959af98141bbc52aada52931b508 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:19:36 -0600 Subject: [PATCH 0204/1096] Add a test for overwriting part of a relocation. --- src/interpreter.rs | 31 ++++++++++++++++++++++++------- test/errors.rs | 12 ++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100755 test/errors.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index b050e042f711d..d0faf7dc15330 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -411,6 +411,25 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.copy(val, ptr, size)); } + // FIXME(tsion): Handle different integer types correctly. + "add_with_overflow" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.ty_size(ty); + + let left_arg = try!(self.eval_operand(&args[0])); + let right_arg = try!(self.eval_operand(&args[1])); + + let left = try!(self.memory.read_int(left_arg, size)); + let right = try!(self.memory.read_int(right_arg, size)); + + let (n, overflowed) = unsafe { + ::std::intrinsics::add_with_overflow::(left, right) + }; + + try!(self.memory.write_int(dest, n, size)); + try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); + } + // FIXME(tsion): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); @@ -1174,15 +1193,13 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) }; let substs = miri.tcx.mk_substs(Substs::empty()); miri.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); - if let Err(e) = miri.run() { - tcx.sess.err(&e.to_string()); - } - tcx.sess.abort_if_errors(); - - if let Some(ret) = return_ptr { + if let Err(_e) = miri.run() { + // TODO(tsion): Detect whether the error was already reported or not. + // tcx.sess.err(&e.to_string()); + } else if let Some(ret) = return_ptr { miri.memory.dump(ret.alloc_id); - println!(""); } + println!(""); } } } diff --git a/test/errors.rs b/test/errors.rs new file mode 100755 index 0000000000000..1b0401bb9cc24 --- /dev/null +++ b/test/errors.rs @@ -0,0 +1,12 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { + let mut p: *const i32 = &42; + unsafe { + let ptr = &mut p as *mut *const i32 as *mut u32; + *ptr = 123; + *p + } +} From 7568a0b5b1f722e83a46135aa53cb7056b6fd908 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:20:35 -0600 Subject: [PATCH 0205/1096] Add an Rc reference cycle test. --- test/std.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/test/std.rs b/test/std.rs index 17511a193718e..36f9ca6a1ff02 100644 --- a/test/std.rs +++ b/test/std.rs @@ -1,21 +1,22 @@ #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use std::sync::Arc; + #[miri_run] -fn rc_cell() -> i32 { - use std::rc::Rc; - use std::cell::Cell; +fn rc_cell() -> Rc> { let r = Rc::new(Cell::new(42)); let x = r.get(); r.set(x + x); - r.get() + r } // TODO(tsion): borrow code needs to evaluate string statics via Lvalue::Static +// TODO(tsion): also requires destructors to run for the second borrow to work // #[miri_run] // fn rc_refcell() -> i32 { -// use std::rc::Rc; -// use std::cell::RefCell; // let r = Rc::new(RefCell::new(42)); // *r.borrow_mut() += 10; // let x = *r.borrow(); @@ -23,10 +24,19 @@ fn rc_cell() -> i32 { // } #[miri_run] -fn arc() -> i32 { - use std::sync::Arc; +fn arc() -> Arc { let a = Arc::new(42); - *a + a +} + +struct Loop(Rc>>); + +#[miri_run] +fn rc_reference_cycle() -> Loop { + let a = Rc::new(RefCell::new(None)); + let b = a.clone(); + *a.borrow_mut() = Some(Loop(b)); + Loop(a) } #[miri_run] From 2d5196147fed8bc1b84725bcdf8e3744fd806819 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:24:35 -0600 Subject: [PATCH 0206/1096] Add test for comparing ptrs into different allocs. --- test/errors.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/errors.rs b/test/errors.rs index 1b0401bb9cc24..8faff149167e9 100755 --- a/test/errors.rs +++ b/test/errors.rs @@ -3,10 +3,17 @@ #[miri_run] fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { - let mut p: *const i32 = &42; + let mut p = &42; unsafe { - let ptr = &mut p as *mut *const i32 as *mut u32; - *ptr = 123; - *p + let ptr: *mut _ = &mut p; + *(ptr as *mut u32) = 123; } + *p +} + +#[miri_run] +fn pointers_to_different_allocations_are_unorderable() -> bool { + let x: *const u8 = &1; + let y: *const u8 = &2; + x < y } From cfe36d63e53e8778b8778ce0812a13405fd2df75 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:27:09 -0600 Subject: [PATCH 0207/1096] Add test for invalid booleans. --- test/errors.rs | 6 ++++++ test/vecs.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/errors.rs b/test/errors.rs index 8faff149167e9..8d66ec48ad146 100755 --- a/test/errors.rs +++ b/test/errors.rs @@ -17,3 +17,9 @@ fn pointers_to_different_allocations_are_unorderable() -> bool { let y: *const u8 = &2; x < y } + +#[miri_run] +fn invalid_bools_are_rejected() -> u8 { + let b = unsafe { std::mem::transmute::(2) }; + if b { 1 } else { 2 } +} diff --git a/test/vecs.rs b/test/vecs.rs index b2f2f27ceeabf..e9f6c12b44090 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -16,7 +16,7 @@ fn make_vec_macro() -> Vec { #[miri_run] fn make_vec_macro_repeat() -> Vec { - vec![42; 8] + vec![42; 5] } #[miri_run] From 284404da06ace69589dfc1a0fa5fb7249607fc89 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:35:25 -0600 Subject: [PATCH 0208/1096] Fix undef mask initialization and test undef reads. --- src/memory.rs | 10 ++++++---- test/errors.rs | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index c8f19fa165fa5..84cd95409b8b9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -106,7 +106,7 @@ impl Memory { let alloc = Allocation { bytes: vec![0; size].into_boxed_slice(), relocations: BTreeMap::new(), - undef_mask: UndefMask::new(), + undef_mask: UndefMask::new(size), }; self.alloc_map.insert(self.next_id, alloc); self.next_id += 1; @@ -426,11 +426,13 @@ pub struct UndefMask { } impl UndefMask { - fn new() -> Self { - UndefMask { + fn new(size: usize) -> Self { + let mut m = UndefMask { blocks: vec![], len: 0, - } + }; + m.grow(size, false); + m } /// Check whether the range `start..end` (end-exclusive) is entirely defined. diff --git a/test/errors.rs b/test/errors.rs index 8d66ec48ad146..c6e6e16b889fc 100755 --- a/test/errors.rs +++ b/test/errors.rs @@ -23,3 +23,10 @@ fn invalid_bools_are_rejected() -> u8 { let b = unsafe { std::mem::transmute::(2) }; if b { 1 } else { 2 } } + +#[miri_run] +fn undefined_byte_reads_are_rejected() -> u8 { + let v: Vec = Vec::with_capacity(10); + let undef = unsafe { *v.get_unchecked(5) }; + undef + 1 +} From 8d3d8af67acf1828b7f2d248793424310462729a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 04:43:06 -0600 Subject: [PATCH 0209/1096] Add test for out-of-bounds reads. --- test/errors.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/errors.rs b/test/errors.rs index c6e6e16b889fc..f6f5eae9d0fe6 100755 --- a/test/errors.rs +++ b/test/errors.rs @@ -30,3 +30,9 @@ fn undefined_byte_reads_are_rejected() -> u8 { let undef = unsafe { *v.get_unchecked(5) }; undef + 1 } + +#[miri_run] +fn out_of_bounds_reads_are_rejected() -> u8 { + let v: Vec = vec![1, 2]; + unsafe { *v.get_unchecked(5) } +} From f472018fbb0809504803a5f170d517de10b23dd0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 17:29:56 -0600 Subject: [PATCH 0210/1096] Partially implement reallocation (e.g. for growing Vecs). --- src/interpreter.rs | 11 +++++++++++ src/memory.rs | 28 ++++++++++++++++++++++++++-- test/vecs.rs | 13 +++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d0faf7dc15330..58a27a46f879a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -530,6 +530,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_ptr(dest, ptr)); } + "__rust_reallocate" => { + let ptr_arg = try!(self.eval_operand(&args[0])); + let _old_size_arg = try!(self.eval_operand(&args[1])); + let size_arg = try!(self.eval_operand(&args[2])); + let _align_arg = try!(self.eval_operand(&args[3])); + let ptr = try!(self.memory.read_ptr(ptr_arg)); + let size = try!(self.memory.read_usize(size_arg)); + try!(self.memory.reallocate(ptr, size as usize)); + try!(self.memory.write_ptr(dest, ptr)); + } + _ => panic!("can't call C ABI function: {}", link_name), } diff --git a/src/memory.rs b/src/memory.rs index 84cd95409b8b9..c4f12804c8346 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -63,7 +63,7 @@ pub struct AllocId(u64); #[derive(Debug)] pub struct Allocation { - pub bytes: Box<[u8]>, + pub bytes: Vec, pub relocations: BTreeMap, pub undef_mask: UndefMask, } @@ -104,7 +104,7 @@ impl Memory { pub fn allocate(&mut self, size: usize) -> Pointer { let id = AllocId(self.next_id); let alloc = Allocation { - bytes: vec![0; size].into_boxed_slice(), + bytes: vec![0; size], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), }; @@ -116,6 +116,30 @@ impl Memory { } } + // TODO(tsion): Track which allocations were returned from __rust_allocate and report an error + // when reallocating/deallocating any others. + pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> { + if ptr.offset != 0 { + // TODO(tsion): Report error about non-__rust_allocate'd pointer. + panic!() + } + + let alloc = try!(self.get_mut(ptr.alloc_id)); + let size = alloc.bytes.len(); + if new_size > size { + let amount = new_size - size; + alloc.bytes.extend(iter::repeat(0).take(amount)); + alloc.undef_mask.grow(amount, false); + } else if size > new_size { + unimplemented!() + // alloc.bytes.truncate(new_size); + // alloc.undef_mask.len = new_size; + // TODO: potentially remove relocations + } + + Ok(()) + } + //////////////////////////////////////////////////////////////////////////////// // Allocation accessors //////////////////////////////////////////////////////////////////////////////// diff --git a/test/vecs.rs b/test/vecs.rs index e9f6c12b44090..063d7116744d0 100755 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -2,7 +2,7 @@ #![allow(dead_code, unused_attributes)] #[miri_run] -fn make_vec() -> Vec { +fn make_vec() -> Vec { let mut v = Vec::with_capacity(4); v.push(1); v.push(2); @@ -10,7 +10,7 @@ fn make_vec() -> Vec { } #[miri_run] -fn make_vec_macro() -> Vec { +fn make_vec_macro() -> Vec { vec![1, 2] } @@ -23,3 +23,12 @@ fn make_vec_macro_repeat() -> Vec { fn vec_into_iter() -> i32 { vec![1, 2, 3, 4].into_iter().fold(0, |x, y| x + y) } + +#[miri_run] +fn vec_reallocate() -> Vec { + let mut v = vec![1, 2]; + v.push(3); + v.push(4); + v.push(5); + v +} From f4dce09c97076899264e719bdf605a5bf9458cbf Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 17:33:24 -0600 Subject: [PATCH 0211/1096] Print sizes in allocation dumps. --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index c4f12804c8346..b41cd7760b0c1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -178,7 +178,7 @@ impl Memory { print!("__ "); } } - println!(""); + println!("({} bytes)", alloc.bytes.len()); if !relocations.is_empty() { print!("{:1$}", "", prefix.len()); // Print spaces. From c55320a3db854089442f36a3cd6cb5ef4bbae2c6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 19:00:34 -0600 Subject: [PATCH 0212/1096] Update for changes in rustc master. --- src/interpreter.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 58a27a46f879a..9a03d9c44dfd7 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1179,7 +1179,7 @@ pub fn get_impl_method<'tcx>( } } None => { - tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id)) + bug!("method {:?} not found in {:?}", name, impl_def_id); } } } diff --git a/src/lib.rs b/src/lib.rs index ebbeff6d95d41..46b23b589971a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ // From rustc. extern crate arena; -extern crate rustc; +#[macro_use] extern crate rustc; extern crate rustc_data_structures; extern crate rustc_mir; extern crate syntax; From 6a99779740b7f9d299722159e7cb9ec422f642fa Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 19:01:00 -0600 Subject: [PATCH 0213/1096] Rename ty_size -> type_size and ty_to_repr -> type_repr. --- src/interpreter.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 9a03d9c44dfd7..342f94ca9bf6d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -203,7 +203,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let temp_tys = mir.temp_decls.iter().map(|t| t.ty); let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.ty_size(ty); + let size = self.type_size(ty); self.memory.allocate(size) }).collect(); @@ -294,7 +294,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.ty_size(ty); + let size = self.type_size(ty); try!(self.call_intrinsic(&name, substs, args, return_ptr.unwrap(), size)) } @@ -380,7 +380,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.ty_size(elem_ty); + let elem_size = self.type_size(elem_ty); let src_arg = try!(self.eval_operand(&args[0])); let dest_arg = try!(self.eval_operand(&args[1])); @@ -402,7 +402,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "move_val_init" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty); + let size = self.type_size(ty); let ptr_arg = try!(self.eval_operand(&args[0])); let ptr = try!(self.memory.read_ptr(ptr_arg)); @@ -414,7 +414,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FIXME(tsion): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty); + let size = self.type_size(ty); let left_arg = try!(self.eval_operand(&args[0])); let right_arg = try!(self.eval_operand(&args[1])); @@ -433,7 +433,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FIXME(tsion): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty); + let size = self.type_size(ty); let left_arg = try!(self.eval_operand(&args[0])); let right_arg = try!(self.eval_operand(&args[1])); @@ -451,7 +451,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.ty_size(pointee_ty) as isize; + let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = try!(self.eval_operand(&args[0])); let offset_arg = try!(self.eval_operand(&args[1])); @@ -475,7 +475,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { // FIXME(tsion): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty); + let size = self.type_size(ty); let left_arg = try!(self.eval_operand(&args[0])); let right_arg = try!(self.eval_operand(&args[1])); @@ -489,7 +489,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.ty_size(ty) as u64; + let size = self.type_size(ty) as u64; try!(self.memory.write_uint(dest, size, dest_size)); } @@ -672,7 +672,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Box(ty) => { - let size = self.ty_size(ty); + let size = self.type_size(ty); let ptr = self.memory.allocate(size); try!(self.memory.write_ptr(dest, ptr)); } @@ -731,7 +731,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { match *literal { Value { ref value } => Ok(( try!(self.const_to_ptr(value)), - self.ty_to_repr(ty), + self.type_repr(ty), )), Item { .. } => unimplemented!(), } @@ -743,7 +743,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> &'arena Repr { use rustc::mir::tcx::LvalueTy; match self.mir().lvalue_ty(self.tcx, lvalue) { - LvalueTy::Ty { ty } => self.ty_to_repr(ty), + LvalueTy::Ty { ty } => self.type_repr(ty), LvalueTy::Downcast { adt_def, substs, variant_index } => { let field_tys = adt_def.variants[variant_index].fields.iter() .map(|f| f.ty(self.tcx, substs)); @@ -799,8 +799,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Index(ref operand) => { let elem_size = match base_ty.sty { - ty::TyArray(elem_ty, _) => self.ty_size(elem_ty), - ty::TySlice(elem_ty) => self.ty_size(elem_ty), + ty::TyArray(elem_ty, _) => self.type_size(elem_ty), + ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = try!(self.eval_operand(operand)); @@ -876,11 +876,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) } - fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize { - self.ty_to_repr(ty).size() + fn type_size(&self, ty: ty::Ty<'tcx>) -> usize { + self.type_repr(ty).size() } - fn ty_to_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { + fn type_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { let ty = self.monomorphize(ty); if let Some(repr) = self.repr_cache.borrow().get(ty) { @@ -910,7 +910,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } ty::TyArray(elem_ty, length) => Repr::Array { - elem_size: self.ty_size(elem_ty), + elem_size: self.type_size(elem_ty), length: length, }, @@ -948,7 +948,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut size = 0; for ty in field_tys { - let field_size = self.ty_size(ty); + let field_size = self.type_size(ty); let offest = size; size += field_size; fields.push(FieldRepr { offset: offest, size: field_size }); @@ -1197,7 +1197,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) let mut miri = Interpreter::new(tcx, mir_map, &repr_arena); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { - let size = miri.ty_size(ty); + let size = miri.type_size(ty); Some(miri.memory.allocate(size)) } ty::FnDiverging => None, From a75c19336c4bd8a1b66700f50c26ee994592b6cf Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 19:05:17 -0600 Subject: [PATCH 0214/1096] Normalize test file modes. --- test/arrays.rs | 0 test/bools.rs | 0 test/c_enums.rs | 0 test/errors.rs | 0 test/heap.rs | 0 test/specialization.rs | 0 test/strings.rs | 0 test/vecs.rs | 0 8 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 test/arrays.rs mode change 100755 => 100644 test/bools.rs mode change 100755 => 100644 test/c_enums.rs mode change 100755 => 100644 test/errors.rs mode change 100755 => 100644 test/heap.rs mode change 100755 => 100644 test/specialization.rs mode change 100755 => 100644 test/strings.rs mode change 100755 => 100644 test/vecs.rs diff --git a/test/arrays.rs b/test/arrays.rs old mode 100755 new mode 100644 diff --git a/test/bools.rs b/test/bools.rs old mode 100755 new mode 100644 diff --git a/test/c_enums.rs b/test/c_enums.rs old mode 100755 new mode 100644 diff --git a/test/errors.rs b/test/errors.rs old mode 100755 new mode 100644 diff --git a/test/heap.rs b/test/heap.rs old mode 100755 new mode 100644 diff --git a/test/specialization.rs b/test/specialization.rs old mode 100755 new mode 100644 diff --git a/test/strings.rs b/test/strings.rs old mode 100755 new mode 100644 diff --git a/test/vecs.rs b/test/vecs.rs old mode 100755 new mode 100644 From f97eb352225fc6b971c6db799458133301e5d6f3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 6 Apr 2016 19:28:40 -0600 Subject: [PATCH 0215/1096] Change debug log format. --- src/interpreter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 342f94ca9bf6d..24e65b5b678ec 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -151,9 +151,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn log(&self, extra_indent: usize, f: F) where F: FnOnce() { - let indent = self.stack.len() - 1 + extra_indent; + let indent = self.stack.len() + extra_indent; if !TRACE_EXECUTION { return; } - for _ in 0..indent { print!(" "); } + for _ in 0..indent { print!(" "); } f(); println!(""); } @@ -163,19 +163,19 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut current_block = self.frame().next_block; loop { - self.log(0, || print!("{:?}", current_block)); + self.log(0, || print!("// {:?}", current_block)); let current_mir = self.mir().clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { - self.log(1, || print!("{:?}", stmt)); + self.log(0, || print!("{:?}", stmt)); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.eval_assignment(lvalue, rvalue); try!(self.maybe_report(stmt.span, result)); } let terminator = block_data.terminator(); - self.log(1, || print!("{:?}", terminator.kind)); + self.log(0, || print!("{:?}", terminator.kind)); let result = self.eval_terminator(terminator); match try!(self.maybe_report(terminator.span, result)) { From bef57b291b55e81a1dc14c0e3926e254ad6876eb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 7 Apr 2016 02:02:30 -0600 Subject: [PATCH 0216/1096] Simplify intrinsic/c_abi call argument evaluation. --- src/interpreter.rs | 109 +++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 67 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 24e65b5b678ec..5ea2a117b8c58 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -371,25 +371,28 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(target) } - fn call_intrinsic(&mut self, name: &str, substs: &'tcx Substs<'tcx>, - args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize) - -> EvalResult - { + fn call_intrinsic( + &mut self, + name: &str, + substs: &'tcx Substs<'tcx>, + args: &[mir::Operand<'tcx>], + dest: Pointer, + dest_size: usize + ) -> EvalResult { + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = try!(args_res); + match name { "assume" => {} "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.type_size(elem_ty); - - let src_arg = try!(self.eval_operand(&args[0])); - let dest_arg = try!(self.eval_operand(&args[1])); - let count_arg = try!(self.eval_operand(&args[2])); - - let src = try!(self.memory.read_ptr(src_arg)); - let dest = try!(self.memory.read_ptr(dest_arg)); - let count = try!(self.memory.read_isize(count_arg)); - + let src = try!(self.memory.read_ptr(args[0])); + let dest = try!(self.memory.read_ptr(args[1])); + let count = try!(self.memory.read_isize(args[2])); try!(self.memory.copy(src, dest, count as usize * elem_size)); } @@ -403,29 +406,19 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "move_val_init" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - - let ptr_arg = try!(self.eval_operand(&args[0])); - let ptr = try!(self.memory.read_ptr(ptr_arg)); - - let val = try!(self.eval_operand(&args[1])); - try!(self.memory.copy(val, ptr, size)); + let ptr = try!(self.memory.read_ptr(args[0])); + try!(self.memory.copy(args[1], ptr, size)); } // FIXME(tsion): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - - let left_arg = try!(self.eval_operand(&args[0])); - let right_arg = try!(self.eval_operand(&args[1])); - - let left = try!(self.memory.read_int(left_arg, size)); - let right = try!(self.memory.read_int(right_arg, size)); - + let left = try!(self.memory.read_int(args[0], size)); + let right = try!(self.memory.read_int(args[1], size)); let (n, overflowed) = unsafe { ::std::intrinsics::add_with_overflow::(left, right) }; - try!(self.memory.write_int(dest, n, size)); try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); } @@ -434,17 +427,11 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - - let left_arg = try!(self.eval_operand(&args[0])); - let right_arg = try!(self.eval_operand(&args[1])); - - let left = try!(self.memory.read_int(left_arg, size)); - let right = try!(self.memory.read_int(right_arg, size)); - + let left = try!(self.memory.read_int(args[0], size)); + let right = try!(self.memory.read_int(args[1], size)); let (n, overflowed) = unsafe { ::std::intrinsics::mul_with_overflow::(left, right) }; - try!(self.memory.write_int(dest, n, size)); try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); } @@ -452,11 +439,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.type_size(pointee_ty) as isize; - - let ptr_arg = try!(self.eval_operand(&args[0])); - let offset_arg = try!(self.eval_operand(&args[1])); - - let offset = try!(self.memory.read_isize(offset_arg)); + let ptr_arg = args[0]; + let offset = try!(self.memory.read_isize(args[1])); match self.memory.read_ptr(ptr_arg) { Ok(ptr) => { @@ -476,13 +460,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - - let left_arg = try!(self.eval_operand(&args[0])); - let right_arg = try!(self.eval_operand(&args[1])); - - let left = try!(self.memory.read_int(left_arg, size)); - let right = try!(self.memory.read_int(right_arg, size)); - + let left = try!(self.memory.read_int(args[0], size)); + let right = try!(self.memory.read_int(args[1], size)); let n = left.wrapping_sub(right); try!(self.memory.write_int(dest, n, size)); } @@ -493,14 +472,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(dest, size, dest_size)); } - "transmute" => { - let src = try!(self.eval_operand(&args[0])); - try!(self.memory.copy(src, dest, dest_size)); - } - - "uninit" => { - try!(self.memory.mark_definedness(dest, dest_size, false)); - } + "transmute" => try!(self.memory.copy(args[0], dest, dest_size)), + "uninit" => try!(self.memory.mark_definedness(dest, dest_size, false)), name => panic!("can't handle intrinsic: {}", name), } @@ -511,9 +484,12 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(TerminatorTarget::Call) } - fn call_c_abi(&mut self, def_id: DefId, args: &[mir::Operand<'tcx>], dest: Pointer) - -> EvalResult - { + fn call_c_abi( + &mut self, + def_id: DefId, + args: &[mir::Operand<'tcx>], + dest: Pointer + ) -> EvalResult { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { @@ -521,22 +497,21 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { None => name.as_str(), }; + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = try!(args_res); + match &link_name[..] { "__rust_allocate" => { - let size_arg = try!(self.eval_operand(&args[0])); - let _align_arg = try!(self.eval_operand(&args[1])); - let size = try!(self.memory.read_usize(size_arg)); + let size = try!(self.memory.read_usize(args[0])); let ptr = self.memory.allocate(size as usize); try!(self.memory.write_ptr(dest, ptr)); } "__rust_reallocate" => { - let ptr_arg = try!(self.eval_operand(&args[0])); - let _old_size_arg = try!(self.eval_operand(&args[1])); - let size_arg = try!(self.eval_operand(&args[2])); - let _align_arg = try!(self.eval_operand(&args[3])); - let ptr = try!(self.memory.read_ptr(ptr_arg)); - let size = try!(self.memory.read_usize(size_arg)); + let ptr = try!(self.memory.read_ptr(args[0])); + let size = try!(self.memory.read_usize(args[2])); try!(self.memory.reallocate(ptr, size as usize)); try!(self.memory.write_ptr(dest, ptr)); } From 1f6583fe06ecb307004a6a7744c43a26bac85e8c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 7 Apr 2016 03:02:02 -0600 Subject: [PATCH 0217/1096] Implement drop/deallocation for Box. --- src/interpreter.rs | 32 ++++++++++++++++++++++++++++++-- src/memory.rs | 16 ++++++++++++++++ test/errors.rs | 15 ++++++++++++--- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5ea2a117b8c58..324a2008e3810 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -360,8 +360,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - Drop { target, .. } => { - // TODO: Handle destructors and dynamic drop. + Drop { ref value, target, .. } => { + let ptr = try!(self.eval_lvalue(value)).to_ptr(); + let ty = self.lvalue_ty(value); + try!(self.drop(ptr, ty)); TerminatorTarget::Block(target) } @@ -371,6 +373,28 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(target) } + fn drop(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> { + if !self.type_needs_drop(ty) { + self.log(1, || print!("no need to drop {:?}", ty)); + return Ok(()); + } + self.log(1, || print!("need to drop {:?}", ty)); + + match ty.sty { + ty::TyBox(contents_ty) => { + let contents_ptr = try!(self.memory.read_ptr(ptr)); + try!(self.drop(contents_ptr, contents_ty)); + self.log(1, || print!("deallocating box")); + try!(self.memory.deallocate(contents_ptr)); + } + + // TODO(tsion): Implement drop for other relevant types (e.g. aggregates). + _ => {} + } + + Ok(()) + } + fn call_intrinsic( &mut self, name: &str, @@ -847,6 +871,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { infer::normalize_associated_type(self.tcx, &substituted) } + fn type_needs_drop(&self, ty: ty::Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) + } + fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) } diff --git a/src/memory.rs b/src/memory.rs index b41cd7760b0c1..661f45672648b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -140,6 +140,22 @@ impl Memory { Ok(()) } + // TODO(tsion): See comment on `reallocate`. + pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> { + if ptr.offset != 0 { + // TODO(tsion): Report error about non-__rust_allocate'd pointer. + panic!() + } + + if self.alloc_map.remove(&ptr.alloc_id.0).is_none() { + // TODO(tsion): Report error about erroneous free. This is blocked on properly tracking + // already-dropped state since this if-statement is entered even in safe code without + // it. + } + + Ok(()) + } + //////////////////////////////////////////////////////////////////////////////// // Allocation accessors //////////////////////////////////////////////////////////////////////////////// diff --git a/test/errors.rs b/test/errors.rs index f6f5eae9d0fe6..a488d7acb433f 100644 --- a/test/errors.rs +++ b/test/errors.rs @@ -19,20 +19,29 @@ fn pointers_to_different_allocations_are_unorderable() -> bool { } #[miri_run] -fn invalid_bools_are_rejected() -> u8 { +fn invalid_bool() -> u8 { let b = unsafe { std::mem::transmute::(2) }; if b { 1 } else { 2 } } #[miri_run] -fn undefined_byte_reads_are_rejected() -> u8 { +fn undefined_byte_read() -> u8 { let v: Vec = Vec::with_capacity(10); let undef = unsafe { *v.get_unchecked(5) }; undef + 1 } #[miri_run] -fn out_of_bounds_reads_are_rejected() -> u8 { +fn out_of_bounds_read() -> u8 { let v: Vec = vec![1, 2]; unsafe { *v.get_unchecked(5) } } + +#[miri_run] +fn dangling_pointer_deref() -> i32 { + let p = { + let b = Box::new(42); + &*b as *const i32 + }; + unsafe { *p } +} From 6be14eab15242f503e56bf023237651bea43861d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 7 Apr 2016 03:07:57 -0600 Subject: [PATCH 0218/1096] Handle missing allocations in Memory::dump. --- src/memory.rs | 9 ++++++++- test/pointers.rs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 661f45672648b..105bcf68849cf 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -176,11 +176,18 @@ impl Memory { while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id.0); - let alloc = self.get(id).unwrap(); let prefix = format!("Alloc {:<5} ", format!("{}:", id.0)); print!("{}", prefix); let mut relocations = vec![]; + let alloc = match self.alloc_map.get(&id.0) { + Some(a) => a, + None => { + println!("(deallocated)"); + continue; + } + }; + for i in 0..alloc.bytes.len() { if let Some(&target_id) = alloc.relocations.get(&i) { if !allocs_seen.contains(&target_id.0) { diff --git a/test/pointers.rs b/test/pointers.rs index 494adf243acb1..9e7e217ec5c72 100644 --- a/test/pointers.rs +++ b/test/pointers.rs @@ -51,3 +51,9 @@ fn match_ref_mut() -> i8 { } t.0 } + +#[miri_run] +fn dangling_pointer() -> *const i32 { + let b = Box::new(42); + &*b as *const i32 +} From 910ad2a39177c4203b879acc12bb8dfb4995210b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 7 Apr 2016 05:56:07 -0600 Subject: [PATCH 0219/1096] Implement filling drop. --- src/interpreter.rs | 94 +++++++++++++++++++++++++++++++++------------- src/lib.rs | 9 ++++- src/memory.rs | 14 ++++++- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 324a2008e3810..850ddf8aa18b0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -11,9 +11,9 @@ use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; -use std::iter; use std::ops::Deref; use std::rc::Rc; +use std::{iter, mem}; use syntax::ast; use syntax::attr; use syntax::codemap::{self, DUMMY_SP}; @@ -318,25 +318,27 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let mut arg_srcs = Vec::new(); for arg in args { - let (src, repr) = try!(self.eval_operand_and_repr(arg)); - arg_srcs.push((src, repr.size())); + let src = try!(self.eval_operand(arg)); + let src_ty = self.operand_ty(arg); + arg_srcs.push((src, src_ty)); } if fn_ty.abi == Abi::RustCall && !args.is_empty() { arg_srcs.pop(); let last_arg = args.last().unwrap(); - let (last_src, last_repr) = - try!(self.eval_operand_and_repr(last_arg)); - match *last_repr { - Repr::Aggregate { discr_size: 0, ref variants, .. } => { + let last = try!(self.eval_operand(last_arg)); + let last_ty = self.operand_ty(last_arg); + let last_repr = self.type_repr(last_ty); + match (&last_ty.sty, last_repr) { + (&ty::TyTuple(ref fields), + &Repr::Aggregate { discr_size: 0, ref variants, .. }) => { assert_eq!(variants.len(), 1); - for field in &variants[0] { - let src = last_src.offset(field.offset as isize); - arg_srcs.push((src, field.size)); + for (repr, ty) in variants[0].iter().zip(fields) { + let src = last.offset(repr.offset as isize); + arg_srcs.push((src, ty)); } } - - _ => panic!("expected tuple as last argument in function with 'rust-call' ABI"), + ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } } @@ -344,9 +346,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { self.name_stack.push((def_id, substs, terminator.span)); self.push_stack_frame(mir, resolved_substs, return_ptr); - for (i, (src, size)) in arg_srcs.into_iter().enumerate() { + for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; - try!(self.memory.copy(src, dest, size)); + try!(self.move_(src, dest, src_ty)); } TerminatorTarget::Call @@ -380,18 +382,37 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } self.log(1, || print!("need to drop {:?}", ty)); + // TODO(tsion): Call user-defined Drop::drop impls. + match ty.sty { ty::TyBox(contents_ty) => { - let contents_ptr = try!(self.memory.read_ptr(ptr)); - try!(self.drop(contents_ptr, contents_ty)); - self.log(1, || print!("deallocating box")); - try!(self.memory.deallocate(contents_ptr)); + match self.memory.read_ptr(ptr) { + Ok(contents_ptr) => { + try!(self.drop(contents_ptr, contents_ty)); + self.log(1, || print!("deallocating box")); + try!(self.memory.deallocate(contents_ptr)); + } + Err(EvalError::ReadBytesAsPointer) => { + let possible_drop_fill = try!(self.memory.read_usize(ptr)); + if possible_drop_fill == mem::POST_DROP_U64 { + return Ok(()); + } else { + return Err(EvalError::ReadBytesAsPointer); + } + } + Err(e) => return Err(e), + } } // TODO(tsion): Implement drop for other relevant types (e.g. aggregates). _ => {} } + // Filling drop. + // FIXME(tsion): Trait objects (with no static size) probably get filled, too. + let size = self.type_size(ty); + try!(self.memory.drop_fill(ptr, size)); + Ok(()) } @@ -420,8 +441,13 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.copy(src, dest, count as usize * elem_size)); } - // TODO(tsion): Mark as dropped? - "forget" => {} + "forget" => { + let arg_ty = *substs.types.get(subst::FnSpace, 0); + let arg_size = self.type_size(arg_ty); + try!(self.memory.drop_fill(args[0], arg_size)); + } + + "init" => try!(self.memory.write_repeat(dest, 0, dest_size)), "min_align_of" => { try!(self.memory.write_int(dest, 1, dest_size)); @@ -429,9 +455,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { "move_val_init" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); let ptr = try!(self.memory.read_ptr(args[0])); - try!(self.memory.copy(args[1], ptr, size)); + try!(self.move_(args[1], ptr, ty)); } // FIXME(tsion): Handle different integer types correctly. @@ -496,7 +521,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.write_uint(dest, size, dest_size)); } - "transmute" => try!(self.memory.copy(args[0], dest, dest_size)), + "transmute" => { + let ty = *substs.types.get(subst::FnSpace, 0); + try!(self.move_(args[0], dest, ty)); + } "uninit" => try!(self.memory.mark_definedness(dest, dest_size, false)), name => panic!("can't handle intrinsic: {}", name), @@ -565,8 +593,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let after_discr = dest.offset(discr_size as isize); for (field, operand) in variants[variant].iter().zip(operands) { let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); let field_dest = after_discr.offset(field.offset as isize); - try!(self.memory.copy(src, field_dest, field.size)); + try!(self.move_(src, field_dest, src_ty)); } } _ => panic!("expected Repr::Aggregate target"), @@ -578,13 +607,14 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { -> EvalResult<()> { let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); + let dest_ty = self.lvalue_ty(lvalue); let dest_repr = self.lvalue_repr(lvalue); use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { let src = try!(self.eval_operand(operand)); - try!(self.memory.copy(src, dest, dest_repr.size())); + try!(self.move_(src, dest, dest_ty)); } BinaryOp(bin_op, ref left, ref right) => { @@ -622,8 +652,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { assert_eq!(length, operands.len()); for (i, operand) in operands.iter().enumerate() { let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.memory.copy(src, elem_dest, elem_size)); + try!(self.move_(src, elem_dest, src_ty)); } } else { panic!("expected Repr::Array target"); @@ -683,7 +714,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { use rustc::mir::repr::CastKind::*; match kind { Unsize => { - try!(self.memory.copy(src, dest, 8)); + try!(self.move_(src, dest, src_ty)); let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -875,6 +906,15 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) } + fn move_(&mut self, src: Pointer, dest: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> { + let size = self.type_size(ty); + try!(self.memory.copy(src, dest, size)); + if self.type_needs_drop(ty) { + try!(self.memory.drop_fill(src, size)); + } + Ok(()) + } + fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) } diff --git a/src/lib.rs b/src/lib.rs index 46b23b589971a..8ddafb4991115 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,11 @@ -#![feature(btree_range, collections, collections_bound, core_intrinsics, rustc_private)] +#![feature( + btree_range, + collections, + collections_bound, + core_intrinsics, + filling_drop, + rustc_private, +)] // From rustc. extern crate arena; diff --git a/src/memory.rs b/src/memory.rs index 105bcf68849cf..2a2956bbb4c46 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -279,7 +279,19 @@ impl Memory { } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { - self.get_bytes_mut(ptr, src.len()).map(|dest| dest.clone_from_slice(src)) + let bytes = try!(self.get_bytes_mut(ptr, src.len())); + bytes.clone_from_slice(src); + Ok(()) + } + + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<()> { + let bytes = try!(self.get_bytes_mut(ptr, count)); + for b in bytes { *b = val; } + Ok(()) + } + + pub fn drop_fill(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + self.write_repeat(ptr, mem::POST_DROP_U8, size) } pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { From 65a88a60f145b2ab10087956bb65404e2ad7e069 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 8 Apr 2016 13:25:36 -0600 Subject: [PATCH 0220/1096] Move slides into new tex directory. --- .gitignore | 2 +- .../final-presentation}/latexmkrc | 0 .../final-presentation}/rust-logo-512x512.png | Bin .../final-presentation}/slides.tex | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {final-presentation => tex/final-presentation}/latexmkrc (100%) rename {final-presentation => tex/final-presentation}/rust-logo-512x512.png (100%) rename {final-presentation => tex/final-presentation}/slides.tex (100%) diff --git a/.gitignore b/.gitignore index bbd046fa2735f..d9940aa58b5ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target /doc -/final-presentation/out +tex/*/out *.dot *.mir diff --git a/final-presentation/latexmkrc b/tex/final-presentation/latexmkrc similarity index 100% rename from final-presentation/latexmkrc rename to tex/final-presentation/latexmkrc diff --git a/final-presentation/rust-logo-512x512.png b/tex/final-presentation/rust-logo-512x512.png similarity index 100% rename from final-presentation/rust-logo-512x512.png rename to tex/final-presentation/rust-logo-512x512.png diff --git a/final-presentation/slides.tex b/tex/final-presentation/slides.tex similarity index 100% rename from final-presentation/slides.tex rename to tex/final-presentation/slides.tex From e68cf00d09a16f99cbe656e72e99b8f4743527e4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 8 Apr 2016 14:37:17 -0600 Subject: [PATCH 0221/1096] Add basic final paper LaTeX with abstract. --- tex/paper/latexmkrc | 12 ++++++++++ tex/paper/miri.tex | 55 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tex/paper/latexmkrc create mode 100644 tex/paper/miri.tex diff --git a/tex/paper/latexmkrc b/tex/paper/latexmkrc new file mode 100644 index 0000000000000..23aa1a481b3eb --- /dev/null +++ b/tex/paper/latexmkrc @@ -0,0 +1,12 @@ +# vim: ft=perl + +$pdf_mode = 1; +$pdflatex = 'lualatex --shell-escape %O %S'; +$out_dir = 'out'; + +# This improves latexmk's detection of source files and generated files. +$recorder = 1; + +# Ignore always-regenerated *.pyg files from the minted package when considering +# whether to run pdflatex again. +$hash_calc_ignore_pattern{'pyg'} = '.*'; diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex new file mode 100644 index 0000000000000..1419e347f969c --- /dev/null +++ b/tex/paper/miri.tex @@ -0,0 +1,55 @@ +% vim: tw=100 + +\documentclass[twocolumn]{article} +\usepackage{blindtext} +\usepackage{fontspec} +\usepackage[colorlinks, urlcolor={blue!80!black}]{hyperref} +\usepackage{relsize} +\usepackage{xcolor} + +\begin{document} + +\title{Miri: \\ \smaller{An interpreter for Rust's mid-level intermediate representation}} +% \subtitle{test} +\author{Scott Olson\footnote{\href{mailto:scott@solson.me}{scott@solson.me}} \\ + \smaller{Supervised by Christopher Dutchyn}} +\date{April 8th, 2016} +\maketitle + +\section{Abstract} + +The increasing need for safe low-level code in contexts like operating systems and browsers is +driving the development of Rust\footnote{\url{https://www.rust-lang.org}}, a programming language +backed by Mozilla promising blazing speed without the segfaults. To make programming more +convenient, it's often desirable to be able to generate code or perform some computation at +compile-time. The former is mostly covered by Rust's existing macro feature, but the latter is +currently restricted to a limited form of constant evaluation capable of little beyond simple math. + +When the existing constant evaluator was built, it would have been difficult to make it more +powerful than it is. However, a new intermediate representation was recently +added\footnote{\href{https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md}{The MIR RFC}} +to the Rust compiler between the abstract syntax tree and the back-end LLVM IR, called mid-level +intermediate representation, or MIR for short. As it turns out, writing an interpreter for MIR is a +surprisingly effective approach for supporting a large proportion of Rust's features in compile-time +execution. + +\section{Motivation} + +\blindtext + +\section{First implementation} + +% TODO(tsion): Find a place for this text. +Making Miri work was primarily an implementation problem. Writing an interpreter which models values +of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some +unconventional techniques compared to many interpreters. Miri's execution remains safe even while +simulating execution of unsafe code, which allows it to detect when unsafe code does something +invalid. + +\blindtext[2] + +\section{Data layout} + +\blindtext + +\end{document} From 438eabbba4c4540b4c28aa20c863f05e90d0b18c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 8 Apr 2016 19:54:03 -0600 Subject: [PATCH 0222/1096] Add background and intro to first implementation. --- tex/paper/miri.tex | 102 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex index 1419e347f969c..20a0dd4c1d21b 100644 --- a/tex/paper/miri.tex +++ b/tex/paper/miri.tex @@ -2,11 +2,15 @@ \documentclass[twocolumn]{article} \usepackage{blindtext} +\usepackage[hypcap]{caption} \usepackage{fontspec} \usepackage[colorlinks, urlcolor={blue!80!black}]{hyperref} +\usepackage[outputdir=out]{minted} \usepackage{relsize} \usepackage{xcolor} +\newcommand{\rust}[1]{\mintinline{rust}{#1}} + \begin{document} \title{Miri: \\ \smaller{An interpreter for Rust's mid-level intermediate representation}} @@ -33,12 +37,85 @@ \section{Abstract} surprisingly effective approach for supporting a large proportion of Rust's features in compile-time execution. -\section{Motivation} +\section{Background} -\blindtext +The Rust compiler (\texttt{rustc}) generates an instance of \rust{Mir} [\autoref{fig:mir}] for each +function. Each \rust{Mir} structure represents a control-flow graph for a given function, and +contains a list of ``basic blocks'' which in turn contain a list of statements followed by a single +terminator. Each statement is of the form \rust{lvalue = rvalue}. An \rust{Lvalue} is used for +referencing variables and calculating addresses such as when dereferencing pointers, accessing +fields, or indexing arrays. An \rust{Rvalue} represents the core set of operations possible in MIR, +including reading a value from an lvalue, performing math operations, creating new pointers, +structs, and arrays, and so on. Finally, a terminator decides where control will flow next, +optionally based on a boolean or some other condition. + +\begin{figure}[ht] + \begin{minted}[autogobble]{rust} + struct Mir { + basic_blocks: Vec, + // ... + } + struct BasicBlockData { + statements: Vec, + terminator: Terminator, + // ... + } + struct Statement { + lvalue: Lvalue, + rvalue: Rvalue + } + enum Terminator { + Goto { target: BasicBlock }, + If { + cond: Operand, + targets: [BasicBlock; 2] + }, + // ... + } + \end{minted} + \caption{MIR (simplified)} + \label{fig:mir} +\end{figure} \section{First implementation} +\subsection{Basic operation} + +Initially, I wrote a simple version of Miri that was quite capable despite its flaws. The structure +of the interpreter essentially mirrors the structure of MIR itself. Miri starts executing a function +by iterating the list of statements in the starting basic block, matching over the lvalue to produce +a pointer and matching over the rvalue to decide what to write into that pointer. Evaluating the +rvalue may generally involve reads (such as for the left and right hand side of a binary operation) +or construction of new values. Upon reaching the terminator, a similar matching is done and a new +basic block is selected. Finally, Miri returns to the top of the main interpreter loop and this +entire process repeats, reading statements from the new block. + +\subsection{Function calls} + +To handle function call terminators\footnote{Calls occur only as terminators, never as rvalues.}, +Miri is required to store some information in a virtual call stack so that it may pick up where it +left off when the callee returns. Each stack frame stores a reference to the \rust{Mir} for the +function being executed, its local variables, its return value location\footnote{Return value +pointers are passed in by callers.}, and the basic block where execution should resume. To +facilitate returning, there is a \rust{Return} terminator which causes Miri to pop a stack frame and +resume the previous function. The entire execution of a program completes when the first function +that Miri called returns, rendering the call stack empty. + +It should be noted that Miri does not itself recurse when a function is called; it merely pushes a +virtual stack frame and jumps to the top of the interpreter loop. This property implies that Miri +can interpret deeply recursive programs without crashing. Alternately, Miri could set a stack +depth limit and return an error when a program exceeds it. + +\subsection{Flaws} + +% TODO(tsion): Incorporate this text from the slides. +% At first I wrote a naive version with a number of downsides: +% * I represented values in a traditional dynamic language format, +% where every value was the same size. +% * I didn’t work well for aggregates (structs, enums, arrays, etc.). +% *I made unsafe programming tricks that make assumptions +% about low-level value layout essentially impossible + % TODO(tsion): Find a place for this text. Making Miri work was primarily an implementation problem. Writing an interpreter which models values of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some @@ -46,10 +123,29 @@ \section{First implementation} simulating execution of unsafe code, which allows it to detect when unsafe code does something invalid. -\blindtext[2] +\blindtext \section{Data layout} \blindtext +\section{Future work} + +Other possible uses for Miri include: + +\begin{itemize} + \item A graphical or text-mode debugger that steps through MIR execution one statement at a time, + for figuring out why some compile-time execution is raising an error or simply learning how Rust + works at a low level. + \item An read-eval-print-loop (REPL) for Rust may be easier to implement on top of Miri than the + usual LLVM back-end. + \item An extended version of Miri could be developed apart from the purpose of compile-time + execution that is able to run foreign functions from C/C++ and generally have full access to the + operating system. Such a version of Miri could be used to more quickly prototype changes to the + Rust language that would otherwise require changes to the LLVM back-end. + \item Miri might be useful for unit-testing the compiler by comparing the results of Miri's + execution against the results of LLVM-compiled machine code's execution. This would help to + guarantee that compile-time execution works the same as runtime execution. +\end{itemize} + \end{document} From a69ad6703f769b47c07edb8a87d4a2ce6ade1d31 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 19:31:53 -0600 Subject: [PATCH 0223/1096] Store AllocIds directly in allocation map. --- src/memory.rs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 2a2956bbb4c46..21ab8e36c563f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ use byteorder::{ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{iter, mem, ptr}; +use std::{fmt, iter, mem, ptr}; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -58,9 +58,15 @@ impl Repr { // Allocations and pointers //////////////////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct AllocId(u64); +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Debug)] pub struct Allocation { pub bytes: Vec, @@ -85,8 +91,8 @@ impl Pointer { //////////////////////////////////////////////////////////////////////////////// pub struct Memory { - alloc_map: HashMap, - next_id: u64, + alloc_map: HashMap, + next_id: AllocId, pub pointer_size: usize, } @@ -94,7 +100,7 @@ impl Memory { pub fn new() -> Self { Memory { alloc_map: HashMap::new(), - next_id: 0, + next_id: AllocId(0), // TODO(tsion): Should this be host's or target's usize? pointer_size: mem::size_of::(), @@ -102,14 +108,14 @@ impl Memory { } pub fn allocate(&mut self, size: usize) -> Pointer { - let id = AllocId(self.next_id); let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), }; - self.alloc_map.insert(self.next_id, alloc); - self.next_id += 1; + let id = self.next_id; + self.next_id.0 += 1; + self.alloc_map.insert(id, alloc); Pointer { alloc_id: id, offset: 0, @@ -147,7 +153,7 @@ impl Memory { panic!() } - if self.alloc_map.remove(&ptr.alloc_id.0).is_none() { + if self.alloc_map.remove(&ptr.alloc_id).is_none() { // TODO(tsion): Report error about erroneous free. This is blocked on properly tracking // already-dropped state since this if-statement is entered even in safe code without // it. @@ -161,11 +167,11 @@ impl Memory { //////////////////////////////////////////////////////////////////////////////// pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { - self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref) + self.alloc_map.get(&id).ok_or(EvalError::DanglingPointerDeref) } pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { - self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref) + self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref) } /// Print an allocation and all allocations it points to, recursively. @@ -175,12 +181,12 @@ impl Memory { allocs_to_print.push_back(id); while let Some(id) = allocs_to_print.pop_front() { - allocs_seen.insert(id.0); - let prefix = format!("Alloc {:<5} ", format!("{}:", id.0)); + allocs_seen.insert(id); + let prefix = format!("Alloc {:<5} ", format!("{}:", id)); print!("{}", prefix); let mut relocations = vec![]; - let alloc = match self.alloc_map.get(&id.0) { + let alloc = match self.alloc_map.get(&id) { Some(a) => a, None => { println!("(deallocated)"); @@ -190,12 +196,12 @@ impl Memory { for i in 0..alloc.bytes.len() { if let Some(&target_id) = alloc.relocations.get(&i) { - if !allocs_seen.contains(&target_id.0) { + if !allocs_seen.contains(&target_id) { allocs_to_print.push_back(target_id); } - relocations.push((i, target_id.0)); + relocations.push((i, target_id)); } - if alloc.undef_mask.is_range_defined(i, i+1) { + if alloc.undef_mask.is_range_defined(i, i + 1) { print!("{:02x} ", alloc.bytes[i]); } else { print!("__ "); From 998fcb82c583b76fd5a3c65a35abc1c71981e838 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 19:36:55 -0600 Subject: [PATCH 0224/1096] Reword and reformat various parts. --- tex/paper/miri.tex | 83 +++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex index 20a0dd4c1d21b..02c86a2a676ac 100644 --- a/tex/paper/miri.tex +++ b/tex/paper/miri.tex @@ -9,6 +9,12 @@ \usepackage{relsize} \usepackage{xcolor} +\setmonofont{Source Code Pro}[ + BoldFont={* Medium}, + BoldItalicFont={* Medium Italic}, + Scale=MatchLowercase, +] + \newcommand{\rust}[1]{\mintinline{rust}{#1}} \begin{document} @@ -20,6 +26,8 @@ \date{April 8th, 2016} \maketitle +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \section{Abstract} The increasing need for safe low-level code in contexts like operating systems and browsers is @@ -37,58 +45,65 @@ \section{Abstract} surprisingly effective approach for supporting a large proportion of Rust's features in compile-time execution. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \section{Background} -The Rust compiler (\texttt{rustc}) generates an instance of \rust{Mir} [\autoref{fig:mir}] for each -function. Each \rust{Mir} structure represents a control-flow graph for a given function, and -contains a list of ``basic blocks'' which in turn contain a list of statements followed by a single -terminator. Each statement is of the form \rust{lvalue = rvalue}. An \rust{Lvalue} is used for -referencing variables and calculating addresses such as when dereferencing pointers, accessing -fields, or indexing arrays. An \rust{Rvalue} represents the core set of operations possible in MIR, -including reading a value from an lvalue, performing math operations, creating new pointers, -structs, and arrays, and so on. Finally, a terminator decides where control will flow next, -optionally based on a boolean or some other condition. +The Rust compiler generates an instance of \rust{Mir} for each function [\autoref{fig:mir}]. Each +\rust{Mir} structure represents a control-flow graph for a given function, and contains a list of +``basic blocks'' which in turn contain a list of statements followed by a single terminator. Each +statement is of the form \rust{lvalue = rvalue}. An \rust{Lvalue} is used for referencing variables +and calculating addresses such as when dereferencing pointers, accessing fields, or indexing arrays. +An \rust{Rvalue} represents the core set of operations possible in MIR, including reading a value +from an lvalue, performing math operations, creating new pointers, structs, and arrays, and so on. +Finally, a terminator decides where control will flow next, optionally based on a boolean or some +other condition. \begin{figure}[ht] \begin{minted}[autogobble]{rust} struct Mir { - basic_blocks: Vec, - // ... + basic_blocks: Vec, + // ... } + struct BasicBlockData { - statements: Vec, - terminator: Terminator, - // ... + statements: Vec, + terminator: Terminator, + // ... } + struct Statement { - lvalue: Lvalue, - rvalue: Rvalue + lvalue: Lvalue, + rvalue: Rvalue } + enum Terminator { - Goto { target: BasicBlock }, - If { - cond: Operand, - targets: [BasicBlock; 2] - }, - // ... + Goto { target: BasicBlock }, + If { + cond: Operand, + targets: [BasicBlock; 2] + }, + // ... } \end{minted} \caption{MIR (simplified)} \label{fig:mir} \end{figure} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \section{First implementation} \subsection{Basic operation} -Initially, I wrote a simple version of Miri that was quite capable despite its flaws. The structure -of the interpreter essentially mirrors the structure of MIR itself. Miri starts executing a function -by iterating the list of statements in the starting basic block, matching over the lvalue to produce -a pointer and matching over the rvalue to decide what to write into that pointer. Evaluating the -rvalue may generally involve reads (such as for the left and right hand side of a binary operation) -or construction of new values. Upon reaching the terminator, a similar matching is done and a new -basic block is selected. Finally, Miri returns to the top of the main interpreter loop and this -entire process repeats, reading statements from the new block. +Initially, I wrote a simple version of Miri\footnote{\url{https://github.com/tsion/miri}} that was +quite capable despite its flaws. The structure of the interpreter closely mirrors the structure of +MIR itself. It starts executing a function by iterating the statement list in the starting basic +block, matching over the lvalue to produce a pointer and matching over the rvalue to decide what to +write into that pointer. Evaluating the rvalue may involve reads (such as for the two sides of a +binary operation) or construction of new values. Upon reaching the terminator, a similar matching is +done and a new basic block is selected. Finally, Miri returns to the top of the main interpreter +loop and this entire process repeats, reading statements from the new block. \subsection{Function calls} @@ -102,9 +117,9 @@ \subsection{Function calls} that Miri called returns, rendering the call stack empty. It should be noted that Miri does not itself recurse when a function is called; it merely pushes a -virtual stack frame and jumps to the top of the interpreter loop. This property implies that Miri -can interpret deeply recursive programs without crashing. Alternately, Miri could set a stack -depth limit and return an error when a program exceeds it. +virtual stack frame and jumps to the top of the interpreter loop. Consequently, Miri can interpret +deeply recursive programs without crashing. It could also set a stack depth limit and report an +error when a program exceeds it. \subsection{Flaws} @@ -127,7 +142,7 @@ \subsection{Flaws} \section{Data layout} -\blindtext +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Future work} From 3250837f4be3046b5634acc0405d30c2cf23bc63 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 22:22:06 -0600 Subject: [PATCH 0225/1096] report: Add "Flaws" and "Current implementation". --- tex/paper/miri.tex | 130 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 12 deletions(-) diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex index 02c86a2a676ac..3ea16e0ee3cc0 100644 --- a/tex/paper/miri.tex +++ b/tex/paper/miri.tex @@ -123,20 +123,126 @@ \subsection{Function calls} \subsection{Flaws} -% TODO(tsion): Incorporate this text from the slides. -% At first I wrote a naive version with a number of downsides: -% * I represented values in a traditional dynamic language format, -% where every value was the same size. -% * I didn’t work well for aggregates (structs, enums, arrays, etc.). -% *I made unsafe programming tricks that make assumptions -% about low-level value layout essentially impossible +This version of Miri was surprisingly easy to write and already supported quite a bit of the Rust +language, including booleans, integers, if-conditions, while-loops, structs, enums, arrays, tuples, +pointers, and function calls, all in about 400 lines of Rust code. However, it had a particularly +naive value representation with a number of downsides. It resembled the data layout of a dynamic +language like Ruby or Python, where every value has the same size\footnote{A Rust \rust{enum} is a +discriminated union with a tag and data the size of the largest variant, regardless of which variant +it contains.} in the interpreter: + +\begin{minted}[autogobble]{rust} + enum Value { + Uninitialized, + Bool(bool), + Int(i64), + Pointer(Pointer), // index into stack + Adt { variant: usize, data_ptr: Pointer }, + // ... + } +\end{minted} + +This representation did not work well for \rust{Adt}s\footnote{Algebraic data types: structs, enums, +arrays, and tuples.} and required strange hacks to support them. Their contained values were +allocated elsewhere on the stack and pointed to by the \rust{Adt} value. When it came to copying +\rust{Adt} values from place to place, this made it more complicated. + +Moreover, while the \rust{Adt} issues could be worked around, this value representation made common +\rust{unsafe} programming tricks (which make assumptions about the low-level value layout) +fundamentally impossible. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Current implementation} + +Roughly halfway through my time working on Miri, Rust compiler team member Eduard +Burtescu\footnote{\href{https://www.rust-lang.org/team.html\#Compiler}{The Rust compiler team}} made +a post on Rust's internal +forums\footnote{\href{https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31}{Burtescu's +``Rust Abstract Machine'' forum post}} about a ``Rust Abstract Machine'' specification which could +be used to implement more powerful compile-time function execution, similar to what is supported by +C++14's \mintinline{cpp}{constexpr} feature. After clarifying some of the details of the abstract +machine's data layout with Burtescu via IRC, I started implementing it in Miri. + +\subsection{Raw value representation} + +The main difference in the new value representation was to represent values by ``abstract +allocations'' containing arrays of raw bytes with different sizes depending on the types of the +values. This closely mimics how Rust values are represented when compiled for traditional machines. +In addition to the raw bytes, allocations carry information about pointers and undefined bytes. + +\begin{minted}[autogobble]{rust} + struct Memory { + map: HashMap, + next_id: AllocId, + } + + struct Allocation { + bytes: Vec, + relocations: BTreeMap, + undef_mask: UndefMask, + } +\end{minted} + +\subsubsection{Relocations} + +The abstract machine represents pointers through ``relocations'', which are analogous to relocations +in linkers\footnote{\href{https://en.wikipedia.org/wiki/Relocation_(computing)}{Relocation +(computing) - Wikipedia}}. Instead of storing a global memory address in the raw byte representation +like a traditional machine, we store an offset from the start of the target allocation and add an +entry to the relocation table. The entry maps the index of the start of the offset bytes to the +\rust{AllocId} of the target allocation. + +\begin{figure}[ht] + \begin{minted}[autogobble]{rust} + let a: [i16; 3] = [2, 4, 6]; + let b = &a[1]; + // A: 02 00 04 00 06 00 (6 bytes) + // B: 02 00 00 00 (4 bytes) + // └───(A)───┘ + \end{minted} + \caption{Example relocation on 32-bit little-endian} + \label{fig:reloc} +\end{figure} + +In effect, the abstract machine treats each allocation as a separate address space and represents +pointers as \rust{(address_space, offset)} pairs. This makes it easy to detect when pointer accesses +go out of bounds. + +See \autoref{fig:reloc} for an example of a relocation. Variable \rust{b} points to the second +16-bit integer in \rust{a}, so it contains a relocation with offset 2 and target allocation +\rust{A}. + +\subsubsection{Undefined byte mask} + +The final piece of an abstract allocation is the undefined byte mask. Logically, we store a boolean +for the definedness of every byte in the allocation, but there are multiple ways to make the storage +more compact. I tried two implementations: one based on the endpoints of alternating ranges of +defined and undefined bytes and the other based on a simple bitmask. The former is more compact but +I found it surprisingly difficult to update cleanly. I currently use the bitmask system, which is +comparatively trivial. + +See \autoref{fig:undef} for an example undefined byte, represented by underscores. Note that there +would still be a value for the second byte in the byte array, but we don't care what it is. The +bitmask would be $10_2$ i.e. \rust{[true, false]}. + +\begin{figure}[hb] + \begin{minted}[autogobble]{rust} + let a: [u8; 2] = unsafe { + [1, std::mem::uninitialized()] + }; + // A: 01 __ (2 bytes) + \end{minted} + \caption{Example undefined byte} + \label{fig:undef} +\end{figure} % TODO(tsion): Find a place for this text. -Making Miri work was primarily an implementation problem. Writing an interpreter which models values -of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some -unconventional techniques compared to many interpreters. Miri's execution remains safe even while -simulating execution of unsafe code, which allows it to detect when unsafe code does something -invalid. +% Making Miri work was primarily an implementation problem. Writing an interpreter which models values +% of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some +% unconventional techniques compared to many interpreters. Miri's execution remains safe even while +% simulating execution of unsafe code, which allows it to detect when unsafe code does something +% invalid. \blindtext From b072298d0cb6572bb0c0465513144771ad396e6b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 22:23:00 -0600 Subject: [PATCH 0226/1096] report: Add "Vec" example. --- tex/paper/miri.tex | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex index 3ea16e0ee3cc0..d54d28f8b3865 100644 --- a/tex/paper/miri.tex +++ b/tex/paper/miri.tex @@ -244,9 +244,37 @@ \subsubsection{Undefined byte mask} % simulating execution of unsafe code, which allows it to detect when unsafe code does something % invalid. -\blindtext +\begin{figure}[t] + \begin{minted}[autogobble]{rust} + struct Vec { + data: *mut T, // 4 byte pointer + capacity: usize, // 4 byte integer + length: usize, // 4 byte integer + } -\section{Data layout} + let mut v: Vec = + Vec::with_capacity(2); + // A: 00 00 00 00 02 00 00 00 00 00 00 00 + // └───(B)───┘ + // B: __ __ + + v.push(1); + // A: 00 00 00 00 02 00 00 00 01 00 00 00 + // └───(B)───┘ + // B: 01 __ + + v.push(2); + // A: 00 00 00 00 02 00 00 00 02 00 00 00 + // └───(B)───┘ + // B: 01 02 + + v.push(3); + // A: 00 00 00 00 04 00 00 00 03 00 00 00 + // └───(B)───┘ + // B: 01 02 03 __ + \end{minted} + \caption{\rust{Vec} example on 32-bit little-endian} +\end{figure} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 4bb4251269b46ed82cab54cf235c7c726712b09e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 22:23:15 -0600 Subject: [PATCH 0227/1096] report: Add stub "Thanks" section. --- tex/paper/miri.tex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tex/paper/miri.tex b/tex/paper/miri.tex index d54d28f8b3865..99d7733725c9d 100644 --- a/tex/paper/miri.tex +++ b/tex/paper/miri.tex @@ -297,4 +297,10 @@ \section{Future work} guarantee that compile-time execution works the same as runtime execution. \end{itemize} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Thanks} + +Eduard Burtescu, Niko Matsakis, and Christopher Dutchyn. + \end{document} From 9d566a04971ba63138a2eae8a4fee9280e0c56ea Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 22:23:50 -0600 Subject: [PATCH 0228/1096] Rename paper to report. --- tex/{paper => report}/latexmkrc | 0 tex/{paper/miri.tex => report/miri-report.tex} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tex/{paper => report}/latexmkrc (100%) rename tex/{paper/miri.tex => report/miri-report.tex} (100%) diff --git a/tex/paper/latexmkrc b/tex/report/latexmkrc similarity index 100% rename from tex/paper/latexmkrc rename to tex/report/latexmkrc diff --git a/tex/paper/miri.tex b/tex/report/miri-report.tex similarity index 100% rename from tex/paper/miri.tex rename to tex/report/miri-report.tex From cb6a1e98bdf1c2197671c2647b2fb345c402d100 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 9 Apr 2016 23:00:31 -0600 Subject: [PATCH 0229/1096] report: Minor fixes. --- tex/report/miri-report.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 99d7733725c9d..e588291f4e8e8 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -137,7 +137,7 @@ \subsection{Flaws} Bool(bool), Int(i64), Pointer(Pointer), // index into stack - Adt { variant: usize, data_ptr: Pointer }, + Adt { variant: usize, data: Pointer }, // ... } \end{minted} @@ -224,7 +224,7 @@ \subsubsection{Undefined byte mask} See \autoref{fig:undef} for an example undefined byte, represented by underscores. Note that there would still be a value for the second byte in the byte array, but we don't care what it is. The -bitmask would be $10_2$ i.e. \rust{[true, false]}. +bitmask would be $10_2$, i.e. \rust{[true, false]}. \begin{figure}[hb] \begin{minted}[autogobble]{rust} @@ -286,7 +286,7 @@ \section{Future work} \item A graphical or text-mode debugger that steps through MIR execution one statement at a time, for figuring out why some compile-time execution is raising an error or simply learning how Rust works at a low level. - \item An read-eval-print-loop (REPL) for Rust may be easier to implement on top of Miri than the + \item A read-eval-print-loop (REPL) for Rust may be easier to implement on top of Miri than the usual LLVM back-end. \item An extended version of Miri could be developed apart from the purpose of compile-time execution that is able to run foreign functions from C/C++ and generally have full access to the From f3d0e1826478d6da609a78e2a4f05fa5b8b2eecc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 12 Apr 2016 18:42:28 -0600 Subject: [PATCH 0230/1096] report: Fill in most of the language support section, plus data layout and determinism. --- tex/report/miri-report.tex | 218 ++++++++++++++++++++++++++++++++++--- 1 file changed, 201 insertions(+), 17 deletions(-) diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index e588291f4e8e8..9655dcdaddb07 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -20,10 +20,9 @@ \begin{document} \title{Miri: \\ \smaller{An interpreter for Rust's mid-level intermediate representation}} -% \subtitle{test} \author{Scott Olson\footnote{\href{mailto:scott@solson.me}{scott@solson.me}} \\ \smaller{Supervised by Christopher Dutchyn}} -\date{April 8th, 2016} +\date{April 12th, 2016} \maketitle %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -155,14 +154,15 @@ \subsection{Flaws} \section{Current implementation} -Roughly halfway through my time working on Miri, Rust compiler team member Eduard -Burtescu\footnote{\href{https://www.rust-lang.org/team.html\#Compiler}{The Rust compiler team}} made -a post on Rust's internal -forums\footnote{\href{https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31}{Burtescu's -``Rust Abstract Machine'' forum post}} about a ``Rust Abstract Machine'' specification which could -be used to implement more powerful compile-time function execution, similar to what is supported by -C++14's \mintinline{cpp}{constexpr} feature. After clarifying some of the details of the abstract -machine's data layout with Burtescu via IRC, I started implementing it in Miri. +Roughly halfway through my time working on Miri, Eduard +Burtescu\footnote{\href{https://github.com/eddyb}{Eduard Burtescu on GitHub}} from the Rust compiler +team\footnote{\href{https://www.rust-lang.org/team.html\#Compiler}{The Rust compiler team}} made a +post on Rust's internal forums about a ``Rust Abstract Machine'' +specification\footnote{\href{https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31}{Burtescu's +``Rust Abstract Machine'' forum post}} which could be used to implement more powerful compile-time +function execution, similar to what is supported by C++14's \mintinline{cpp}{constexpr} feature. +After clarifying some of the details of the abstract machine's data layout with Burtescu via IRC, I +started implementing it in Miri. \subsection{Raw value representation} @@ -224,7 +224,7 @@ \subsubsection{Undefined byte mask} See \autoref{fig:undef} for an example undefined byte, represented by underscores. Note that there would still be a value for the second byte in the byte array, but we don't care what it is. The -bitmask would be $10_2$, i.e. \rust{[true, false]}. +bitmask would be $10_2$, i.e.\ \rust{[true, false]}. \begin{figure}[hb] \begin{minted}[autogobble]{rust} @@ -237,12 +237,179 @@ \subsubsection{Undefined byte mask} \label{fig:undef} \end{figure} -% TODO(tsion): Find a place for this text. -% Making Miri work was primarily an implementation problem. Writing an interpreter which models values -% of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some -% unconventional techniques compared to many interpreters. Miri's execution remains safe even while -% simulating execution of unsafe code, which allows it to detect when unsafe code does something -% invalid. +\subsection{Computing data layout} + +Currently, the Rust compiler's data layout computations used in translation from MIR to LLVM IR are +hidden from Miri, so I do my own basic data layout computation which doesn't generally match what +translation does. In the future, the Rust compiler may be modified so that Miri can use the exact +same data layout. + +Miri's data layout calculation is a relatively simple transformation from Rust types to a basic +structure with constant size values for primitives and sets of fields with offsets for aggregate +types. These layouts are cached for performance. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Deterministic execution} +\label{sec:deterministic} + +In order to be effective as a compile-time evaluator, Miri must have \emph{deterministic execution}, +as explained by Burtescu in the ``Rust Abstract Machine'' post. That is, given a function and +arguments to that function, Miri should always produce identical results. This is important for +coherence in the type checker when constant evaluations are involved in types, such as for sizes of +array types: + +\begin{minted}[autogobble,mathescape]{rust} + const fn get_size() -> usize { /* $\ldots$ */ } + let array: [i32; get_size()]; +\end{minted} + +Since Miri allows execution of unsafe code\footnote{In fact, the distinction between safe and unsafe +doesn't exist at the MIR level.}, it is specifically designed to remain safe while interpreting +potentially unsafe code. When Miri encounters an unrecoverable error, it reports it via the Rust +compiler's usual error reporting mechanism, pointing to the part of the original code where the +error occurred. For example: + +\begin{minted}[autogobble]{rust} + let b = Box::new(42); + let p: *const i32 = &*b; + drop(b); + unsafe { *p } + // ~~ error: dangling pointer + // was dereferenced +\end{minted} +\label{dangling-pointer} + +There are more examples in Miri's +repository.\footnote{\href{https://github.com/tsion/miri/blob/master/test/errors.rs}{Miri's error +tests}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Language support} + +In its current state, Miri supports a large proportion of the Rust language, with a few major +exceptions such as the lack of support for FFI\footnote{Foreign Function Interface, e.g.\ calling +functions defined in Assembly, C, or C++.}, which eliminates possibilities like reading and writing +files, user input, graphics, and more. The following is a tour of what is currently supported. + +\subsection{Primitives} + +Miri supports booleans and integers of various sizes and signed-ness (i.e.\ \rust{i8}, \rust{i16}, +\rust{i32}, \rust{i64}, \rust{isize}, \rust{u8}, \rust{u16}, \rust{u32}, \rust{u64}, \rust{usize}), +as well as unary and boolean operations over these types. The \rust{isize} and \rust{usize} types +will be sized according to the target machine's pointer size just like in compiled Rust. The +\rust{char} and float types (\rust{f32}, \rust{f64}) are not supported yet, but there are no known +barriers to doing so. + +When examining a boolean in an \rust{if} condition, Miri will report an error if it is not precisely +0 or 1, since this is undefined behaviour in Rust. The \rust{char} type has similar restrictions to +check for once it is implemented. + +\subsection{Pointers} + +Both references and raw pointers are supported, with essentially no difference between them in Miri. +It is also possible to do basic pointer comparisons and math. However, a few operations are +considered errors and a few require special support. + +Firstly, pointers into the same allocations may be compared for ordering, but pointers into +different allocations are considered unordered and Miri will complain if you attempt this. The +reasoning is that different allocations may have different orderings in the global address space at +runtime, making this non-deterministic. However, pointers into different allocations \emph{may} be +compared for direct equality (they are always, automatically unequal). + +Finally, for things like null pointer checks, abstract pointers (the kind represented using +relocations) may be compared against pointers casted from integers (e.g.\ \rust{0 as *const i32}). +To handle these cases, Miri has a concept of ``integer pointers'' which are always unequal to +abstract pointers. Integer pointers can be compared and operated upon freely. However, note that it +is impossible to go from an integer pointer to an abstract pointer backed by a relocation. It is not +valid to dereference an integer pointer. + +\subsubsection{Slice pointers} + +Rust supports pointers to ``dynamically-sized types'' such as \rust{[T]} and \rust{str} which +represent arrays of indeterminate size. Pointers to such types contain an address \emph{and} the +length of the referenced array. Miri supports these fully. + +\subsubsection{Trait objects} + +Rust also supports pointers to ``trait objects'' which represent some type that implements a trait, +with the specific type unknown at compile-time. These are implemented using virtual dispatch with a +vtable, similar to virtual methods in C++. Miri does not currently support this at all. + +\subsection{Aggregates} + +Aggregates include types declared as \rust{struct} or \rust{enum} as well as tuples, arrays, and +closures\footnote{Closures are essentially structs with a field for each variable captured by the +closure.}. Miri supports all common usage of all of these types. The main missing piece is to handle +\texttt{\#[repr(..)]} annotations which adjust the layout of a \rust{struct} or \rust{enum}. + +\subsection{Control flow} + +All of Rust's standard control flow features, including \rust{loop}, \rust{while}, \rust{for}, +\rust{if}, \rust{if let}, \rust{while let}, \rust{match}, \rust{break}, \rust{continue}, and +\rust{return} are supported. In fact, supporting these were quite easy since the Rust compiler +reduces them all down to a comparatively smaller set of control-flow graph primitives in MIR. + +\subsection{Closures} + +Closures are like structs containing a field for each captured variable, but closures also have an +associated function. Supporting closure function calls required some extra machinery to get the +necessary information from the compiler, but it is all supported except for one edge case on my todo +list\footnote{The edge case is calling a closure that takes a reference to its captures via a +closure interface that passes the captures by value.}. + +\subsection{Intrinsics} + +To support unsafe code, and in particular the unsafe code used to implement Rust's standard library, +it became clear that Miri would have to support calls to compiler +intrinsics\footnote{\href{https://doc.rust-lang.org/stable/std/intrinsics/index.html}{Rust +intrinsics documentation}}. Intrinsics are function calls which cause the Rust compiler to produce +special-purpose code instead of a regular function call. Miri simply recognizes intrinsic calls by +their unique ABI\footnote{Application Binary Interface, which defines calling conventions. Includes +``C'', ``Rust'', and ``rust-intrinsic''.} and name and runs special purpose code to handle them. + +An example of an important intrinsic is \rust{size_of} which will cause Miri to write the size of +the type in question to the return value location. The Rust standard library uses intrinsics heavily +to implement various data structures, so this was a major step toward supporting them. So far, I've +been implementing intrinsics on a case-by-case basis as I write test cases which require missing +ones, so I haven't yet exhaustively implemented them all. + +\subsection{Heap allocations} + +The next piece of the puzzle for supporting interesting programs (and the standard library) was heap +allocations. There are two main interfaces for heap allocation in Rust, the built-in \rust{Box} +rvalue in MIR and a set of C ABI foreign functions including \rust{__rust_allocate}, +\rust{__rust_reallocate}, and \rust{__rust_deallocate}. These correspond approximately to +\mintinline{c}{malloc}, \mintinline{c}{realloc}, and \mintinline{c}{free} in C. + +The \rust{Box} rvalue allocates enough space for a single value of a given type. This was easy to +support in Miri. It simply creates a new abstract allocation in the same manner as for +stack-allocated values, since there's no major difference between them in Miri. + +The allocator functions, which are used to implement things like Rust's standard \rust{Vec} type, +were a bit trickier. Rust declares them as \rust{extern "C" fn} so that different allocator +libraries can be linked in at the user's option. Since Miri doesn't actually support FFI and we want +full control of allocations for safety, Miri ``cheats'' and recognizes these allocator function in +essentially the same way it recognizes compiler intrinsics. Then, a call to \rust{__rust_allocate} +simply creates another abstract allocation with the requested size and \rust{__rust_reallocate} +grows one. + +In the future, Miri should also track which allocations came from \rust{__rust_allocate} so it can +reject reallocate or deallocate calls on stack allocations. + +\subsection{Destructors} + +Miri doesn't yet support calling user-defined destructors, but it has most of the machinery in place +to do so already and it's next on my to-do list. There \emph{is} support for dropping \rust{Box} +types, including deallocating their associated allocations. This is enough to properly execute the +dangling pointer example in \autoref{sec:deterministic}. + +\subsection{Standard library} +\blindtext + +\section{Unsupported} +\blindtext \begin{figure}[t] \begin{minted}[autogobble]{rust} @@ -280,6 +447,12 @@ \subsubsection{Undefined byte mask} \section{Future work} +\subsection{Finishing the implementation} + +\blindtext + +\subsection{Alternative applications} + Other possible uses for Miri include: \begin{itemize} @@ -299,6 +472,17 @@ \section{Future work} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Final thoughts} + +% TODO(tsion): Reword this. +Making Miri work was primarily an implementation problem. Writing an interpreter which models values +of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some +unconventional techniques compared to many interpreters. Miri's execution remains safe even while +simulating execution of unsafe code, which allows it to detect when unsafe code does something +invalid. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \section{Thanks} Eduard Burtescu, Niko Matsakis, and Christopher Dutchyn. From 8d9df5b442929fe33fb941ac302496845a08a7ab Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 12 Apr 2016 22:51:19 -0600 Subject: [PATCH 0231/1096] report: Finish the report. --- tex/report/miri-report.tex | 204 ++++++++++++++++++++++++++++++++----- 1 file changed, 181 insertions(+), 23 deletions(-) diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 9655dcdaddb07..e1eb35a316dc9 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -344,6 +344,12 @@ \subsection{Aggregates} closure.}. Miri supports all common usage of all of these types. The main missing piece is to handle \texttt{\#[repr(..)]} annotations which adjust the layout of a \rust{struct} or \rust{enum}. +\subsection{Lvalue projections} + +This category includes field accesses like \rust{foo.bar}, dereferencing, accessing data in an +\rust{enum} variant, and indexing arrays. Miri supports all of these, including nested projections +such as \rust{*foo.bar[2]}. + \subsection{Control flow} All of Rust's standard control flow features, including \rust{loop}, \rust{while}, \rust{for}, @@ -351,7 +357,21 @@ \subsection{Control flow} \rust{return} are supported. In fact, supporting these were quite easy since the Rust compiler reduces them all down to a comparatively smaller set of control-flow graph primitives in MIR. -\subsection{Closures} +\subsection{Function calls} + +As previously described, Miri supports arbitrary function calls without growing its own stack (only +its virtual call stack). It is somewhat limited by the fact that cross-crate\footnote{A crate is a +single Rust library (or executable).} calls only work for functions whose MIR is stored in crate +metadata. This is currently true for \rust{const}, generic, and \texttt{\#[inline]} functions. A +branch of the compiler could be made that stores MIR for all functions. This would be a non-issue +for a compile-time evaluator based on Miri, since it would only call \rust{const fn}s. + +\subsubsection{Method calls} + +Trait method calls require a bit more machinery dealing with compiler internals than normal function +calls, but Miri supports them. + +\subsubsection{Closures} Closures are like structs containing a field for each captured variable, but closures also have an associated function. Supporting closure function calls required some extra machinery to get the @@ -359,7 +379,14 @@ \subsection{Closures} list\footnote{The edge case is calling a closure that takes a reference to its captures via a closure interface that passes the captures by value.}. -\subsection{Intrinsics} +\subsubsection{Function pointers} + +Function pointers are not currently supported by Miri, but there is a relatively simple way they +could be encoded using a relocation with a special reserved allocation identifier. The offset of the +relocation would determine which function it points to in a special array of functions in the +interpreter. + +\subsubsection{Intrinsics} To support unsafe code, and in particular the unsafe code used to implement Rust's standard library, it became clear that Miri would have to support calls to compiler @@ -375,6 +402,25 @@ \subsection{Intrinsics} been implementing intrinsics on a case-by-case basis as I write test cases which require missing ones, so I haven't yet exhaustively implemented them all. +\subsubsection{Generic function calls} + +Miri needs special support for generic function calls since Rust is a \emph{monomorphizing} +compiler, meaning it generates a special version of each function for each distinct set of type +parameters it gets called with. Since functions in MIR are still polymorphic, Miri has to do the +same thing and substitute function type parameters into all types it encounters to get fully +concrete, monomorphized types. For example, in\ldots + +\begin{minted}[autogobble]{rust} + fn some(t: T) -> Option { Some(t) } +\end{minted} + +\ldots{} Miri needs to know how many bytes to copy from the argument to the return value, based on +the size of \rust{T}. If we call \rust{some(10i32)} Miri will execute \rust{some} knowing that +\rust{T = i32} and generate a representation for \rust{Option}. + +Miri currently does this monomorphization on-demand, or lazily, unlike the Rust back-end which does +it all ahead of time. + \subsection{Heap allocations} The next piece of the puzzle for supporting interesting programs (and the standard library) was heap @@ -400,16 +446,58 @@ \subsection{Heap allocations} \subsection{Destructors} +When values go out of scope that ``own'' some resource, like a heap allocation or file handle, Rust +inserts \emph{drop glue} that calls the user-defined destructor for the type if it exists, and then +drops all of the subfields. Destructors for types like \rust{Box} and \rust{Vec} deallocate +heap memory. + Miri doesn't yet support calling user-defined destructors, but it has most of the machinery in place to do so already and it's next on my to-do list. There \emph{is} support for dropping \rust{Box} types, including deallocating their associated allocations. This is enough to properly execute the dangling pointer example in \autoref{sec:deterministic}. +\subsection{Constants} + +Only basic integer, boolean, string, and byte-string literals are currently supported. Evaluating +more complicated constant expressions in their current form would be a somewhat pointless exercise +for Miri. Instead, we should lower constant expressions to MIR so Miri can run them directly. (This +is precisely what would be done to use Miri as the actual constant evaluator.) + +\subsection{Static variables} + +While it would be invalid to write to static (i.e.\ global) variables in Miri executions, it would +probably be fine to allow reads. However, Miri doesn't currently support them and they would need +support similar to constants. + \subsection{Standard library} -\blindtext -\section{Unsupported} -\blindtext +Throughout the implementation of the above features, I often followed this process: + +\begin{enumerate} + \item Try using a feature from the standard library. + \item See where Miri runs into stuff it can't handle. + \item Fix the problem. + \item Go to 1. +\end{enumerate} + +At present, Miri supports a number of major non-trivial features from the standard library along +with tons of minor features. Smart pointer types such as \rust{Box}, \rust{Rc}\footnote{Reference +counted shared pointer} and \rust{Arc}\footnote{Atomically reference-counted thread-safe shared +pointer} all seem to work. I've also tested using the shared smart pointer types with \rust{Cell} +and \rust{RefCell}\footnote{\href{https://doc.rust-lang.org/stable/std/cell/index.html}{Rust +documentation for cell types}} for internal mutability, and that works as well, although +\rust{RefCell} can't ever be borrowed twice until I implement destructor calls, since its destructor +is what releases the borrow. + +But the standard library collection I spent the most time on was \rust{Vec}, the standard +dynamically-growable array type, similar to C++'s \texttt{std::vector} or Java's +\texttt{java.util.ArrayList}. In Rust, \rust{Vec} is an extremely pervasive collection, so +supporting it is a big win for supporting a larger swath of Rust programs in Miri. + +See \autoref{fig:vec} for an example (working in Miri today) of initializing a \rust{Vec} with a +small amount of space on the heap and then pushing enough elements to force it to reallocate its +data array. This involves cross-crate generic function calls, unsafe code using raw pointers, heap +allocation, handling of uninitialized memory, compiler intrinsics, and more. \begin{figure}[t] \begin{minted}[autogobble]{rust} @@ -441,15 +529,57 @@ \section{Unsupported} // B: 01 02 03 __ \end{minted} \caption{\rust{Vec} example on 32-bit little-endian} + \label{fig:vec} +\end{figure} + +You can even do unsafe things with \rust{Vec} like \rust{v.set_len(10)} or +\rust{v.get_unchecked(2)}, but if you do these things carefully in a way that doesn't cause any +undefined behaviour (just like when you write unsafe code for regular Rust), then Miri can handle it +all. But if you do slip up, Miri will error out with an appropriate message (see +\autoref{fig:vec-error}). + +\begin{figure}[t] + \begin{minted}[autogobble]{rust} + fn out_of_bounds() -> u8 { + let v = vec![1, 2]; + let p = unsafe { v.get_unchecked(5) }; + *p + 10 + // ~~ error: pointer offset outside + // bounds of allocation + } + + fn undefined_bytes() -> u8 { + let v = Vec::::with_capacity(10); + let p = unsafe { v.get_unchecked(5) }; + *p + 10 + // ~~~~~~~ error: attempted to read + // undefined bytes + } + \end{minted} + \caption{\rust{Vec} examples with undefined behaviour} + \label{fig:vec-error} \end{figure} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\section{Future work} +\section{Future directions} \subsection{Finishing the implementation} -\blindtext +There are a number of pressing items on my to-do list for Miri, including: + +\begin{itemize} + \item Destructors and \rust{__rust_deallocate}. + \item Non-trivial casts between primitive types like integers and pointers. + \item Handling statics and global memory. + \item Reporting errors for all undefined behaviour.\footnote{\href{https://doc.rust-lang.org/reference.html\#behavior-considered-undefined}{The Rust reference on what is considered undefined behaviour}} + \item Function pointers. + \item Accounting for target machine primitive type alignment and endianness. + \item Optimizing stuff (undefined byte masks, tail-calls). + \item Benchmarking Miri vs. unoptimized Rust. + \item Various \texttt{TODO}s and \texttt{FIXME}s left in the code. + \item Getting a version of Miri into rustc for real. +\end{itemize} \subsection{Alternative applications} @@ -459,32 +589,60 @@ \subsection{Alternative applications} \item A graphical or text-mode debugger that steps through MIR execution one statement at a time, for figuring out why some compile-time execution is raising an error or simply learning how Rust works at a low level. - \item A read-eval-print-loop (REPL) for Rust may be easier to implement on top of Miri than the - usual LLVM back-end. - \item An extended version of Miri could be developed apart from the purpose of compile-time - execution that is able to run foreign functions from C/C++ and generally have full access to the - operating system. Such a version of Miri could be used to more quickly prototype changes to the - Rust language that would otherwise require changes to the LLVM back-end. - \item Miri might be useful for unit-testing the compiler by comparing the results of Miri's - execution against the results of LLVM-compiled machine code's execution. This would help to - guarantee that compile-time execution works the same as runtime execution. + \item A read-eval-print-loop (REPL) for Rust, which may be easier to implement on top of Miri than + the usual LLVM back-end. + \item An extended version of Miri developed apart from the purpose of compile-time execution that + is able to run foreign functions from C/C++ and generally have full access to the operating + system. Such a version of Miri could be used to more quickly prototype changes to the Rust + language that would otherwise require changes to the LLVM back-end. + \item Unit-testing the compiler by comparing the results of Miri's execution against the results + of LLVM-compiled machine code's execution. This would help to guarantee that compile-time + execution works the same as runtime execution. + \item Some kind of symbolic evaluator that examines multiple possible code paths at once to + determine if undefined behaviour could be observed on any of them. \end{itemize} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Final thoughts} -% TODO(tsion): Reword this. -Making Miri work was primarily an implementation problem. Writing an interpreter which models values -of varying sizes, stack and heap allocation, unsafe memory operations, and more requires some -unconventional techniques compared to many interpreters. Miri's execution remains safe even while -simulating execution of unsafe code, which allows it to detect when unsafe code does something -invalid. +Writing an interpreter which models values of varying sizes, stack and heap allocation, unsafe +memory operations, and more requires some unconventional techniques compared to typical +interpreters. However, aside from the somewhat complicated abstract memory model, making Miri work +was primarily a software engineering problem, and not a particularly tricky one. This is a testament +to MIR's suitability as an intermediate representation for Rust---removing enough unnecessary +abstraction to keep it simple. For example, Miri doesn't even need to know that there are different +kind of loops, or how to match patterns in a \rust{match} expression. + +Another advantage to targeting MIR is that any new features at the syntax-level or type-level +generally require little to no change in Miri. For example, when the new ``question mark'' syntax +for error handling\footnote{ + \href{https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md} + {Question mark syntax RFC}} +was added to rustc, Miri also supported it the same day with no change. When specialization\footnote{ + \href{https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md} + {Specialization RFC}} +was added, Miri supported it with just minor changes to trait method lookup. + +Of course, Miri also has limitations. The inability to execute FFI and inline assembly reduces the +amount of Rust programs Miri could ever execute. The good news is that in the constant evaluator, +FFI can be stubbed out in cases where it makes sense, like I did with \rust{__rust_allocate}, and +for Miri outside of the compiler it may be possible to use libffi to call C functions from the +interpreter. + +In conclusion, Miri was a surprisingly effective project, and a lot of fun to implement. There were +times where I ended up supporting Rust features I didn't even intend to while I was adding support +for some other feature, due to the design of MIR collapsing features at the source level into fewer +features at the MIR level. I am excited to work with the compiler team going forward to try to make +Miri useful for constant evaluation in Rust. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Thanks} -Eduard Burtescu, Niko Matsakis, and Christopher Dutchyn. +A big thanks goes to Eduard Burtescu for writing the abstract machine specification and answering my +incessant questions on IRC, to Niko Matsakis for coming up with the idea for Miri and supporting my +desire to work with the Rust compiler, and to my research supervisor Christopher Dutchyn. Thanks +also to everyone else on the compiler team and on Mozilla IRC who helped me figure stuff out. \end{document} From 00dc20ab26591b9641bb822d85fc353272b6307b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 06:12:28 -0600 Subject: [PATCH 0232/1096] report: Numerous fixes. :heart: @DanielKeep, @programble, @ubsan, @eddyb --- test/vecs.rs | 7 +- tex/report/miri-report.tex | 432 +++++++++++++++++++------------------ 2 files changed, 231 insertions(+), 208 deletions(-) diff --git a/test/vecs.rs b/test/vecs.rs index 063d7116744d0..a1894505fb969 100644 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -20,8 +20,11 @@ fn make_vec_macro_repeat() -> Vec { } #[miri_run] -fn vec_into_iter() -> i32 { - vec![1, 2, 3, 4].into_iter().fold(0, |x, y| x + y) +fn vec_into_iter() -> u8 { + vec![1, 2, 3, 4] + .into_iter() + .map(|x| x * x) + .fold(0, |x, y| x + y) } #[miri_run] diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index e1eb35a316dc9..82efc73605caa 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -31,18 +31,19 @@ \section{Abstract} The increasing need for safe low-level code in contexts like operating systems and browsers is driving the development of Rust\footnote{\url{https://www.rust-lang.org}}, a programming language -backed by Mozilla promising blazing speed without the segfaults. To make programming more -convenient, it's often desirable to be able to generate code or perform some computation at -compile-time. The former is mostly covered by Rust's existing macro feature, but the latter is -currently restricted to a limited form of constant evaluation capable of little beyond simple math. - -When the existing constant evaluator was built, it would have been difficult to make it more -powerful than it is. However, a new intermediate representation was recently -added\footnote{\href{https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md}{The MIR RFC}} +promising high performance without the risk of memory unsafety. To make programming more convenient, +it's often desirable to be able to generate code or perform some computation at compile-time. The +former is mostly covered by Rust's existing macro feature or build-time code generation, but the +latter is currently restricted to a limited form of constant evaluation capable of little beyond +simple math. + +The architecture of the compiler at the time the existing constant evaluator was built limited its +potential for future extension. However, a new intermediate representation was recently +added\footnote{\href{https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md}{Rust RFC \#1211: Mid-level IR (MIR)}} to the Rust compiler between the abstract syntax tree and the back-end LLVM IR, called mid-level -intermediate representation, or MIR for short. As it turns out, writing an interpreter for MIR is a -surprisingly effective approach for supporting a large proportion of Rust's features in compile-time -execution. +intermediate representation, or MIR for short. This report will demonstrate that writing an +interpreter for MIR is a surprisingly effective approach for supporting a large proportion of Rust's +features in compile-time execution. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -54,9 +55,9 @@ \section{Background} statement is of the form \rust{lvalue = rvalue}. An \rust{Lvalue} is used for referencing variables and calculating addresses such as when dereferencing pointers, accessing fields, or indexing arrays. An \rust{Rvalue} represents the core set of operations possible in MIR, including reading a value -from an lvalue, performing math operations, creating new pointers, structs, and arrays, and so on. -Finally, a terminator decides where control will flow next, optionally based on a boolean or some -other condition. +from an lvalue, performing math operations, creating new pointers, structures, and arrays, and so +on. Finally, a terminator decides where control will flow next, optionally based on the value of a +boolean or integer. \begin{figure}[ht] \begin{minted}[autogobble]{rust} @@ -95,14 +96,14 @@ \section{First implementation} \subsection{Basic operation} -Initially, I wrote a simple version of Miri\footnote{\url{https://github.com/tsion/miri}} that was -quite capable despite its flaws. The structure of the interpreter closely mirrors the structure of -MIR itself. It starts executing a function by iterating the statement list in the starting basic -block, matching over the lvalue to produce a pointer and matching over the rvalue to decide what to -write into that pointer. Evaluating the rvalue may involve reads (such as for the two sides of a -binary operation) or construction of new values. Upon reaching the terminator, a similar matching is -done and a new basic block is selected. Finally, Miri returns to the top of the main interpreter -loop and this entire process repeats, reading statements from the new block. +To investigate the possibility of executing Rust at compile-time I wrote an interpreter for MIR +called Miri\footnote{\url{https://github.com/tsion/miri}}. The structure of the interpreter closely +mirrors the structure of MIR itself. It starts executing a function by iterating the statement list +in the starting basic block, translating the lvalue into a pointer and using the rvalue to decide +what to write into that pointer. Evaluating the rvalue may involve reads (such as for the two sides +of a binary operation) or construction of new values. When the terminator is reached, it is used to +decide which basic block to jump to next. Finally, Miri repeats this entire process, reading +statements from the new block. \subsection{Function calls} @@ -110,25 +111,25 @@ \subsection{Function calls} Miri is required to store some information in a virtual call stack so that it may pick up where it left off when the callee returns. Each stack frame stores a reference to the \rust{Mir} for the function being executed, its local variables, its return value location\footnote{Return value -pointers are passed in by callers.}, and the basic block where execution should resume. To -facilitate returning, there is a \rust{Return} terminator which causes Miri to pop a stack frame and -resume the previous function. The entire execution of a program completes when the first function -that Miri called returns, rendering the call stack empty. +pointers are passed in by callers.}, and the basic block where execution should resume. When Miri +encounters a \rust{Return} terminator in the MIR, it pops one frame off the stack and resumes the +previous function. Miri's execution ends when the function it was initially invoked with returns, +leaving the call stack empty. It should be noted that Miri does not itself recurse when a function is called; it merely pushes a virtual stack frame and jumps to the top of the interpreter loop. Consequently, Miri can interpret -deeply recursive programs without crashing. It could also set a stack depth limit and report an -error when a program exceeds it. +deeply recursive programs without overflowing its native call stack. This approach would allow Miri +to set a virtual stack depth limit and report an error when a program exceeds it. \subsection{Flaws} -This version of Miri was surprisingly easy to write and already supported quite a bit of the Rust -language, including booleans, integers, if-conditions, while-loops, structs, enums, arrays, tuples, -pointers, and function calls, all in about 400 lines of Rust code. However, it had a particularly -naive value representation with a number of downsides. It resembled the data layout of a dynamic -language like Ruby or Python, where every value has the same size\footnote{A Rust \rust{enum} is a -discriminated union with a tag and data the size of the largest variant, regardless of which variant -it contains.} in the interpreter: +This version of Miri supported quite a bit of the Rust language, including booleans, integers, +if-conditions, while-loops, structures, enums, arrays, tuples, pointers, and function calls, +requiring approximately 400 lines of Rust code. However, it had a particularly naive value +representation with a number of downsides. It resembled the data layout of a dynamic language like +Ruby or Python, where every value has the same size\footnote{An \rust{enum} is a discriminated union +with a tag and space to fit the largest variant, regardless of which variant it contains.} in the +interpreter: \begin{minted}[autogobble]{rust} enum Value { @@ -136,40 +137,42 @@ \subsection{Flaws} Bool(bool), Int(i64), Pointer(Pointer), // index into stack - Adt { variant: usize, data: Pointer }, - // ... + Aggregate { + variant: usize, + data: Pointer, + }, } \end{minted} -This representation did not work well for \rust{Adt}s\footnote{Algebraic data types: structs, enums, -arrays, and tuples.} and required strange hacks to support them. Their contained values were -allocated elsewhere on the stack and pointed to by the \rust{Adt} value. When it came to copying -\rust{Adt} values from place to place, this made it more complicated. +This representation did not work well for aggregate types\footnote{That is, structures, enums, +arrays, tuples, and closures.} and required strange hacks to support them. Their contained values +were allocated elsewhere on the stack and pointed to by the aggregate value, which made it more +complicated to implement copying aggregate values from place to place. -Moreover, while the \rust{Adt} issues could be worked around, this value representation made common -\rust{unsafe} programming tricks (which make assumptions about the low-level value layout) -fundamentally impossible. +Moreover, while the aggregate issues could be worked around, this value representation made common +unsafe programming tricks (which make assumptions about the low-level value layout) fundamentally +impossible. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Current implementation} Roughly halfway through my time working on Miri, Eduard -Burtescu\footnote{\href{https://github.com/eddyb}{Eduard Burtescu on GitHub}} from the Rust compiler -team\footnote{\href{https://www.rust-lang.org/team.html\#Compiler}{The Rust compiler team}} made a -post on Rust's internal forums about a ``Rust Abstract Machine'' +Burtescu\footnote{\href{https://github.com/eddyb}{eddyb on GitHub}} from the Rust compiler +team\footnote{\url{https://www.rust-lang.org/team.html\#Compiler}} made a post on Rust's internal +forums about a ``Rust Abstract Machine'' specification\footnote{\href{https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31}{Burtescu's -``Rust Abstract Machine'' forum post}} which could be used to implement more powerful compile-time +reply on ``MIR constant evaluation''}} which could be used to implement more powerful compile-time function execution, similar to what is supported by C++14's \mintinline{cpp}{constexpr} feature. -After clarifying some of the details of the abstract machine's data layout with Burtescu via IRC, I -started implementing it in Miri. +After clarifying some of the details of the data layout with Burtescu via IRC, I started +implementing it in Miri. \subsection{Raw value representation} The main difference in the new value representation was to represent values by ``abstract -allocations'' containing arrays of raw bytes with different sizes depending on the types of the -values. This closely mimics how Rust values are represented when compiled for traditional machines. -In addition to the raw bytes, allocations carry information about pointers and undefined bytes. +allocations'' containing arrays of raw bytes with different sizes depending on their types. This +mimics how Rust values are represented when compiled for physical machines. In addition to the raw +bytes, allocations carry information about pointers and undefined bytes. \begin{minted}[autogobble]{rust} struct Memory { @@ -189,49 +192,48 @@ \subsubsection{Relocations} The abstract machine represents pointers through ``relocations'', which are analogous to relocations in linkers\footnote{\href{https://en.wikipedia.org/wiki/Relocation_(computing)}{Relocation (computing) - Wikipedia}}. Instead of storing a global memory address in the raw byte representation -like a traditional machine, we store an offset from the start of the target allocation and add an -entry to the relocation table. The entry maps the index of the start of the offset bytes to the -\rust{AllocId} of the target allocation. +like on a physical machine, we store an offset from the start of the target allocation and add an +entry to the relocation table which maps the index of the offset bytes to the target allocation. -\begin{figure}[ht] +In \autoref{fig:reloc}, the relocation stored at offset 0 in \rust{y} points to offset 2 in \rust{x} +(the 2nd 16-bit integer). Thus, the relocation table for \rust{y} is \texttt{\{0 => +x\}}, meaning the next $N$ bytes after offset 0 denote an offset into allocation \rust{x} where $N$ +is the size of a pointer (4 in this example). The example shows this as a labelled line beneath the +offset bytes. + +In effect, the abstract machine represents pointers as \rust{(allocation_id, offset)} pairs. This +makes it easy to detect when pointer accesses go out of bounds. + +\begin{figure}[hb] \begin{minted}[autogobble]{rust} - let a: [i16; 3] = [2, 4, 6]; - let b = &a[1]; - // A: 02 00 04 00 06 00 (6 bytes) - // B: 02 00 00 00 (4 bytes) - // └───(A)───┘ + let x: [i16; 3] = [0xAABB, 0xCCDD, 0xEEFF]; + let y = &x[1]; + // x: BB AA DD CC FF EE (6 bytes) + // y: 02 00 00 00 (4 bytes) + // └───(x)───┘ \end{minted} \caption{Example relocation on 32-bit little-endian} \label{fig:reloc} \end{figure} -In effect, the abstract machine treats each allocation as a separate address space and represents -pointers as \rust{(address_space, offset)} pairs. This makes it easy to detect when pointer accesses -go out of bounds. - -See \autoref{fig:reloc} for an example of a relocation. Variable \rust{b} points to the second -16-bit integer in \rust{a}, so it contains a relocation with offset 2 and target allocation -\rust{A}. - \subsubsection{Undefined byte mask} The final piece of an abstract allocation is the undefined byte mask. Logically, we store a boolean for the definedness of every byte in the allocation, but there are multiple ways to make the storage more compact. I tried two implementations: one based on the endpoints of alternating ranges of -defined and undefined bytes and the other based on a simple bitmask. The former is more compact but -I found it surprisingly difficult to update cleanly. I currently use the bitmask system, which is -comparatively trivial. +defined and undefined bytes and the other based on a bitmask. The former is more compact but I found +it surprisingly difficult to update cleanly. I currently use the much simpler bitmask system. -See \autoref{fig:undef} for an example undefined byte, represented by underscores. Note that there -would still be a value for the second byte in the byte array, but we don't care what it is. The -bitmask would be $10_2$, i.e.\ \rust{[true, false]}. +See \autoref{fig:undef} for an example of an undefined byte in a value, represented by underscores. +Note that there is a value for the second byte in the byte array, but it doesn't matter what it is. +The bitmask would be $10_2$, i.e.\ \rust{[true, false]}. \begin{figure}[hb] \begin{minted}[autogobble]{rust} - let a: [u8; 2] = unsafe { + let x: [u8; 2] = unsafe { [1, std::mem::uninitialized()] }; - // A: 01 __ (2 bytes) + // x: 01 __ (2 bytes) \end{minted} \caption{Example undefined byte} \label{fig:undef} @@ -239,14 +241,14 @@ \subsubsection{Undefined byte mask} \subsection{Computing data layout} -Currently, the Rust compiler's data layout computations used in translation from MIR to LLVM IR are -hidden from Miri, so I do my own basic data layout computation which doesn't generally match what -translation does. In the future, the Rust compiler may be modified so that Miri can use the exact -same data layout. +Currently, the Rust compiler's data layouts for types are hidden from Miri, so it does its own data +layout computation which will not always match what the compiler does, since Miri doesn't take +target type alignments into account. In the future, the Rust compiler may be modified so that Miri +can use the exact same data layout. -Miri's data layout calculation is a relatively simple transformation from Rust types to a basic -structure with constant size values for primitives and sets of fields with offsets for aggregate -types. These layouts are cached for performance. +Miri's data layout calculation is a relatively simple transformation from Rust types to a structure +with constant size values for primitives and sets of fields with offsets for aggregate types. These +layouts are cached for performance. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -268,7 +270,8 @@ \section{Deterministic execution} doesn't exist at the MIR level.}, it is specifically designed to remain safe while interpreting potentially unsafe code. When Miri encounters an unrecoverable error, it reports it via the Rust compiler's usual error reporting mechanism, pointing to the part of the original code where the -error occurred. For example: +error occurred. Below is an example from Miri's +repository.\footnote{\href{https://github.com/tsion/miri/blob/master/test/errors.rs}{miri/test/errors.rs}} \begin{minted}[autogobble]{rust} let b = Box::new(42); @@ -280,50 +283,47 @@ \section{Deterministic execution} \end{minted} \label{dangling-pointer} -There are more examples in Miri's -repository.\footnote{\href{https://github.com/tsion/miri/blob/master/test/errors.rs}{Miri's error -tests}} - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Language support} -In its current state, Miri supports a large proportion of the Rust language, with a few major -exceptions such as the lack of support for FFI\footnote{Foreign Function Interface, e.g.\ calling +In its current state, Miri supports a large proportion of the Rust language, detailed below. The +major exception is a lack of support for FFI\footnote{Foreign Function Interface, e.g.\ calling functions defined in Assembly, C, or C++.}, which eliminates possibilities like reading and writing -files, user input, graphics, and more. The following is a tour of what is currently supported. +files, user input, graphics, and more. However, for compile-time evaluation in Rust, this limitation +is desired. \subsection{Primitives} -Miri supports booleans and integers of various sizes and signed-ness (i.e.\ \rust{i8}, \rust{i16}, +Miri supports booleans, integers of various sizes and signed-ness (i.e.\ \rust{i8}, \rust{i16}, \rust{i32}, \rust{i64}, \rust{isize}, \rust{u8}, \rust{u16}, \rust{u32}, \rust{u64}, \rust{usize}), -as well as unary and boolean operations over these types. The \rust{isize} and \rust{usize} types -will be sized according to the target machine's pointer size just like in compiled Rust. The -\rust{char} and float types (\rust{f32}, \rust{f64}) are not supported yet, but there are no known -barriers to doing so. +and unary and binary operations over these types. The \rust{isize} and \rust{usize} types will be +sized according to the target machine's pointer size just like in compiled Rust. The \rust{char} and +float types (\rust{f32}, \rust{f64}) are not supported yet, but there are no known barriers to doing +so. -When examining a boolean in an \rust{if} condition, Miri will report an error if it is not precisely -0 or 1, since this is undefined behaviour in Rust. The \rust{char} type has similar restrictions to -check for once it is implemented. +When examining a boolean in an \rust{if} condition, Miri will report an error if its byte +representation is not precisely 0 or 1, since having any other value for a boolean is undefined +behaviour in Rust. The \rust{char} type will have similar restrictions once it is implemented. \subsection{Pointers} Both references and raw pointers are supported, with essentially no difference between them in Miri. -It is also possible to do basic pointer comparisons and math. However, a few operations are -considered errors and a few require special support. +It is also possible to do pointer comparisons and math. However, a few operations are considered +errors and a few require special support. Firstly, pointers into the same allocations may be compared for ordering, but pointers into different allocations are considered unordered and Miri will complain if you attempt this. The reasoning is that different allocations may have different orderings in the global address space at runtime, making this non-deterministic. However, pointers into different allocations \emph{may} be -compared for direct equality (they are always, automatically unequal). +compared for direct equality (they are always unequal). -Finally, for things like null pointer checks, abstract pointers (the kind represented using -relocations) may be compared against pointers casted from integers (e.g.\ \rust{0 as *const i32}). -To handle these cases, Miri has a concept of ``integer pointers'' which are always unequal to -abstract pointers. Integer pointers can be compared and operated upon freely. However, note that it -is impossible to go from an integer pointer to an abstract pointer backed by a relocation. It is not -valid to dereference an integer pointer. +Secondly, pointers represented using relocations may be compared against pointers casted from +integers (e.g.\ \rust{0 as *const i32}) for things like null pointer checks. To handle these cases, +Miri has a concept of ``integer pointers'' which are always unequal to abstract pointers. Integer +pointers can be compared and operated upon freely. However, note that it is impossible to go from an +integer pointer to an abstract pointer backed by a relocation. It is not valid to dereference an +integer pointer. \subsubsection{Slice pointers} @@ -335,49 +335,48 @@ \subsubsection{Trait objects} Rust also supports pointers to ``trait objects'' which represent some type that implements a trait, with the specific type unknown at compile-time. These are implemented using virtual dispatch with a -vtable, similar to virtual methods in C++. Miri does not currently support this at all. +vtable, similar to virtual methods in C++. Miri does not currently support these at all. \subsection{Aggregates} -Aggregates include types declared as \rust{struct} or \rust{enum} as well as tuples, arrays, and -closures\footnote{Closures are essentially structs with a field for each variable captured by the -closure.}. Miri supports all common usage of all of these types. The main missing piece is to handle +Aggregates include types declared with \rust{struct} or \rust{enum} as well as tuples, arrays, and +closures. Miri supports all common usage of all of these types. The main missing piece is to handle \texttt{\#[repr(..)]} annotations which adjust the layout of a \rust{struct} or \rust{enum}. \subsection{Lvalue projections} -This category includes field accesses like \rust{foo.bar}, dereferencing, accessing data in an -\rust{enum} variant, and indexing arrays. Miri supports all of these, including nested projections -such as \rust{*foo.bar[2]}. +This category includes field accesses, dereferencing, accessing data in an \rust{enum} variant, and +indexing arrays. Miri supports all of these, including nested projections such as +\rust{*foo.bar[2]}. \subsection{Control flow} All of Rust's standard control flow features, including \rust{loop}, \rust{while}, \rust{for}, \rust{if}, \rust{if let}, \rust{while let}, \rust{match}, \rust{break}, \rust{continue}, and -\rust{return} are supported. In fact, supporting these were quite easy since the Rust compiler -reduces them all down to a comparatively smaller set of control-flow graph primitives in MIR. +\rust{return} are supported. In fact, supporting these was quite easy since the Rust compiler +reduces them all down to a small set of control-flow graph primitives in MIR. \subsection{Function calls} -As previously described, Miri supports arbitrary function calls without growing its own stack (only -its virtual call stack). It is somewhat limited by the fact that cross-crate\footnote{A crate is a -single Rust library (or executable).} calls only work for functions whose MIR is stored in crate -metadata. This is currently true for \rust{const}, generic, and \texttt{\#[inline]} functions. A -branch of the compiler could be made that stores MIR for all functions. This would be a non-issue +As previously described, Miri supports arbitrary function calls without growing the native stack +(only its virtual call stack). It is somewhat limited by the fact that cross-crate\footnote{A crate +is a single Rust library (or executable).} calls only work for functions whose MIR is stored in +crate metadata. This is currently true for \rust{const}, generic, and inline functions. +A branch of the compiler could be made that stores MIR for all functions. This would be a non-issue for a compile-time evaluator based on Miri, since it would only call \rust{const fn}s. \subsubsection{Method calls} -Trait method calls require a bit more machinery dealing with compiler internals than normal function -calls, but Miri supports them. +Miri supports trait method calls, including invoking all the compiler-internal lookup needed to find +the correct implementation of the method. \subsubsection{Closures} -Closures are like structs containing a field for each captured variable, but closures also have an -associated function. Supporting closure function calls required some extra machinery to get the -necessary information from the compiler, but it is all supported except for one edge case on my todo -list\footnote{The edge case is calling a closure that takes a reference to its captures via a -closure interface that passes the captures by value.}. +Calls to closures are also supported with the exception of one edge case\footnote{Calling a closure +that takes a reference to its captures via a closure interface that passes the captures by value is +not yet supported.}. The value part of a closure that holds the captured variables is handled as an +aggregate and the function call part is mostly the same as a trait method call, but with the added +complication that closures use a separate calling convention within the compiler. \subsubsection{Function pointers} @@ -388,19 +387,19 @@ \subsubsection{Function pointers} \subsubsection{Intrinsics} -To support unsafe code, and in particular the unsafe code used to implement Rust's standard library, -it became clear that Miri would have to support calls to compiler -intrinsics\footnote{\href{https://doc.rust-lang.org/stable/std/intrinsics/index.html}{Rust -intrinsics documentation}}. Intrinsics are function calls which cause the Rust compiler to produce -special-purpose code instead of a regular function call. Miri simply recognizes intrinsic calls by -their unique ABI\footnote{Application Binary Interface, which defines calling conventions. Includes -``C'', ``Rust'', and ``rust-intrinsic''.} and name and runs special purpose code to handle them. +To support unsafe code, and in particular to support Rust's standard library, it became clear that +Miri would have to support calls to compiler +intrinsics\footnote{\url{https://doc.rust-lang.org/stable/std/intrinsics/index.html}}. Intrinsics +are function calls which cause the Rust compiler to produce special-purpose code instead of a +regular function call. Miri simply recognizes intrinsic calls by their unique +ABI\footnote{Application Binary Interface, which defines calling conventions. Includes ``C'', +``Rust'', and ``rust-intrinsic''.} and name and runs special-purpose code to handle them. An example of an important intrinsic is \rust{size_of} which will cause Miri to write the size of the type in question to the return value location. The Rust standard library uses intrinsics heavily -to implement various data structures, so this was a major step toward supporting them. So far, I've -been implementing intrinsics on a case-by-case basis as I write test cases which require missing -ones, so I haven't yet exhaustively implemented them all. +to implement various data structures, so this was a major step toward supporting them. Intrinsics +have been implemented on a case-by-case basis as tests which required them were written, and not all +intrinsics are supported yet. \subsubsection{Generic function calls} @@ -414,17 +413,17 @@ \subsubsection{Generic function calls} fn some(t: T) -> Option { Some(t) } \end{minted} -\ldots{} Miri needs to know how many bytes to copy from the argument to the return value, based on -the size of \rust{T}. If we call \rust{some(10i32)} Miri will execute \rust{some} knowing that +\ldots{}Miri needs to know the size of \rust{T} to copy the right amount of bytes from the argument +to the return value. If we call \rust{some(10i32)} Miri will execute \rust{some} knowing that \rust{T = i32} and generate a representation for \rust{Option}. -Miri currently does this monomorphization on-demand, or lazily, unlike the Rust back-end which does -it all ahead of time. +Miri currently does this monomorphization lazily on-demand unlike the Rust back-end which does it +all ahead of time. \subsection{Heap allocations} The next piece of the puzzle for supporting interesting programs (and the standard library) was heap -allocations. There are two main interfaces for heap allocation in Rust, the built-in \rust{Box} +allocations. There are two main interfaces for heap allocation in Rust: the built-in \rust{Box} rvalue in MIR and a set of C ABI foreign functions including \rust{__rust_allocate}, \rust{__rust_reallocate}, and \rust{__rust_deallocate}. These correspond approximately to \mintinline{c}{malloc}, \mintinline{c}{realloc}, and \mintinline{c}{free} in C. @@ -435,8 +434,8 @@ \subsection{Heap allocations} The allocator functions, which are used to implement things like Rust's standard \rust{Vec} type, were a bit trickier. Rust declares them as \rust{extern "C" fn} so that different allocator -libraries can be linked in at the user's option. Since Miri doesn't actually support FFI and we want -full control of allocations for safety, Miri ``cheats'' and recognizes these allocator function in +libraries can be linked in at the user's option. Since Miri doesn't actually support FFI and wants +full control of allocations for safety, it ``cheats'' and recognizes these allocator functions in essentially the same way it recognizes compiler intrinsics. Then, a call to \rust{__rust_allocate} simply creates another abstract allocation with the requested size and \rust{__rust_reallocate} grows one. @@ -446,28 +445,28 @@ \subsection{Heap allocations} \subsection{Destructors} -When values go out of scope that ``own'' some resource, like a heap allocation or file handle, Rust -inserts \emph{drop glue} that calls the user-defined destructor for the type if it exists, and then -drops all of the subfields. Destructors for types like \rust{Box} and \rust{Vec} deallocate -heap memory. +When a value which ``owns'' some resource (like a heap allocation or file handle) goes out of scope, +Rust inserts \emph{drop glue} that calls the user-defined destructor for the type if it has one, and +then drops all of the subfields. Destructors for types like \rust{Box} and \rust{Vec} +deallocate heap memory. Miri doesn't yet support calling user-defined destructors, but it has most of the machinery in place -to do so already and it's next on my to-do list. There \emph{is} support for dropping \rust{Box} -types, including deallocating their associated allocations. This is enough to properly execute the -dangling pointer example in \autoref{sec:deterministic}. +to do so already. There \emph{is} support for dropping \rust{Box} types, including deallocating +their associated allocations. This is enough to properly execute the dangling pointer example in +\autoref{sec:deterministic}. \subsection{Constants} Only basic integer, boolean, string, and byte-string literals are currently supported. Evaluating more complicated constant expressions in their current form would be a somewhat pointless exercise -for Miri. Instead, we should lower constant expressions to MIR so Miri can run them directly. (This -is precisely what would be done to use Miri as the actual constant evaluator.) +for Miri. Instead, we should lower constant expressions to MIR so Miri can run them directly, which +is precisely what would need be done to use Miri as the compiler's constant evaluator. \subsection{Static variables} -While it would be invalid to write to static (i.e.\ global) variables in Miri executions, it would -probably be fine to allow reads. However, Miri doesn't currently support them and they would need -support similar to constants. +Miri doesn't currently support statics, but they would need support similar to constants. Also note +that while it would be invalid to write to static (i.e.\ global) variables in Miri executions, it +would probably be fine to allow reads. \subsection{Standard library} @@ -486,7 +485,7 @@ \subsection{Standard library} pointer} all seem to work. I've also tested using the shared smart pointer types with \rust{Cell} and \rust{RefCell}\footnote{\href{https://doc.rust-lang.org/stable/std/cell/index.html}{Rust documentation for cell types}} for internal mutability, and that works as well, although -\rust{RefCell} can't ever be borrowed twice until I implement destructor calls, since its destructor +\rust{RefCell} can't ever be borrowed twice until I implement destructor calls, since a destructor is what releases the borrow. But the standard library collection I spent the most time on was \rust{Vec}, the standard @@ -509,35 +508,40 @@ \subsection{Standard library} let mut v: Vec = Vec::with_capacity(2); - // A: 00 00 00 00 02 00 00 00 00 00 00 00 - // └───(B)───┘ - // B: __ __ + // v: 00 00 00 00 02 00 00 00 00 00 00 00 + // └─(data)──┘ + // data: __ __ v.push(1); - // A: 00 00 00 00 02 00 00 00 01 00 00 00 - // └───(B)───┘ - // B: 01 __ + // v: 00 00 00 00 02 00 00 00 01 00 00 00 + // └─(data)──┘ + // data: 01 __ v.push(2); - // A: 00 00 00 00 02 00 00 00 02 00 00 00 - // └───(B)───┘ - // B: 01 02 + // v: 00 00 00 00 02 00 00 00 02 00 00 00 + // └─(data)──┘ + // data: 01 02 v.push(3); - // A: 00 00 00 00 04 00 00 00 03 00 00 00 - // └───(B)───┘ - // B: 01 02 03 __ + // v: 00 00 00 00 04 00 00 00 03 00 00 00 + // └─(data)──┘ + // data: 01 02 03 __ \end{minted} \caption{\rust{Vec} example on 32-bit little-endian} \label{fig:vec} \end{figure} -You can even do unsafe things with \rust{Vec} like \rust{v.set_len(10)} or -\rust{v.get_unchecked(2)}, but if you do these things carefully in a way that doesn't cause any -undefined behaviour (just like when you write unsafe code for regular Rust), then Miri can handle it -all. But if you do slip up, Miri will error out with an appropriate message (see +Miri supports unsafe operations on \rust{Vec} like \rust{v.set_len(10)} or +\rust{v.get_unchecked(2)}, provided that such calls do no invoke undefined behaviour. If a call +\emph{does} invoke undefined behaviour, Miri will abort with an appropriate error message (see \autoref{fig:vec-error}). +% You can even do unsafe things with \rust{Vec} like \rust{v.set_len(10)} or +% \rust{v.get_unchecked(2)}, but if you do these things carefully in a way that doesn't cause any +% undefined behaviour (just like when you write unsafe code for regular Rust), then Miri can handle it +% all. But if you do slip up, Miri will error out with an appropriate message (see +% \autoref{fig:vec-error}). + \begin{figure}[t] \begin{minted}[autogobble]{rust} fn out_of_bounds() -> u8 { @@ -560,6 +564,20 @@ \subsection{Standard library} \label{fig:vec-error} \end{figure} +\newpage + +Here is one final code sample Miri can execute that demonstrates many features at once, including +vectors, heap allocation, iterators, closures, raw pointers, and math: + +\begin{minted}[autogobble]{rust} + let x: u8 = vec![1, 2, 3, 4] + .into_iter() + .map(|x| x * x) + .fold(0, |x, y| x + y); + // x: 1e (that is, the hex value + // 0x1e = 30 = 1 + 4 + 9 + 16) +\end{minted} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Future directions} @@ -569,37 +587,38 @@ \subsection{Finishing the implementation} There are a number of pressing items on my to-do list for Miri, including: \begin{itemize} - \item Destructors and \rust{__rust_deallocate}. + \item A much more comprehensive and automated test suite. + \item User-defined destructor calls. \item Non-trivial casts between primitive types like integers and pointers. \item Handling statics and global memory. \item Reporting errors for all undefined behaviour.\footnote{\href{https://doc.rust-lang.org/reference.html\#behavior-considered-undefined}{The Rust reference on what is considered undefined behaviour}} \item Function pointers. \item Accounting for target machine primitive type alignment and endianness. - \item Optimizing stuff (undefined byte masks, tail-calls). + \item Optimizations (undefined byte masks, tail-calls). \item Benchmarking Miri vs. unoptimized Rust. \item Various \texttt{TODO}s and \texttt{FIXME}s left in the code. - \item Getting a version of Miri into rustc for real. + \item Integrating into the compiler proper. \end{itemize} -\subsection{Alternative applications} +\subsection{Future projects} -Other possible uses for Miri include: +Other possible Miri-related projects include: \begin{itemize} + \item A read-eval-print-loop (REPL) for Rust, which may be easier to implement on top of Miri than + the usual LLVM back-end. \item A graphical or text-mode debugger that steps through MIR execution one statement at a time, for figuring out why some compile-time execution is raising an error or simply learning how Rust works at a low level. - \item A read-eval-print-loop (REPL) for Rust, which may be easier to implement on top of Miri than - the usual LLVM back-end. - \item An extended version of Miri developed apart from the purpose of compile-time execution that - is able to run foreign functions from C/C++ and generally have full access to the operating - system. Such a version of Miri could be used to more quickly prototype changes to the Rust - language that would otherwise require changes to the LLVM back-end. + \item A less restricted version of Miri that is able to run foreign functions from C/C++ and + generally has full access to the operating system. Such an interpreter could be used to more + quickly prototype changes to the Rust language that would otherwise require changes to the LLVM + back-end. \item Unit-testing the compiler by comparing the results of Miri's execution against the results of LLVM-compiled machine code's execution. This would help to guarantee that compile-time execution works the same as runtime execution. - \item Some kind of symbolic evaluator that examines multiple possible code paths at once to - determine if undefined behaviour could be observed on any of them. + \item Some kind of Miri-based symbolic evaluator that examines multiple possible code paths at + once to determine if undefined behaviour could be observed on any of them. \end{itemize} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -607,34 +626,35 @@ \subsection{Alternative applications} \section{Final thoughts} Writing an interpreter which models values of varying sizes, stack and heap allocation, unsafe -memory operations, and more requires some unconventional techniques compared to typical -interpreters. However, aside from the somewhat complicated abstract memory model, making Miri work -was primarily a software engineering problem, and not a particularly tricky one. This is a testament -to MIR's suitability as an intermediate representation for Rust---removing enough unnecessary -abstraction to keep it simple. For example, Miri doesn't even need to know that there are different -kind of loops, or how to match patterns in a \rust{match} expression. +memory operations, and more requires some unconventional techniques compared to conventional +interpreters targeting dynamically-typed languages. However, aside from the somewhat complicated +abstract memory model, making Miri work was primarily a software engineering problem, and not a +particularly tricky one. This is a testament to MIR's suitability as an intermediate representation +for Rust---removing enough unnecessary abstraction to keep it simple. For example, Miri doesn't even +need to know that there are different kinds of loops, or how to match patterns in a \rust{match} +expression. Another advantage to targeting MIR is that any new features at the syntax-level or type-level generally require little to no change in Miri. For example, when the new ``question mark'' syntax for error handling\footnote{ \href{https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md} {Question mark syntax RFC}} -was added to rustc, Miri also supported it the same day with no change. When specialization\footnote{ +was added to rustc, Miri required no change to support it. +When specialization\footnote{ \href{https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md} {Specialization RFC}} was added, Miri supported it with just minor changes to trait method lookup. Of course, Miri also has limitations. The inability to execute FFI and inline assembly reduces the amount of Rust programs Miri could ever execute. The good news is that in the constant evaluator, -FFI can be stubbed out in cases where it makes sense, like I did with \rust{__rust_allocate}, and -for Miri outside of the compiler it may be possible to use libffi to call C functions from the -interpreter. - -In conclusion, Miri was a surprisingly effective project, and a lot of fun to implement. There were -times where I ended up supporting Rust features I didn't even intend to while I was adding support -for some other feature, due to the design of MIR collapsing features at the source level into fewer -features at the MIR level. I am excited to work with the compiler team going forward to try to make -Miri useful for constant evaluation in Rust. +FFI can be stubbed out in cases where it makes sense, like I did with \rust{__rust_allocate}. For a +version of Miri not intended for constant evaluation, it may be possible to use libffi to call C +functions from the interpreter. + +In conclusion, Miri is a surprisingly effective project, and a lot of fun to implement. Due to MIR's +tendency to collapse multiple source-level features into one, I often ended up supporting features I +hadn't explicitly intended to. I am excited to work with the compiler team going forward to try to +make Miri useful for constant evaluation in Rust. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From e0f7d8f38c549454bc1f33cfbca33c8bcf0dc075 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 06:15:02 -0600 Subject: [PATCH 0233/1096] report: Credit where it's due. --- tex/report/miri-report.tex | 1 + 1 file changed, 1 insertion(+) diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 82efc73605caa..6d2686c138f7d 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -664,5 +664,6 @@ \section{Thanks} incessant questions on IRC, to Niko Matsakis for coming up with the idea for Miri and supporting my desire to work with the Rust compiler, and to my research supervisor Christopher Dutchyn. Thanks also to everyone else on the compiler team and on Mozilla IRC who helped me figure stuff out. +Finally, thanks to Daniel Keep and everyone else who helped fix my numerous writing mistakes. \end{document} From 4867051c6f68626b9ce5b399234067959a9b27df Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 07:32:32 -0600 Subject: [PATCH 0234/1096] readme: Add build and run instructions. --- README.md | 42 +++++++++++++++++++++++++++++++++++------- test/arrays.rs | 1 + test/bools.rs | 1 + test/c_enums.rs | 1 + test/calls.rs | 1 + test/closures.rs | 1 + test/errors.rs | 1 + test/heap.rs | 1 + test/ints.rs | 1 + test/loops.rs | 1 + test/pointers.rs | 1 + test/products.rs | 1 + test/specialization.rs | 1 + test/std.rs | 1 + test/strings.rs | 1 + test/sums.rs | 1 + test/trivial.rs | 1 + test/vecs.rs | 1 + 18 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4c591dff015e3..25bb64af80c2e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,35 @@ -# miri +# Miri -An experimental interpreter for [Rust][rust]'s [mid-level -intermediate representation][mir] (MIR). This project is part of my course work -for an undergraduate research course at the [University of Saskatchewan][usask]. +An experimental interpreter for [Rust][rust]'s [mid-level intermediate +representation][mir] (MIR). This project began as a part of my course work for +an undergraduate research course at the [University of Saskatchewan][usask]. -[rust]: https://www.rust-lang.org/ -[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md -[usask]: https://www.usask.ca/ +## Download Rust nightly + +I currently recommend that you install [multirust][multirust] and then use it to +install the current rustc nightly version that works with Miri: + +```sh +multirust update nightly-2016-04-05 +``` + +## Build + +```sh +multirust run nightly-2016-04-05 cargo build +``` + +## Run a test + +```sh +multirust run nightly-2016-04-05 cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly-2016-04-05 + test/filename.rs +``` + +If you installed without using multirust, you'll need to adjust the command to +run your cargo and set the `sysroot` to the directory where your rust compiler +is installed (`$sysroot/bin/rustc` should be a valid path). ## License @@ -21,3 +44,8 @@ Licensed under either of Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions. + +[rust]: https://www.rust-lang.org/ +[mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md +[usask]: https://www.usask.ca/ +[multirust]: https://github.com/brson/multirust diff --git a/test/arrays.rs b/test/arrays.rs index 835e09780fc48..05925fa98cfb4 100644 --- a/test/arrays.rs +++ b/test/arrays.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/bools.rs b/test/bools.rs index f8e6c2d89d22a..699409e17a4d6 100644 --- a/test/bools.rs +++ b/test/bools.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/c_enums.rs b/test/c_enums.rs index 190e82ac01c5e..1da35d64b8409 100644 --- a/test/c_enums.rs +++ b/test/c_enums.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/calls.rs b/test/calls.rs index 2cda1c3bddf1b..0e9199f969df8 100644 --- a/test/calls.rs +++ b/test/calls.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/closures.rs b/test/closures.rs index bff172d45b479..9b6e296664877 100644 --- a/test/closures.rs +++ b/test/closures.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/errors.rs b/test/errors.rs index a488d7acb433f..e123538f6ce0d 100644 --- a/test/errors.rs +++ b/test/errors.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/heap.rs b/test/heap.rs index 05efc56f0f33e..268b4121dcbdb 100644 --- a/test/heap.rs +++ b/test/heap.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] diff --git a/test/ints.rs b/test/ints.rs index 718fa17a74180..b790afe3b54f1 100644 --- a/test/ints.rs +++ b/test/ints.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/loops.rs b/test/loops.rs index b59c813a423e0..008c6e6ad8293 100644 --- a/test/loops.rs +++ b/test/loops.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/pointers.rs b/test/pointers.rs index 9e7e217ec5c72..ab1226c759b3a 100644 --- a/test/pointers.rs +++ b/test/pointers.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/products.rs b/test/products.rs index ba72bfb52a704..2fad1daae068a 100644 --- a/test/products.rs +++ b/test/products.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/specialization.rs b/test/specialization.rs index ac510ec4cb4da..a7323431f4cc8 100644 --- a/test/specialization.rs +++ b/test/specialization.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute, specialization)] #![allow(dead_code, unused_attributes)] diff --git a/test/std.rs b/test/std.rs index 36f9ca6a1ff02..55e801b93d03e 100644 --- a/test/std.rs +++ b/test/std.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] diff --git a/test/strings.rs b/test/strings.rs index 7db84d35cd52f..6d27153aaa897 100644 --- a/test/strings.rs +++ b/test/strings.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/sums.rs b/test/sums.rs index 67257050364c9..209629eabbf82 100644 --- a/test/sums.rs +++ b/test/sums.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/trivial.rs b/test/trivial.rs index 99a1ef06186a1..9761e94792950 100644 --- a/test/trivial.rs +++ b/test/trivial.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] diff --git a/test/vecs.rs b/test/vecs.rs index a1894505fb969..63757947b0a1f 100644 --- a/test/vecs.rs +++ b/test/vecs.rs @@ -1,3 +1,4 @@ +#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] From 8cac01ae66a8eebca4711a5771ad161cb8f498ba Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 07:36:54 -0600 Subject: [PATCH 0235/1096] readme: Fix shell syntax. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 25bb64af80c2e..afe251f92ad03 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ multirust run nightly-2016-04-05 cargo build ```sh multirust run nightly-2016-04-05 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-04-05 + --sysroot $HOME/.multirust/toolchains/nightly-2016-04-05 \ test/filename.rs ``` From 6abfa56b20009546be67837cc7cd92c9c13f5951 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 14 Apr 2016 00:01:00 +0200 Subject: [PATCH 0236/1096] Update to Rust Nightly 2016-04-11 --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 850ddf8aa18b0..a1db521760fbe 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,7 +1,7 @@ use arena::TypedArena; use rustc::infer; use rustc::middle::const_val; -use rustc::middle::def_id::DefId; +use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::traits::{self, ProjectionMode}; From 3ae75f3bb6641ae96e47b6028c921e4a6e81cc00 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 18:41:37 -0600 Subject: [PATCH 0237/1096] readme: Add links to presentation and report PDFs. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index afe251f92ad03..f9c65ebe0ba6d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Miri +[[slides](https://solson.me/miri-slides.pdf)] +[[report](https://solson.me/miri-report.pdf)] + An experimental interpreter for [Rust][rust]'s [mid-level intermediate representation][mir] (MIR). This project began as a part of my course work for an undergraduate research course at the [University of Saskatchewan][usask]. From 539ded20af7b5647c785f42f8ff557d1dd397baf Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 13 Apr 2016 18:47:03 -0600 Subject: [PATCH 0238/1096] readme: Rewording. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9c65ebe0ba6d..0e93046ed635c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [[report](https://solson.me/miri-report.pdf)] An experimental interpreter for [Rust][rust]'s [mid-level intermediate -representation][mir] (MIR). This project began as a part of my course work for -an undergraduate research course at the [University of Saskatchewan][usask]. +representation][mir] (MIR). This project began as part of my work for the +undergraduate research course at the [University of Saskatchewan][usask]. ## Download Rust nightly From 4b9d141e97ef040453fbd97a40839b8739126af4 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 14 Apr 2016 10:21:32 +0200 Subject: [PATCH 0239/1096] Readme: Newer Nightly Version, Mention Rustup --- README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index afe251f92ad03..c84d5ba2037fc 100644 --- a/README.md +++ b/README.md @@ -10,26 +10,32 @@ I currently recommend that you install [multirust][multirust] and then use it to install the current rustc nightly version that works with Miri: ```sh -multirust update nightly-2016-04-05 +multirust update nightly-2016-04-11 ``` ## Build ```sh -multirust run nightly-2016-04-05 cargo build +multirust run nightly-2016-04-11 cargo build ``` ## Run a test ```sh -multirust run nightly-2016-04-05 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-04-05 \ +multirust run nightly-2016-04-11 cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly-2016-04-11 \ test/filename.rs ``` -If you installed without using multirust, you'll need to adjust the command to -run your cargo and set the `sysroot` to the directory where your rust compiler -is installed (`$sysroot/bin/rustc` should be a valid path). +If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), +the `sysroot` path will also include your build target (e.g. +`$HOME/.multirust/toolchains/nightly-2016-04-11-x86_64-apple-darwin`). You can +see the current toolchain's directory by running `rustup which cargo` (ignoring +the trailing `/bin/cargo). + +If you installed without using multirust or rustup, you'll need to adjust the +command to run your cargo and set the `sysroot` to the directory where your +Rust compiler is installed (`$sysroot/bin/rustc` should be a valid path). ## License @@ -49,3 +55,4 @@ additional terms or conditions. [mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md [usask]: https://www.usask.ca/ [multirust]: https://github.com/brson/multirust +[rustup]: https://www.rustup.rs \ No newline at end of file From f6f393996b5d6f0ba010faaa55f41564ceda8dee Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 14 Apr 2016 12:31:42 +0200 Subject: [PATCH 0240/1096] Add Compile Test --- tests/compile-test.rs | 37 ++++++++++++++++++++++ {test => tests/run-pass}/arrays.rs | 0 {test => tests/run-pass}/bools.rs | 0 {test => tests/run-pass}/c_enums.rs | 0 {test => tests/run-pass}/calls.rs | 0 {test => tests/run-pass}/closures.rs | 0 {test => tests/run-pass}/errors.rs | 0 {test => tests/run-pass}/heap.rs | 0 {test => tests/run-pass}/ints.rs | 0 {test => tests/run-pass}/loops.rs | 0 {test => tests/run-pass}/pointers.rs | 0 {test => tests/run-pass}/products.rs | 0 {test => tests/run-pass}/specialization.rs | 0 {test => tests/run-pass}/std.rs | 0 {test => tests/run-pass}/strings.rs | 0 {test => tests/run-pass}/sums.rs | 0 {test => tests/run-pass}/trivial.rs | 0 {test => tests/run-pass}/vecs.rs | 0 18 files changed, 37 insertions(+) create mode 100644 tests/compile-test.rs rename {test => tests/run-pass}/arrays.rs (100%) rename {test => tests/run-pass}/bools.rs (100%) rename {test => tests/run-pass}/c_enums.rs (100%) rename {test => tests/run-pass}/calls.rs (100%) rename {test => tests/run-pass}/closures.rs (100%) rename {test => tests/run-pass}/errors.rs (100%) rename {test => tests/run-pass}/heap.rs (100%) rename {test => tests/run-pass}/ints.rs (100%) rename {test => tests/run-pass}/loops.rs (100%) rename {test => tests/run-pass}/pointers.rs (100%) rename {test => tests/run-pass}/products.rs (100%) rename {test => tests/run-pass}/specialization.rs (100%) rename {test => tests/run-pass}/std.rs (100%) rename {test => tests/run-pass}/strings.rs (100%) rename {test => tests/run-pass}/sums.rs (100%) rename {test => tests/run-pass}/trivial.rs (100%) rename {test => tests/run-pass}/vecs.rs (100%) diff --git a/tests/compile-test.rs b/tests/compile-test.rs new file mode 100644 index 0000000000000..c7cdb974caea7 --- /dev/null +++ b/tests/compile-test.rs @@ -0,0 +1,37 @@ +use std::{env, fs}; +use std::process::{Command, Output}; + +fn run_miri(file: &str, sysroot: &str) -> Output { + Command::new("cargo") + .args(&["run", "--", "--sysroot", sysroot, file]) + .output() + .unwrap_or_else(|e| panic!("failed to execute process: {}", e)) +} + +#[test] +fn run_pass() { + let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + + let test_files = fs::read_dir("./tests/run-pass/") + .expect("Can't read `run-pass` directory") + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.clone() + .file_type() + .map(|x| x.is_file()) + .unwrap_or(false) + }) + .filter_map(|entry| entry.path().to_str().map(|x| x.to_string())); + + for file in test_files { + println!("{}: compile test running", file); + + let test_run = run_miri(&file, &sysroot); + + if test_run.status.code().unwrap_or(-1) != 0 { + println!("{}: error {:?}", file, test_run); + } else { + println!("{}: ok", file); + } + } +} diff --git a/test/arrays.rs b/tests/run-pass/arrays.rs similarity index 100% rename from test/arrays.rs rename to tests/run-pass/arrays.rs diff --git a/test/bools.rs b/tests/run-pass/bools.rs similarity index 100% rename from test/bools.rs rename to tests/run-pass/bools.rs diff --git a/test/c_enums.rs b/tests/run-pass/c_enums.rs similarity index 100% rename from test/c_enums.rs rename to tests/run-pass/c_enums.rs diff --git a/test/calls.rs b/tests/run-pass/calls.rs similarity index 100% rename from test/calls.rs rename to tests/run-pass/calls.rs diff --git a/test/closures.rs b/tests/run-pass/closures.rs similarity index 100% rename from test/closures.rs rename to tests/run-pass/closures.rs diff --git a/test/errors.rs b/tests/run-pass/errors.rs similarity index 100% rename from test/errors.rs rename to tests/run-pass/errors.rs diff --git a/test/heap.rs b/tests/run-pass/heap.rs similarity index 100% rename from test/heap.rs rename to tests/run-pass/heap.rs diff --git a/test/ints.rs b/tests/run-pass/ints.rs similarity index 100% rename from test/ints.rs rename to tests/run-pass/ints.rs diff --git a/test/loops.rs b/tests/run-pass/loops.rs similarity index 100% rename from test/loops.rs rename to tests/run-pass/loops.rs diff --git a/test/pointers.rs b/tests/run-pass/pointers.rs similarity index 100% rename from test/pointers.rs rename to tests/run-pass/pointers.rs diff --git a/test/products.rs b/tests/run-pass/products.rs similarity index 100% rename from test/products.rs rename to tests/run-pass/products.rs diff --git a/test/specialization.rs b/tests/run-pass/specialization.rs similarity index 100% rename from test/specialization.rs rename to tests/run-pass/specialization.rs diff --git a/test/std.rs b/tests/run-pass/std.rs similarity index 100% rename from test/std.rs rename to tests/run-pass/std.rs diff --git a/test/strings.rs b/tests/run-pass/strings.rs similarity index 100% rename from test/strings.rs rename to tests/run-pass/strings.rs diff --git a/test/sums.rs b/tests/run-pass/sums.rs similarity index 100% rename from test/sums.rs rename to tests/run-pass/sums.rs diff --git a/test/trivial.rs b/tests/run-pass/trivial.rs similarity index 100% rename from test/trivial.rs rename to tests/run-pass/trivial.rs diff --git a/test/vecs.rs b/tests/run-pass/vecs.rs similarity index 100% rename from test/vecs.rs rename to tests/run-pass/vecs.rs From 5eb4ab22bf0a85e201a77d659304ef8b10825058 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 14 Apr 2016 00:10:20 +0200 Subject: [PATCH 0241/1096] Add EditorConfig --- .editorconfig | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000..3c1f41bdcca6c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.rs] +indent_style = space +indent_size = 4 + +[*.toml] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false From 926bbba459d5f7cd7d15aceba8fe1fbfb069a94b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 14 Apr 2016 17:38:52 -0600 Subject: [PATCH 0242/1096] Fix over-long bitshift on 32-bit hosts. Fixes #4. (Hopefully.) --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a1db521760fbe..e52aaf37f2e0a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1001,7 +1001,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { variants.push(fields); } - let discr_size = match variants.len() { + let discr_size = match variants.len() as u64 { n if n <= 1 => 0, n if n <= 1 << 8 => 1, n if n <= 1 << 16 => 2, From 18737f542bc8d5f46b0ac209d99be6cb66a493da Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Thu, 14 Apr 2016 00:10:31 +0200 Subject: [PATCH 0243/1096] Add Travis Config --- .travis.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000..8a35c59bfa5f4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: rust +rust: +- nightly-2016-04-11 +- nightly +matrix: + allow_failures: + - rust: nightly +before_script: +- | + pip install 'travis-cargo<0.2' --user && + export PATH=$HOME/.local/bin:$PATH +script: +- | + travis-cargo build && + env RUST_SYSROOT=$HOME/rust RUST_TEST_NOCAPTURE=1 travis-cargo test +notifications: + email: + on_success: never From 84f21584ea68c36e30935216b7ed18523e0bb861 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 15 Apr 2016 03:16:35 -0600 Subject: [PATCH 0244/1096] Fix drop fill checking on 32-bit hosts. --- src/interpreter.rs | 5 +++-- src/memory.rs | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index e52aaf37f2e0a..91682207ed638 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -393,8 +393,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { try!(self.memory.deallocate(contents_ptr)); } Err(EvalError::ReadBytesAsPointer) => { - let possible_drop_fill = try!(self.memory.read_usize(ptr)); - if possible_drop_fill == mem::POST_DROP_U64 { + let size = self.memory.pointer_size; + let possible_drop_fill = try!(self.memory.read_bytes(ptr, size)); + if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { return Ok(()); } else { return Err(EvalError::ReadBytesAsPointer); diff --git a/src/memory.rs b/src/memory.rs index 21ab8e36c563f..b99dae15970f8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -284,6 +284,10 @@ impl Memory { Ok(()) } + pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + self.get_bytes(ptr, size) + } + pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { let bytes = try!(self.get_bytes_mut(ptr, src.len())); bytes.clone_from_slice(src); From e81d88d236f4780dd6c09267fbb56710eda196a2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 15 Apr 2016 03:28:10 -0600 Subject: [PATCH 0245/1096] Use 8-byte pointers on 32-bit hosts for now. This will be target-dependent and host-independent eventually. --- src/memory.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index b99dae15970f8..14aa4f9d799ec 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -102,8 +102,9 @@ impl Memory { alloc_map: HashMap::new(), next_id: AllocId(0), - // TODO(tsion): Should this be host's or target's usize? - pointer_size: mem::size_of::(), + // FIXME(tsion): This should work for both 4 and 8, but it currently breaks some things + // when set to 4. + pointer_size: 8, } } From 9fd2b47c27a3dca4da8e6e3f8162f48cbd5b60cb Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Fri, 15 Apr 2016 16:50:47 +0200 Subject: [PATCH 0246/1096] Fix Typo in Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 74cd0e4e4c6b3..655de829b9ca8 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), the `sysroot` path will also include your build target (e.g. `$HOME/.multirust/toolchains/nightly-2016-04-11-x86_64-apple-darwin`). You can see the current toolchain's directory by running `rustup which cargo` (ignoring -the trailing `/bin/cargo). +the trailing `/bin/cargo`). If you installed without using multirust or rustup, you'll need to adjust the command to run your cargo and set the `sysroot` to the directory where your @@ -58,4 +58,4 @@ additional terms or conditions. [mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md [usask]: https://www.usask.ca/ [multirust]: https://github.com/brson/multirust -[rustup]: https://www.rustup.rs \ No newline at end of file +[rustup]: https://www.rustup.rs From 52775ce2d7845e69adee814c33b78670504c76d3 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Fri, 15 Apr 2016 16:50:59 +0200 Subject: [PATCH 0247/1096] Add Build Status to Readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 655de829b9ca8..5a4e274317031 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ An experimental interpreter for [Rust][rust]'s [mid-level intermediate representation][mir] (MIR). This project began as part of my work for the undergraduate research course at the [University of Saskatchewan][usask]. +[![Build Status](https://travis-ci.org/tsion/miri.svg?branch=master)](https://travis-ci.org/tsion/miri) + ## Download Rust nightly I currently recommend that you install [multirust][multirust] and then use it to From a85d876bcd87fbd41eb631f83b657d4105c20ac4 Mon Sep 17 00:00:00 2001 From: Pascal Hertleif Date: Fri, 15 Apr 2016 16:50:36 +0200 Subject: [PATCH 0248/1096] Fix Travis Config for Nightly --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8a35c59bfa5f4..598ea9852e56e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,11 @@ before_script: script: - | travis-cargo build && - env RUST_SYSROOT=$HOME/rust RUST_TEST_NOCAPTURE=1 travis-cargo test + env RUST_SYSROOT=$HOME/rust travis-cargo test notifications: email: on_success: never +env: + global: + - RUST_TEST_NOCAPTURE=1 + - TRAVIS_CARGO_NIGHTLY_FEATURE="" From 211c12a1d05b35b9353ec35b9d5d894369cad5f0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 22 Apr 2016 10:34:14 +0200 Subject: [PATCH 0249/1096] use compiletest_rs --- Cargo.lock | 14 ++++++++ Cargo.toml | 3 ++ src/bin/miri.rs | 3 +- tests/{run-pass => compile-fail}/errors.rs | 15 +++++---- tests/compile-test.rs | 37 ---------------------- tests/compiletest.rs | 23 ++++++++++++++ tests/run-pass/arrays.rs | 3 +- tests/run-pass/bools.rs | 3 +- tests/run-pass/c_enums.rs | 3 +- tests/run-pass/calls.rs | 3 +- tests/run-pass/closures.rs | 3 +- tests/run-pass/heap.rs | 3 +- tests/run-pass/ints.rs | 3 +- tests/run-pass/loops.rs | 3 +- tests/run-pass/pointers.rs | 3 +- tests/run-pass/products.rs | 3 +- tests/run-pass/specialization.rs | 3 +- tests/run-pass/std.rs | 3 +- tests/run-pass/strings.rs | 3 +- tests/run-pass/sums.rs | 3 +- tests/run-pass/trivial.rs | 3 +- tests/run-pass/vecs.rs | 3 +- 22 files changed, 81 insertions(+), 62 deletions(-) rename tests/{run-pass => compile-fail}/errors.rs (65%) delete mode 100644 tests/compile-test.rs create mode 100644 tests/compiletest.rs diff --git a/Cargo.lock b/Cargo.lock index 8fc715429970f..4827e7fd8a6a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10,3 +11,16 @@ name = "byteorder" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "compiletest_rs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/Cargo.toml b/Cargo.toml index a369f6070c86b..9fa9f145c72b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ name = "miri" [dependencies] byteorder = "0.4.2" + +[dev-dependencies] +compiletest_rs = "0.1.1" diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8efa1a6864def..ebfb379d31348 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -6,14 +6,13 @@ extern crate rustc_driver; use miri::interpreter; use rustc::session::Session; -use rustc_driver::{driver, CompilerCalls, Compilation}; +use rustc_driver::{driver, CompilerCalls}; struct MiriCompilerCalls; impl<'a> CompilerCalls<'a> for MiriCompilerCalls { fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { let mut control = driver::CompileController::basic(); - control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); diff --git a/tests/run-pass/errors.rs b/tests/compile-fail/errors.rs similarity index 65% rename from tests/run-pass/errors.rs rename to tests/compile-fail/errors.rs index e123538f6ce0d..d971724f6878b 100644 --- a/tests/run-pass/errors.rs +++ b/tests/compile-fail/errors.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -9,33 +8,33 @@ fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { let ptr: *mut _ = &mut p; *(ptr as *mut u32) = 123; } - *p + *p //~ ERROR: attempted to read undefined bytes } #[miri_run] fn pointers_to_different_allocations_are_unorderable() -> bool { let x: *const u8 = &1; let y: *const u8 = &2; - x < y + x < y //~ ERROR: attempted to do math or a comparison on pointers into different allocations } #[miri_run] fn invalid_bool() -> u8 { let b = unsafe { std::mem::transmute::(2) }; - if b { 1 } else { 2 } + if b { 1 } else { 2 } //~ ERROR: invalid boolean value read } #[miri_run] fn undefined_byte_read() -> u8 { let v: Vec = Vec::with_capacity(10); let undef = unsafe { *v.get_unchecked(5) }; - undef + 1 + undef + 1 //~ ERROR: attempted to read undefined bytes } #[miri_run] fn out_of_bounds_read() -> u8 { let v: Vec = vec![1, 2]; - unsafe { *v.get_unchecked(5) } + unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset outside bounds of allocation } #[miri_run] @@ -44,5 +43,7 @@ fn dangling_pointer_deref() -> i32 { let b = Box::new(42); &*b as *const i32 }; - unsafe { *p } + unsafe { *p } //~ ERROR: dangling pointer was dereferenced } + +fn main() {} diff --git a/tests/compile-test.rs b/tests/compile-test.rs deleted file mode 100644 index c7cdb974caea7..0000000000000 --- a/tests/compile-test.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::{env, fs}; -use std::process::{Command, Output}; - -fn run_miri(file: &str, sysroot: &str) -> Output { - Command::new("cargo") - .args(&["run", "--", "--sysroot", sysroot, file]) - .output() - .unwrap_or_else(|e| panic!("failed to execute process: {}", e)) -} - -#[test] -fn run_pass() { - let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - - let test_files = fs::read_dir("./tests/run-pass/") - .expect("Can't read `run-pass` directory") - .filter_map(|entry| entry.ok()) - .filter(|entry| { - entry.clone() - .file_type() - .map(|x| x.is_file()) - .unwrap_or(false) - }) - .filter_map(|entry| entry.path().to_str().map(|x| x.to_string())); - - for file in test_files { - println!("{}: compile test running", file); - - let test_run = run_miri(&file, &sysroot); - - if test_run.status.code().unwrap_or(-1) != 0 { - println!("{}: error {:?}", file, test_run); - } else { - println!("{}: ok", file); - } - } -} diff --git a/tests/compiletest.rs b/tests/compiletest.rs new file mode 100644 index 0000000000000..224d7f0611ff7 --- /dev/null +++ b/tests/compiletest.rs @@ -0,0 +1,23 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::default_config(); + config.rustc_path = "target/debug/miri".into(); + let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + config.target_rustcflags = Some(format!("--sysroot {}", path)); + config.host_rustcflags = Some(format!("--sysroot {}", path)); + let cfg_mode = mode.parse().ok().expect("Invalid mode"); + + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); + run_mode("run-pass"); +} diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index 05925fa98cfb4..26a4196b1b975 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -33,3 +32,5 @@ fn index() -> i32 { fn array_repeat() -> [u8; 8] { [42; 8] } + +fn main() {} diff --git a/tests/run-pass/bools.rs b/tests/run-pass/bools.rs index 699409e17a4d6..948c09c0fdaab 100644 --- a/tests/run-pass/bools.rs +++ b/tests/run-pass/bools.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -27,3 +26,5 @@ fn match_bool() -> i16 { _ => 0, } } + +fn main() {} diff --git a/tests/run-pass/c_enums.rs b/tests/run-pass/c_enums.rs index 1da35d64b8409..442e58a22a981 100644 --- a/tests/run-pass/c_enums.rs +++ b/tests/run-pass/c_enums.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -20,3 +19,5 @@ fn unsafe_match() -> bool { _ => false, } } + +fn main() {} diff --git a/tests/run-pass/calls.rs b/tests/run-pass/calls.rs index 0e9199f969df8..62ea521d956e5 100644 --- a/tests/run-pass/calls.rs +++ b/tests/run-pass/calls.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -39,3 +38,5 @@ fn cross_crate_fn_call() -> i64 { fn test_size_of() -> usize { ::std::mem::size_of::>() } + +fn main() {} diff --git a/tests/run-pass/closures.rs b/tests/run-pass/closures.rs index 9b6e296664877..3612cfcb4c476 100644 --- a/tests/run-pass/closures.rs +++ b/tests/run-pass/closures.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -37,3 +36,5 @@ fn crazy_closure() -> (i32, i32, i32) { // } // y // } + +fn main() {} diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index 268b4121dcbdb..3cc01e5829bb9 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] @@ -11,3 +10,5 @@ fn make_box() -> Box<(i16, i16)> { fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } + +fn main() {} diff --git a/tests/run-pass/ints.rs b/tests/run-pass/ints.rs index b790afe3b54f1..cc113eaed5923 100644 --- a/tests/run-pass/ints.rs +++ b/tests/run-pass/ints.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -53,3 +52,5 @@ fn match_int_range() -> i64 { _ => 5, } } + +fn main() {} diff --git a/tests/run-pass/loops.rs b/tests/run-pass/loops.rs index 008c6e6ad8293..081e7bb228b4a 100644 --- a/tests/run-pass/loops.rs +++ b/tests/run-pass/loops.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -34,3 +33,5 @@ fn for_loop() -> usize { } sum } + +fn main() {} diff --git a/tests/run-pass/pointers.rs b/tests/run-pass/pointers.rs index ab1226c759b3a..b66aabb2505c4 100644 --- a/tests/run-pass/pointers.rs +++ b/tests/run-pass/pointers.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -58,3 +57,5 @@ fn dangling_pointer() -> *const i32 { let b = Box::new(42); &*b as *const i32 } + +fn main() {} diff --git a/tests/run-pass/products.rs b/tests/run-pass/products.rs index 2fad1daae068a..1d65006ae6f67 100644 --- a/tests/run-pass/products.rs +++ b/tests/run-pass/products.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -30,3 +29,5 @@ fn field_access() -> (i8, i8) { p.x += 5; (p.x, p.y) } + +fn main() {} diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index a7323431f4cc8..b8b101fe84217 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute, specialization)] #![allow(dead_code, unused_attributes)] @@ -18,3 +17,5 @@ impl IsUnit for () { fn specialization() -> (bool, bool) { (i32::is_unit(), <()>::is_unit()) } + +fn main() {} diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index 55e801b93d03e..d9c0e3ca1fe48 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] @@ -44,3 +43,5 @@ fn rc_reference_cycle() -> Loop { fn true_assert() { assert_eq!(1, 1); } + +fn main() {} diff --git a/tests/run-pass/strings.rs b/tests/run-pass/strings.rs index 6d27153aaa897..3d71fa4e09f87 100644 --- a/tests/run-pass/strings.rs +++ b/tests/run-pass/strings.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -21,3 +20,5 @@ fn hello_bytes() -> &'static [u8; 13] { fn hello_bytes_fat() -> &'static [u8] { b"Hello, world!" } + +fn main() {} diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 209629eabbf82..7f92f0d5ffffe 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -53,3 +52,5 @@ fn match_opt_some() -> i8 { fn two_nones() -> (Option, Option) { (None, None) } + +fn main() {} diff --git a/tests/run-pass/trivial.rs b/tests/run-pass/trivial.rs index 9761e94792950..e9d0f694648b2 100644 --- a/tests/run-pass/trivial.rs +++ b/tests/run-pass/trivial.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -10,3 +9,5 @@ fn unit_var() { let x = (); x } + +fn main() {} diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index 63757947b0a1f..325762289c5bd 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -1,4 +1,3 @@ -#![crate_type = "lib"] #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] @@ -36,3 +35,5 @@ fn vec_reallocate() -> Vec { v.push(5); v } + +fn main() {} From ef5fc75c35d6bc66503814907a5e045c744a9456 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 22 Apr 2016 14:38:46 +0200 Subject: [PATCH 0250/1096] various testing improvements --- src/bin/miri.rs | 4 ++- tests/compile-fail/bugs/discriminant_value.rs | 9 ++++++ tests/compile-fail/bugs/memcmp.rs | 11 +++++++ tests/compile-fail/bugs/slice_index.rs | 12 +++++++ tests/compiletest.rs | 1 + tests/run-fail/inception.rs | 32 +++++++++++++++++++ tests/run-pass/arrays.rs | 17 +++++++++- tests/run-pass/bools.rs | 8 ++++- tests/run-pass/c_enums.rs | 5 ++- tests/run-pass/calls.rs | 9 +++++- tests/run-pass/closures.rs | 5 ++- tests/run-pass/heap.rs | 6 +++- tests/run-pass/ints.rs | 11 ++++++- tests/run-pass/loops.rs | 7 +++- tests/run-pass/pointers.rs | 12 ++++++- tests/run-pass/products.rs | 10 +++++- tests/run-pass/specialization.rs | 4 ++- tests/run-pass/std.rs | 8 ++++- tests/run-pass/sums.rs | 14 +++++++- tests/run-pass/vecs.rs | 7 +++- 20 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 tests/compile-fail/bugs/discriminant_value.rs create mode 100644 tests/compile-fail/bugs/memcmp.rs create mode 100644 tests/compile-fail/bugs/slice_index.rs create mode 100644 tests/run-fail/inception.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index ebfb379d31348..a03d0a46ca93b 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,4 +1,5 @@ -#![feature(rustc_private)] +#![feature(rustc_private, custom_attribute)] +#![allow(unused_attributes)] extern crate miri; extern crate rustc; @@ -23,6 +24,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } +#[miri_run] fn main() { let args: Vec = std::env::args().collect(); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); diff --git a/tests/compile-fail/bugs/discriminant_value.rs b/tests/compile-fail/bugs/discriminant_value.rs new file mode 100644 index 0000000000000..f0a487e20df72 --- /dev/null +++ b/tests/compile-fail/bugs/discriminant_value.rs @@ -0,0 +1,9 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// error-pattern:can't handle intrinsic: discriminant_value + +#[miri_run] +fn main() { + assert_eq!(None::, None); +} diff --git a/tests/compile-fail/bugs/memcmp.rs b/tests/compile-fail/bugs/memcmp.rs new file mode 100644 index 0000000000000..d854f21ca317e --- /dev/null +++ b/tests/compile-fail/bugs/memcmp.rs @@ -0,0 +1,11 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// error-pattern:can't handle intrinsic: size_of_val + +#[miri_run] +fn memcmp() { + assert_eq!("", ""); +} + +fn main() {} diff --git a/tests/compile-fail/bugs/slice_index.rs b/tests/compile-fail/bugs/slice_index.rs new file mode 100644 index 0000000000000..52a76247ca468 --- /dev/null +++ b/tests/compile-fail/bugs/slice_index.rs @@ -0,0 +1,12 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +// error-pattern:assertion failed + +#[miri_run] +fn slice() -> u8 { + let arr: &[_] = &[101, 102, 103, 104, 105, 106]; + arr[5] +} + +fn main() {} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 224d7f0611ff7..51634fd15bffb 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -20,4 +20,5 @@ fn run_mode(mode: &'static str) { fn compile_test() { run_mode("compile-fail"); run_mode("run-pass"); + run_mode("run-fail"); } diff --git a/tests/run-fail/inception.rs b/tests/run-fail/inception.rs new file mode 100644 index 0000000000000..25eb72aa04c23 --- /dev/null +++ b/tests/run-fail/inception.rs @@ -0,0 +1,32 @@ +// error-pattern:no mir for DefId + +use std::env; +use std::process::{Command, Output}; + +fn run_miri(file: &str, sysroot: &str) -> Output { + let path = env::current_dir().unwrap(); + let libpath = path.join("target").join("debug"); + let libpath = libpath.to_str().unwrap(); + let libpath2 = path.join("target").join("debug").join("deps"); + let libpath2 = libpath2.to_str().unwrap(); + Command::new("cargo") + .args(&[ + "run", "--", + "--sysroot", sysroot, + "-L", libpath, + "-L", libpath2, + file + ]) + .output() + .unwrap_or_else(|e| panic!("failed to execute process: {}", e)) +} + +fn main() { + let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + let test_run = run_miri("src/bin/miri.rs", &sysroot); + + if test_run.status.code().unwrap_or(-1) != 0 { + println!("{}", String::from_utf8(test_run.stdout).unwrap()); + panic!("{}", String::from_utf8(test_run.stderr).unwrap()); + } +} diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index 26a4196b1b975..36b7217f58062 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -6,6 +6,11 @@ fn empty_array() -> [u16; 0] { [] } +#[miri_run] +fn mini_array() -> [u16; 1] { + [42] +} + #[miri_run] fn big_array() -> [u16; 5] { [5, 4, 3, 2, 1] @@ -33,4 +38,14 @@ fn array_repeat() -> [u8; 8] { [42; 8] } -fn main() {} +#[miri_run] +fn main() { + //assert_eq!(empty_array(), []); + assert_eq!(index_unsafe(), 20); + assert_eq!(index(), 20); + /* + assert_eq!(big_array(), [5, 4, 3, 2, 1]); + assert_eq!(array_array(), [[5, 4], [3, 2], [1, 0]]); + assert_eq!(array_repeat(), [42; 8]); + */ +} diff --git a/tests/run-pass/bools.rs b/tests/run-pass/bools.rs index 948c09c0fdaab..953670fef9b27 100644 --- a/tests/run-pass/bools.rs +++ b/tests/run-pass/bools.rs @@ -27,4 +27,10 @@ fn match_bool() -> i16 { } } -fn main() {} +#[miri_run] +fn main() { + assert!(boolean()); + assert_eq!(if_false(), 0); + assert_eq!(if_true(), 1); + assert_eq!(match_bool(), 1); +} diff --git a/tests/run-pass/c_enums.rs b/tests/run-pass/c_enums.rs index 442e58a22a981..fa7d195454ebe 100644 --- a/tests/run-pass/c_enums.rs +++ b/tests/run-pass/c_enums.rs @@ -20,4 +20,7 @@ fn unsafe_match() -> bool { } } -fn main() {} +#[miri_run] +fn main() { + assert!(unsafe_match()); +} diff --git a/tests/run-pass/calls.rs b/tests/run-pass/calls.rs index 62ea521d956e5..68b3581456275 100644 --- a/tests/run-pass/calls.rs +++ b/tests/run-pass/calls.rs @@ -39,4 +39,11 @@ fn test_size_of() -> usize { ::std::mem::size_of::>() } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(call(), 2); + assert_eq!(factorial_recursive(), 3628800); + //assert_eq!(call_generic(), (42, true)); + assert_eq!(cross_crate_fn_call(), 1); + //assert_eq!(test_size_of(), 8); +} diff --git a/tests/run-pass/closures.rs b/tests/run-pass/closures.rs index 3612cfcb4c476..ca35992225ecc 100644 --- a/tests/run-pass/closures.rs +++ b/tests/run-pass/closures.rs @@ -37,4 +37,7 @@ fn crazy_closure() -> (i32, i32, i32) { // y // } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(simple(), 12); +} diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index 3cc01e5829bb9..f5aad1601c77d 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,4 +11,8 @@ fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(*make_box(), (1, 2)); + assert_eq!(*make_box_syntax(), (1, 2)); +} diff --git a/tests/run-pass/ints.rs b/tests/run-pass/ints.rs index cc113eaed5923..76091be3581ec 100644 --- a/tests/run-pass/ints.rs +++ b/tests/run-pass/ints.rs @@ -53,4 +53,13 @@ fn match_int_range() -> i64 { } } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(ret(), 1); + assert_eq!(neg(), -1); + assert_eq!(add(), 3); + assert_eq!(indirect_add(), 3); + assert_eq!(arith(), 5*5); + assert_eq!(match_int(), 20); + assert_eq!(match_int_range(), 4); +} diff --git a/tests/run-pass/loops.rs b/tests/run-pass/loops.rs index 081e7bb228b4a..57a2f7c4357be 100644 --- a/tests/run-pass/loops.rs +++ b/tests/run-pass/loops.rs @@ -34,4 +34,9 @@ fn for_loop() -> usize { sum } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(factorial_loop(), 3628800); + assert_eq!(index_for_loop(), 60); + assert_eq!(for_loop(), 60); +} diff --git a/tests/run-pass/pointers.rs b/tests/run-pass/pointers.rs index b66aabb2505c4..9f28b982b4eee 100644 --- a/tests/run-pass/pointers.rs +++ b/tests/run-pass/pointers.rs @@ -58,4 +58,14 @@ fn dangling_pointer() -> *const i32 { &*b as *const i32 } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(one_line_ref(), 1); + assert_eq!(basic_ref(), 1); + assert_eq!(basic_ref_mut(), 3); + assert_eq!(basic_ref_mut_var(), 3); + assert_eq!(tuple_ref_mut(), (10, 22)); + assert_eq!(match_ref_mut(), 42); + // FIXME: improve this test... how? + assert!(dangling_pointer() != std::ptr::null()); +} diff --git a/tests/run-pass/products.rs b/tests/run-pass/products.rs index 1d65006ae6f67..68a1b429438e0 100644 --- a/tests/run-pass/products.rs +++ b/tests/run-pass/products.rs @@ -16,6 +16,7 @@ fn tuple_5() -> (i16, i16, i16, i16, i16) { (1, 2, 3, 4, 5) } +#[derive(Debug, PartialEq)] struct Pair { x: i8, y: i8 } #[miri_run] @@ -30,4 +31,11 @@ fn field_access() -> (i8, i8) { (p.x, p.y) } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(tuple(), (1,)); + assert_eq!(tuple_2(), (1, 2)); + assert_eq!(tuple_5(), (1, 2, 3, 4, 5)); + assert_eq!(pair(), Pair { x: 10, y: 20} ); + assert_eq!(field_access(), (15, 20)); +} diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index b8b101fe84217..b82038f400308 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -18,4 +18,6 @@ fn specialization() -> (bool, bool) { (i32::is_unit(), <()>::is_unit()) } -fn main() {} +fn main() { + assert_eq!(specialization(), (false, true)); +} diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index d9c0e3ca1fe48..1d4dc8befef8c 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -44,4 +44,10 @@ fn true_assert() { assert_eq!(1, 1); } -fn main() {} +#[miri_run] +fn main() { + //let x = rc_reference_cycle().0; + //assert!(x.borrow().is_some()); + assert_eq!(*arc(), 42); + assert_eq!(rc_cell().get(), 84); +} diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 7f92f0d5ffffe..b8635b4dcd648 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -1,6 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] +#[derive(Debug, PartialEq)] enum Unit { Unit } #[miri_run] @@ -8,6 +9,7 @@ fn return_unit() -> Unit { Unit::Unit } +#[derive(Debug, PartialEq)] enum MyBool { False, True } #[miri_run] @@ -53,4 +55,14 @@ fn two_nones() -> (Option, Option) { (None, None) } -fn main() {} +#[miri_run] +fn main() { + //assert_eq!(two_nones(), (None, None)); + assert_eq!(match_opt_some(), 13); + assert_eq!(match_opt_none(), 42); + //assert_eq!(return_some(), Some(42)); + //assert_eq!(return_none(), None); + //assert_eq!(return_false(), MyBool::False); + //assert_eq!(return_true(), MyBool::True); + //assert_eq!(return_unit(), Unit::Unit); +} diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index 325762289c5bd..2a4cc6128843b 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -36,4 +36,9 @@ fn vec_reallocate() -> Vec { v } -fn main() {} +#[miri_run] +fn main() { + assert_eq!(vec_reallocate().len(), 5); + assert_eq!(vec_into_iter(), 30); + assert_eq!(make_vec().capacity(), 4); +} From feca81307fa341131b48785a72db23ea6fb349c1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 22 Apr 2016 15:00:19 +0200 Subject: [PATCH 0251/1096] wild pointer and null pointer deref --- tests/compile-fail/errors.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/compile-fail/errors.rs b/tests/compile-fail/errors.rs index d971724f6878b..6ef9800a1ad09 100644 --- a/tests/compile-fail/errors.rs +++ b/tests/compile-fail/errors.rs @@ -46,4 +46,15 @@ fn dangling_pointer_deref() -> i32 { unsafe { *p } //~ ERROR: dangling pointer was dereferenced } +#[miri_run] +fn wild_pointer_deref() -> i32 { + let p = 42 as *const i32; + unsafe { *p } //~ ERROR: attempted to interpret some raw bytes as a pointer address +} + +#[miri_run] +fn null_pointer_deref() -> i32 { + unsafe { *std::ptr::null() } //~ ERROR: attempted to interpret some raw bytes as a pointer address +} + fn main() {} From 5ea57ccbcd182b46ff54dc182faa6b5f1ae7737b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 22 Apr 2016 15:04:12 +0200 Subject: [PATCH 0252/1096] bug: transmute::<*const T, Option>>(..) --- tests/compile-fail/bugs/option_box_transmute_ptr.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/compile-fail/bugs/option_box_transmute_ptr.rs diff --git a/tests/compile-fail/bugs/option_box_transmute_ptr.rs b/tests/compile-fail/bugs/option_box_transmute_ptr.rs new file mode 100644 index 0000000000000..84161daf88ddb --- /dev/null +++ b/tests/compile-fail/bugs/option_box_transmute_ptr.rs @@ -0,0 +1,13 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn option_box_deref() -> i32 { + let val = Some(Box::new(42)); + unsafe { + let ptr: *const i32 = std::mem::transmute(val); //~ ERROR: pointer offset outside bounds of allocation + *ptr + } +} + +fn main() {} From 4a863c2a6ab3f68db8882ac9ec2f469c1698d9b6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 00:03:59 -0600 Subject: [PATCH 0253/1096] Replace Repr with the new ty::layout in rustc. Lvalues still need work (see lvalue_layout). --- src/interpreter.rs | 310 +++++++++++++++++------------------------ src/lib.rs | 2 - src/memory.rs | 48 ------- tests/run-pass/sums.rs | 10 +- 4 files changed, 134 insertions(+), 236 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 91682207ed638..546935dee7666 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,3 @@ -use arena::TypedArena; use rustc::infer; use rustc::middle::const_val; use rustc::hir::def_id::DefId; @@ -6,10 +5,10 @@ use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::traits::{self, ProjectionMode}; use rustc::ty::fold::TypeFoldable; +use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::DefIdMap; -use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; @@ -19,12 +18,12 @@ use syntax::attr; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{FieldRepr, Memory, Pointer, Repr}; +use memory::{Memory, Pointer}; use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = false; -struct Interpreter<'a, 'tcx: 'a, 'arena> { +struct Interpreter<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: &'a TyCtxt<'tcx>, @@ -34,12 +33,6 @@ struct Interpreter<'a, 'tcx: 'a, 'arena> { /// A local cache from DefIds to Mir for non-crate-local items. mir_cache: RefCell>>>, - /// An arena allocator for type representations. - repr_arena: &'arena TypedArena, - - /// A cache for in-memory representations of types. - repr_cache: RefCell, &'arena Repr>>, - /// The virtual memory system. memory: Memory, @@ -112,16 +105,12 @@ enum TerminatorTarget { Return, } -impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { - fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>, repr_arena: &'arena TypedArena) - -> Self - { +impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { + fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { Interpreter { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - repr_arena: repr_arena, - repr_cache: RefCell::new(FnvHashMap()), memory: Memory::new(), stack: Vec::new(), substs_stack: Vec::new(), @@ -242,7 +231,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let discr_size = self.lvalue_repr(discr).size(); + let discr_size = + self.lvalue_layout(discr).size(&self.tcx.data_layout).bytes() as usize; let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); // Branch to the `otherwise` case by default, if no match is found. @@ -262,12 +252,12 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Switch { ref discr, ref targets, adt_def } => { let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let adt_repr = self.lvalue_repr(discr); - let discr_size = match *adt_repr { - Repr::Aggregate { discr_size, .. } => discr_size, + let adt_layout = self.lvalue_layout(discr); + let discr_size = match *adt_layout { + Layout::General { discr, .. } => discr.size().bytes(), _ => panic!("attmpted to switch on non-aggregate type"), }; - let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size)); + let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); let matching = adt_def.variants.iter() .position(|v| discr_val == v.disr_val.to_u64_unchecked()); @@ -328,13 +318,15 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let last_arg = args.last().unwrap(); let last = try!(self.eval_operand(last_arg)); let last_ty = self.operand_ty(last_arg); - let last_repr = self.type_repr(last_ty); - match (&last_ty.sty, last_repr) { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { (&ty::TyTuple(ref fields), - &Repr::Aggregate { discr_size: 0, ref variants, .. }) => { - assert_eq!(variants.len(), 1); - for (repr, ty) in variants[0].iter().zip(fields) { - let src = last.offset(repr.offset as isize); + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); arg_srcs.push((src, ty)); } } @@ -578,28 +570,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Ok(TerminatorTarget::Call) } - fn assign_to_aggregate( + fn assign_fields>( &mut self, dest: Pointer, - dest_repr: &Repr, - variant: usize, - discr: Option, + offsets: I, operands: &[mir::Operand<'tcx>], ) -> EvalResult<()> { - match *dest_repr { - Repr::Aggregate { discr_size, ref variants, .. } => { - if discr_size > 0 { - try!(self.memory.write_uint(dest, discr.unwrap(), discr_size)); - } - let after_discr = dest.offset(discr_size as isize); - for (field, operand) in variants[variant].iter().zip(operands) { - let src = try!(self.eval_operand(operand)); - let src_ty = self.operand_ty(operand); - let field_dest = after_discr.offset(field.offset as isize); - try!(self.move_(src, field_dest, src_ty)); - } - } - _ => panic!("expected Repr::Aggregate target"), + for (offset, operand) in offsets.into_iter().zip(operands) { + let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); + let field_dest = dest.offset(offset as isize); + try!(self.move_(src, field_dest, src_ty)); } Ok(()) } @@ -609,7 +590,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { { let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_repr = self.lvalue_repr(lvalue); + let dest_layout = self.lvalue_layout(lvalue); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -639,39 +620,52 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } Aggregate(ref kind, ref operands) => { - use rustc::mir::repr::AggregateKind::*; - match *kind { - Tuple | Closure(..) => - try!(self.assign_to_aggregate(dest, &dest_repr, 0, None, operands)), - - Adt(adt_def, variant, _) => { - let discr = Some(adt_def.variants[variant].disr_val.to_u64_unchecked()); - try!(self.assign_to_aggregate(dest, &dest_repr, variant, discr, operands)); + match *dest_layout { + Layout::Univariant { ref variant, .. } => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter().map(|s| s.bytes())); + try!(self.assign_fields(dest, offsets, operands)); + } + + Layout::Array { .. } => { + let elem_size = match dest_ty.sty { + ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + _ => panic!("tried to assign {:?} aggregate to non-array type {:?}", + kind, dest_ty), + }; + let offsets = (0..).map(|i| i * elem_size); + try!(self.assign_fields(dest, offsets, operands)); } - Vec => if let Repr::Array { elem_size, length } = *dest_repr { - assert_eq!(length, operands.len()); - for (i, operand) in operands.iter().enumerate() { - let src = try!(self.eval_operand(operand)); - let src_ty = self.operand_ty(operand); - let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.move_(src, elem_dest, src_ty)); + Layout::General { discr, ref variants, .. } => { + if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let discr_size = discr.size().bytes() as usize; + try!(self.memory.write_uint(dest, discr_val, discr_size)); + + let offsets = variants[variant].offset_after_field.iter() + .map(|s| s.bytes()); + try!(self.assign_fields(dest, offsets, operands)); + } else { + panic!("tried to assign {:?} aggregate to Layout::General dest", kind); } - } else { - panic!("expected Repr::Array target"); - }, + } + + _ => panic!("can't handle destination layout {:?} when assigning {:?}", + dest_layout, kind), } } Repeat(ref operand, _) => { - if let Repr::Array { elem_size, length } = *dest_repr { - let src = try!(self.eval_operand(operand)); - for i in 0..length { - let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.memory.copy(src, elem_dest, elem_size)); - } - } else { - panic!("expected Repr::Array target"); + let (elem_size, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), + }; + + let src = try!(self.eval_operand(operand)); + for i in 0..length { + let elem_dest = dest.offset((i * elem_size) as isize); + try!(self.memory.copy(src, elem_dest, elem_size)); } } @@ -731,7 +725,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Misc => { // FIXME(tsion): Wrong for almost everything. - let size = dest_repr.size(); + let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; try!(self.memory.copy(src, dest, size)); } @@ -747,22 +741,22 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { - self.eval_operand_and_repr(op).map(|(p, _)| p) + self.eval_operand_and_layout(op).map(|(p, _)| p) } - fn eval_operand_and_repr(&mut self, op: &mir::Operand<'tcx>) - -> EvalResult<(Pointer, &'arena Repr)> + fn eval_operand_and_layout(&mut self, op: &mir::Operand<'tcx>) + -> EvalResult<(Pointer, &'tcx Layout)> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => - Ok((try!(self.eval_lvalue(lvalue)).to_ptr(), self.lvalue_repr(lvalue))), + Ok((try!(self.eval_lvalue(lvalue)).to_ptr(), self.lvalue_layout(lvalue))), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(( try!(self.const_to_ptr(value)), - self.type_repr(ty), + self.type_layout(ty), )), Item { .. } => unimplemented!(), } @@ -770,15 +764,18 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } - // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueRepr). - fn lvalue_repr(&self, lvalue: &mir::Lvalue<'tcx>) -> &'arena Repr { + // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueLayout). + fn lvalue_layout(&self, lvalue: &mir::Lvalue<'tcx>) -> &'tcx Layout { use rustc::mir::tcx::LvalueTy; match self.mir().lvalue_ty(self.tcx, lvalue) { - LvalueTy::Ty { ty } => self.type_repr(ty), + LvalueTy::Ty { ty } => self.type_layout(ty), LvalueTy::Downcast { adt_def, substs, variant_index } => { let field_tys = adt_def.variants[variant_index].fields.iter() .map(|f| f.ty(self.tcx, substs)); - self.repr_arena.alloc(self.make_aggregate_repr(iter::once(field_tys))) + + // FIXME(tsion): Handle LvalueTy::Downcast better somehow... + unimplemented!(); + // self.repr_arena.alloc(self.make_aggregate_layout(iter::once(field_tys))) } } } @@ -796,21 +793,23 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Projection(ref proj) => { let base_ptr = try!(self.eval_lvalue(&proj.base)).to_ptr(); - let base_repr = self.lvalue_repr(&proj.base); + let base_layout = self.lvalue_layout(&proj.base); let base_ty = self.lvalue_ty(&proj.base); + use rustc::mir::repr::ProjectionElem::*; match proj.elem { - Field(field, _) => match *base_repr { - Repr::Aggregate { discr_size: 0, ref variants, .. } => { - let fields = &variants[0]; - base_ptr.offset(fields[field.index()].offset as isize) + Field(field, _) => match *base_layout { + Layout::Univariant { ref variant, .. } => { + let offset = variant.field_offset(field.index()).bytes(); + base_ptr.offset(offset as isize) } - _ => panic!("field access on non-product type: {:?}", base_repr), + _ => panic!("field access on non-product type: {:?}", base_layout), }, - Downcast(..) => match *base_repr { - Repr::Aggregate { discr_size, .. } => base_ptr.offset(discr_size as isize), - _ => panic!("variant downcast on non-aggregate type: {:?}", base_repr), + Downcast(..) => match *base_layout { + Layout::General { discr, .. } => + base_ptr.offset(discr.size().bytes() as isize), + _ => panic!("variant downcast on non-aggregate type: {:?}", base_layout), }, Deref => { @@ -921,99 +920,17 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } fn type_size(&self, ty: ty::Ty<'tcx>) -> usize { - self.type_repr(ty).size() + self.type_layout(ty).size(&self.tcx.data_layout).bytes() as usize } - fn type_repr(&self, ty: ty::Ty<'tcx>) -> &'arena Repr { + fn type_layout(&self, ty: ty::Ty<'tcx>) -> &'tcx Layout { + // TODO(tsion): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty); - if let Some(repr) = self.repr_cache.borrow().get(ty) { - return repr; - } - - use syntax::ast::{IntTy, UintTy}; - let repr = match ty.sty { - ty::TyBool => Repr::Primitive { size: 1 }, - - ty::TyInt(IntTy::I8) | ty::TyUint(UintTy::U8) => Repr::Primitive { size: 1 }, - ty::TyInt(IntTy::I16) | ty::TyUint(UintTy::U16) => Repr::Primitive { size: 2 }, - ty::TyInt(IntTy::I32) | ty::TyUint(UintTy::U32) => Repr::Primitive { size: 4 }, - ty::TyInt(IntTy::I64) | ty::TyUint(UintTy::U64) => Repr::Primitive { size: 8 }, - - ty::TyInt(IntTy::Is) | ty::TyUint(UintTy::Us) => - Repr::Primitive { size: self.memory.pointer_size }, - - ty::TyTuple(ref fields) => - self.make_aggregate_repr(iter::once(fields.iter().cloned())), - - ty::TyEnum(adt_def, substs) | ty::TyStruct(adt_def, substs) => { - let variants = adt_def.variants.iter().map(|v| { - v.fields.iter().map(|f| f.ty(self.tcx, substs)) - }); - self.make_aggregate_repr(variants) - } - - ty::TyArray(elem_ty, length) => Repr::Array { - elem_size: self.type_size(elem_ty), - length: length, - }, - - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyBox(ty) => { - if self.type_is_sized(ty) { - Repr::Primitive { size: self.memory.pointer_size } - } else { - Repr::Primitive { size: self.memory.pointer_size * 2 } - } - } - - ty::TyFnPtr(..) => Repr::Primitive { size: self.memory.pointer_size }, - - ty::TyClosure(_, ref closure_substs) => - self.make_aggregate_repr(iter::once(closure_substs.upvar_tys.iter().cloned())), - - ref t => panic!("can't convert type to repr: {:?}", t), - }; - - let repr_ref = self.repr_arena.alloc(repr); - self.repr_cache.borrow_mut().insert(ty, repr_ref); - repr_ref - } - - fn make_aggregate_repr(&self, variant_fields: V) -> Repr - where V: IntoIterator, V::Item: IntoIterator> - { - let mut variants = Vec::new(); - let mut max_variant_size = 0; - - for field_tys in variant_fields { - let mut fields = Vec::new(); - let mut size = 0; - - for ty in field_tys { - let field_size = self.type_size(ty); - let offest = size; - size += field_size; - fields.push(FieldRepr { offset: offest, size: field_size }); - } - - if size > max_variant_size { max_variant_size = size; } - variants.push(fields); - } + let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); - let discr_size = match variants.len() as u64 { - n if n <= 1 => 0, - n if n <= 1 << 8 => 1, - n if n <= 1 << 16 => 2, - n if n <= 1 << 32 => 4, - _ => 8, - }; - Repr::Aggregate { - discr_size: discr_size, - size: max_variant_size + discr_size, - variants: variants, - } + // TODO(tsion): Report this error properly. + ty.layout(&infcx).unwrap() } pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { @@ -1237,8 +1154,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) println!("Interpreting: {}", item.name); - let repr_arena = TypedArena::new(); - let mut miri = Interpreter::new(tcx, mir_map, &repr_arena); + let mut miri = Interpreter::new(tcx, mir_map); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { let size = miri.type_size(ty); @@ -1259,3 +1175,35 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } } } + +// TODO(tsion): Upstream these methods into rustc::ty::layout. + +trait IntegerExt { + fn size(self) -> Size; +} + +impl IntegerExt for layout::Integer { + fn size(self) -> Size { + use rustc::ty::layout::Integer::*; + match self { + I1 | I8 => Size::from_bits(8), + I16 => Size::from_bits(16), + I32 => Size::from_bits(32), + I64 => Size::from_bits(64), + } + } +} + +trait StructExt { + fn field_offset(&self, index: usize) -> Size; +} + +impl StructExt for layout::Struct { + fn field_offset(&self, index: usize) -> Size { + if index == 0 { + Size::from_bytes(0) + } else { + self.offset_after_field[index - 1] + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 8ddafb4991115..0bf7dfb87d14c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,7 @@ )] // From rustc. -extern crate arena; #[macro_use] extern crate rustc; -extern crate rustc_data_structures; extern crate rustc_mir; extern crate syntax; diff --git a/src/memory.rs b/src/memory.rs index 14aa4f9d799ec..9f4e2691bcce5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,54 +6,6 @@ use std::{fmt, iter, mem, ptr}; use error::{EvalError, EvalResult}; use primval::PrimVal; -//////////////////////////////////////////////////////////////////////////////// -// Value representations -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Repr { - /// Representation for a non-aggregate type such as a boolean, integer, character or pointer. - Primitive { - size: usize - }, - - /// The representation for aggregate types including structs, enums, and tuples. - Aggregate { - /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for - /// structs and tuples. - discr_size: usize, - - /// The size of the entire aggregate, including the discriminant. - size: usize, - - /// The representations of the contents of each variant. - variants: Vec>, - }, - - Array { - elem_size: usize, - - /// Number of elements. - length: usize, - }, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct FieldRepr { - pub offset: usize, - pub size: usize, -} - -impl Repr { - pub fn size(&self) -> usize { - match *self { - Repr::Primitive { size } => size, - Repr::Aggregate { size, .. } => size, - Repr::Array { elem_size, length } => elem_size * length, - } - } -} - //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index b8635b4dcd648..d6eddc69fc9a5 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -2,24 +2,24 @@ #![allow(dead_code, unused_attributes)] #[derive(Debug, PartialEq)] -enum Unit { Unit } +enum Unit { Unit(()) } // Force non-C-enum representation. #[miri_run] fn return_unit() -> Unit { - Unit::Unit + Unit::Unit(()) } #[derive(Debug, PartialEq)] -enum MyBool { False, True } +enum MyBool { False(()), True(()) } // Force non-C-enum representation. #[miri_run] fn return_true() -> MyBool { - MyBool::True + MyBool::True(()) } #[miri_run] fn return_false() -> MyBool { - MyBool::False + MyBool::False(()) } #[miri_run] From f7d7ce27c8104949d36df2020e58acca58c1b1e6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 00:11:56 -0600 Subject: [PATCH 0254/1096] Update to a nightly with rustc::ty::layout. --- .travis.yml | 2 +- README.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 598ea9852e56e..e152db5422709 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: -- nightly-2016-04-11 +- nightly-2016-04-21 - nightly matrix: allow_failures: diff --git a/README.md b/README.md index 5a4e274317031..26d1278ceb846 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,26 @@ I currently recommend that you install [multirust][multirust] and then use it to install the current rustc nightly version that works with Miri: ```sh -multirust update nightly-2016-04-11 +multirust update nightly-2016-04-21 ``` ## Build ```sh -multirust run nightly-2016-04-11 cargo build +multirust run nightly-2016-04-21 cargo build ``` ## Run a test ```sh -multirust run nightly-2016-04-11 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-04-11 \ +multirust run nightly-2016-04-21 cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly-2016-04-21 \ test/filename.rs ``` If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), the `sysroot` path will also include your build target (e.g. -`$HOME/.multirust/toolchains/nightly-2016-04-11-x86_64-apple-darwin`). You can +`$HOME/.multirust/toolchains/nightly-2016-04-21-x86_64-apple-darwin`). You can see the current toolchain's directory by running `rustup which cargo` (ignoring the trailing `/bin/cargo`). From 6f50289d437bd2d9308761bd2405d63a633cbe1e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 00:26:10 -0600 Subject: [PATCH 0255/1096] Fix lvalue projections with fat pointer bases. --- src/interpreter.rs | 2 +- tests/compile-fail/bugs/slice_index.rs | 12 ------------ tests/run-pass/arrays.rs | 7 +++++++ 3 files changed, 8 insertions(+), 13 deletions(-) delete mode 100644 tests/compile-fail/bugs/slice_index.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 546935dee7666..5eea525a54322 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -792,7 +792,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Static(_def_id) => unimplemented!(), Projection(ref proj) => { - let base_ptr = try!(self.eval_lvalue(&proj.base)).to_ptr(); + let base_ptr = try!(self.eval_lvalue(&proj.base)).ptr; let base_layout = self.lvalue_layout(&proj.base); let base_ty = self.lvalue_ty(&proj.base); diff --git a/tests/compile-fail/bugs/slice_index.rs b/tests/compile-fail/bugs/slice_index.rs deleted file mode 100644 index 52a76247ca468..0000000000000 --- a/tests/compile-fail/bugs/slice_index.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -// error-pattern:assertion failed - -#[miri_run] -fn slice() -> u8 { - let arr: &[_] = &[101, 102, 103, 104, 105, 106]; - arr[5] -} - -fn main() {} diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index 36b7217f58062..1fbf24b46684e 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -38,11 +38,18 @@ fn array_repeat() -> [u8; 8] { [42; 8] } +#[miri_run] +fn slice_index() -> u8 { + let arr: &[_] = &[101, 102, 103, 104, 105, 106]; + arr[5] +} + #[miri_run] fn main() { //assert_eq!(empty_array(), []); assert_eq!(index_unsafe(), 20); assert_eq!(index(), 20); + assert_eq!(slice_index(), 106); /* assert_eq!(big_array(), [5, 4, 3, 2, 1]); assert_eq!(array_array(), [[5, 4], [3, 2], [1, 0]]); From 2db3597b564cfb63b36bbd49a6b4bbc146df2adb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 00:39:38 -0600 Subject: [PATCH 0256/1096] Implement boolean binops. --- src/primval.rs | 19 +++++++++++++++---- tests/run-pass/specialization.rs | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/primval.rs b/src/primval.rs index c9117d033fdd5..3d844cebd3117 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,10 +14,11 @@ pub enum PrimVal { } pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult { + use rustc::mir::repr::BinOp::*; + use self::PrimVal::*; + macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ - use rustc::mir::repr::BinOp::*; - use self::PrimVal::*; match bin_op { Add => $v($l + $r), Sub => $v($l - $r), @@ -52,7 +53,6 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul } } - use self::PrimVal::*; let val = match (left, right) { (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), @@ -63,6 +63,18 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (Bool(l), Bool(r)) => { + Bool(match bin_op { + Eq => l == r, + Ne => l != r, + Lt => l < r, + Le => l <= r, + Gt => l > r, + Ge => l >= r, + _ => panic!("invalid binary operation on booleans: {:?}", bin_op), + }) + } + (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), (AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) => @@ -76,7 +88,6 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul let l = l_ptr.offset; let r = r_ptr.offset; - use rustc::mir::repr::BinOp::*; match bin_op { Eq => Bool(l == r), Ne => Bool(l != r), diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index b82038f400308..4b5f510ad4409 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -18,6 +18,7 @@ fn specialization() -> (bool, bool) { (i32::is_unit(), <()>::is_unit()) } +#[miri_run] fn main() { assert_eq!(specialization(), (false, true)); } From 500cd256272160a06f3c2a10b8e6f3a19e494be6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 20:13:00 -0600 Subject: [PATCH 0257/1096] Add missing boolean binops. --- src/primval.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/primval.rs b/src/primval.rs index 3d844cebd3117..ad96fbe7d4199 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -71,7 +71,11 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul Le => l <= r, Gt => l > r, Ge => l >= r, - _ => panic!("invalid binary operation on booleans: {:?}", bin_op), + BitOr => l | r, + BitXor => l ^ r, + BitAnd => l & r, + Add | Sub | Mul | Div | Rem | Shl | Shr => + panic!("invalid binary operation on booleans: {:?}", bin_op), }) } From 3fd2ee9ddc1ce5677ca308b5c4e23010ade60e7b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 23 Apr 2016 20:46:27 -0600 Subject: [PATCH 0258/1096] Remove unused eval_operand_and_layout fn. --- src/interpreter.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5eea525a54322..636aa27240b55 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -741,23 +741,14 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { - self.eval_operand_and_layout(op).map(|(p, _)| p) - } - - fn eval_operand_and_layout(&mut self, op: &mir::Operand<'tcx>) - -> EvalResult<(Pointer, &'tcx Layout)> - { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => - Ok((try!(self.eval_lvalue(lvalue)).to_ptr(), self.lvalue_layout(lvalue))), + Ok(try!(self.eval_lvalue(lvalue)).to_ptr()), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { - Value { ref value } => Ok(( - try!(self.const_to_ptr(value)), - self.type_layout(ty), - )), + Value { ref value } => Ok(try!(self.const_to_ptr(value))), Item { .. } => unimplemented!(), } } From 0f533e3ae065af3f05cbd09f47a45245f37f9712 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 28 Apr 2016 04:13:26 -0600 Subject: [PATCH 0259/1096] report: Remove redundant commented-out paragraph. --- tex/report/miri-report.tex | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 6d2686c138f7d..9ef9652eed1d3 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -536,12 +536,6 @@ \subsection{Standard library} \emph{does} invoke undefined behaviour, Miri will abort with an appropriate error message (see \autoref{fig:vec-error}). -% You can even do unsafe things with \rust{Vec} like \rust{v.set_len(10)} or -% \rust{v.get_unchecked(2)}, but if you do these things carefully in a way that doesn't cause any -% undefined behaviour (just like when you write unsafe code for regular Rust), then Miri can handle it -% all. But if you do slip up, Miri will error out with an appropriate message (see -% \autoref{fig:vec-error}). - \begin{figure}[t] \begin{minted}[autogobble]{rust} fn out_of_bounds() -> u8 { From de64670de02f9210c3bf6094cf1ff1c45abb9763 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 29 Apr 2016 06:01:17 +0200 Subject: [PATCH 0260/1096] Fixed some clippy warnings --- src/interpreter.rs | 14 ++++++-------- src/memory.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 91682207ed638..c0e01c5029e15 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -169,8 +169,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { for stmt in &block_data.statements { self.log(0, || print!("{:?}", stmt)); - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let result = self.eval_assignment(lvalue, rvalue); + let mir::StatementKind::Assign(ref l_value, ref r_value) = stmt.kind; + let result = self.eval_assignment(l_value, r_value); try!(self.maybe_report(stmt.span, result)); } @@ -830,7 +830,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { Index(ref operand) => { let elem_size = match base_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty), + ty::TyArray(elem_ty, _) | ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; @@ -1109,11 +1109,9 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { let vtable = selection.map(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - let vtable = infer::drain_fulfillment_cx_or_panic( + infer::drain_fulfillment_cx_or_panic( DUMMY_SP, &infcx, &mut fulfill_cx, &vtable - ); - - vtable + ) } /// Trait method, which has to be resolved to an impl method. @@ -1166,7 +1164,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { } } -fn pointee_type<'tcx>(ptr_ty: ty::Ty<'tcx>) -> Option> { +fn pointee_type(ptr_ty: ty::Ty) -> Option { match ptr_ty.sty { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | diff --git a/src/memory.rs b/src/memory.rs index 14aa4f9d799ec..285c3554b647b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -47,7 +47,7 @@ pub struct FieldRepr { impl Repr { pub fn size(&self) -> usize { match *self { - Repr::Primitive { size } => size, + Repr::Primitive { size } | Repr::Aggregate { size, .. } => size, Repr::Array { elem_size, length } => elem_size * length, } @@ -406,7 +406,7 @@ impl Memory { fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { // Find all relocations overlapping the given range. let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); - if keys.len() == 0 { return Ok(()); } + if keys.is_empty() { return Ok(()); } // Find the start and end of the given range and its outermost relocations. let start = ptr.offset; From 7cb6c0dbfe3c49c5ed2f08c60b293bfae98c8ef9 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 29 Apr 2016 17:47:04 +0200 Subject: [PATCH 0261/1096] back out similar_names change --- src/interpreter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index c0e01c5029e15..0b4aab71b6157 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -169,8 +169,8 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> { for stmt in &block_data.statements { self.log(0, || print!("{:?}", stmt)); - let mir::StatementKind::Assign(ref l_value, ref r_value) = stmt.kind; - let result = self.eval_assignment(l_value, r_value); + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + let result = self.eval_assignment(lvalue, rvalue); try!(self.maybe_report(stmt.span, result)); } From 30f07f3d7ff1b44ac96af5e03e4882ff913e3f85 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 29 Apr 2016 23:32:15 -0600 Subject: [PATCH 0262/1096] Re-implement support for downcast lvalues. --- src/interpreter.rs | 74 ++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 636aa27240b55..66a75dd4d56b2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -84,7 +84,8 @@ struct Lvalue { enum LvalueExtra { None, Length(u64), - // Vtable(memory::AllocId), + // TODO(tsion): Vtable(memory::AllocId), + DowncastVariant(usize), } #[derive(Clone)] @@ -231,8 +232,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let discr_size = - self.lvalue_layout(discr).size(&self.tcx.data_layout).bytes() as usize; + let discr_size = self + .type_layout(self.lvalue_ty(discr)) + .size(&self.tcx.data_layout) + .bytes() as usize; let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); // Branch to the `otherwise` case by default, if no match is found. @@ -252,7 +255,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Switch { ref discr, ref targets, adt_def } => { let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); - let adt_layout = self.lvalue_layout(discr); + let adt_layout = self.type_layout(self.lvalue_ty(discr)); let discr_size = match *adt_layout { Layout::General { discr, .. } => discr.size().bytes(), _ => panic!("attmpted to switch on non-aggregate type"), @@ -590,7 +593,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { { let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.lvalue_layout(lvalue); + let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -693,6 +696,8 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let len_ptr = dest.offset(self.memory.pointer_size as isize); try!(self.memory.write_usize(len_ptr, len)); } + LvalueExtra::DowncastVariant(..) => + panic!("attempted to take a reference to an enum downcast lvalue"), } } @@ -745,7 +750,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *op { Consume(ref lvalue) => Ok(try!(self.eval_lvalue(lvalue)).to_ptr()), - Constant(mir::Constant { ref literal, ty, .. }) => { + Constant(mir::Constant { ref literal, .. }) => { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(try!(self.const_to_ptr(value))), @@ -755,22 +760,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - // TODO(tsion): Replace this inefficient hack with a wrapper like LvalueTy (e.g. LvalueLayout). - fn lvalue_layout(&self, lvalue: &mir::Lvalue<'tcx>) -> &'tcx Layout { - use rustc::mir::tcx::LvalueTy; - match self.mir().lvalue_ty(self.tcx, lvalue) { - LvalueTy::Ty { ty } => self.type_layout(ty), - LvalueTy::Downcast { adt_def, substs, variant_index } => { - let field_tys = adt_def.variants[variant_index].fields.iter() - .map(|f| f.ty(self.tcx, substs)); - - // FIXME(tsion): Handle LvalueTy::Downcast better somehow... - unimplemented!(); - // self.repr_arena.alloc(self.make_aggregate_layout(iter::once(field_tys))) - } - } - } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { @@ -783,32 +772,45 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Static(_def_id) => unimplemented!(), Projection(ref proj) => { - let base_ptr = try!(self.eval_lvalue(&proj.base)).ptr; - let base_layout = self.lvalue_layout(&proj.base); + let base = try!(self.eval_lvalue(&proj.base)); let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { - Field(field, _) => match *base_layout { - Layout::Univariant { ref variant, .. } => { - let offset = variant.field_offset(field.index()).bytes(); - base_ptr.offset(offset as isize) - } - _ => panic!("field access on non-product type: {:?}", base_layout), + Field(field, _) => { + let variant = match *base_layout { + Layout::Univariant { ref variant, .. } => variant, + Layout::General { ref variants, .. } => { + if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { + &variants[variant_idx] + } else { + panic!("field access on enum had no variant index"); + } + } + _ => panic!("field access on non-product type: {:?}", base_layout), + }; + + let offset = variant.field_offset(field.index()).bytes(); + base.ptr.offset(offset as isize) }, - Downcast(..) => match *base_layout { - Layout::General { discr, .. } => - base_ptr.offset(discr.size().bytes() as isize), + Downcast(_, variant) => match *base_layout { + Layout::General { discr, .. } => { + return Ok(Lvalue { + ptr: base.ptr.offset(discr.size().bytes() as isize), + extra: LvalueExtra::DowncastVariant(variant), + }); + } _ => panic!("variant downcast on non-aggregate type: {:?}", base_layout), }, Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - let ptr = try!(self.memory.read_ptr(base_ptr)); + let ptr = try!(self.memory.read_ptr(base.ptr)); let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { - let len_ptr = base_ptr.offset(self.memory.pointer_size as isize); + let len_ptr = base.ptr.offset(self.memory.pointer_size as isize); let len = try!(self.memory.read_usize(len_ptr)); LvalueExtra::Length(len) } @@ -826,7 +828,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { }; let n_ptr = try!(self.eval_operand(operand)); let n = try!(self.memory.read_usize(n_ptr)); - base_ptr.offset(n as isize * elem_size as isize) + base.ptr.offset(n as isize * elem_size as isize) } ConstantIndex { .. } => unimplemented!(), From 9e289fa0aa93d3a3ea80c7edd0b6e035fa360387 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 30 Apr 2016 01:04:17 -0600 Subject: [PATCH 0263/1096] Fully handle RawNullablePointer layout. --- src/interpreter.rs | 62 +++++++++++++++---- src/memory.rs | 2 +- .../option_box_transmute_ptr.rs | 4 +- 3 files changed, 54 insertions(+), 14 deletions(-) rename tests/{compile-fail/bugs => run-pass}/option_box_transmute_ptr.rs (54%) diff --git a/src/interpreter.rs b/src/interpreter.rs index 66a75dd4d56b2..4ab2413e0ffe2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -256,18 +256,34 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Switch { ref discr, ref targets, adt_def } => { let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); let adt_layout = self.type_layout(self.lvalue_ty(discr)); - let discr_size = match *adt_layout { - Layout::General { discr, .. } => discr.size().bytes(), - _ => panic!("attmpted to switch on non-aggregate type"), - }; - let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); - let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + match *adt_layout { + Layout::General { discr, .. } => { + let discr_size = discr.size().bytes(); + let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); + + let matching = adt_def.variants.iter() + .position(|v| discr_val == v.disr_val.to_u64_unchecked()); - match matching { - Some(i) => TerminatorTarget::Block(targets[i]), - None => return Err(EvalError::InvalidDiscriminant), + match matching { + Some(i) => TerminatorTarget::Block(targets[i]), + None => return Err(EvalError::InvalidDiscriminant), + } + } + + Layout::RawNullablePointer { nndiscr, .. } => { + let is_null = match self.memory.read_usize(adt_ptr) { + Ok(0) => true, + Ok(_) | Err(EvalError::ReadPointerAsBytes) => false, + Err(e) => return Err(e), + }; + + assert!(nndiscr == 0 || nndiscr == 1); + let target = if is_null { 1 - nndiscr } else { nndiscr }; + TerminatorTarget::Block(targets[target as usize]) + } + + _ => panic!("attmpted to switch on non-aggregate type"), } } @@ -633,7 +649,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Layout::Array { .. } => { let elem_size = match dest_ty.sty { ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, - _ => panic!("tried to assign {:?} aggregate to non-array type {:?}", + _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); @@ -650,7 +666,24 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { .map(|s| s.bytes()); try!(self.assign_fields(dest, offsets, operands)); } else { - panic!("tried to assign {:?} aggregate to Layout::General dest", kind); + panic!("tried to assign {:?} to Layout::General", kind); + } + } + + Layout::RawNullablePointer { nndiscr, .. } => { + if let mir::AggregateKind::Adt(_, variant, _) = *kind { + if nndiscr == variant as u64 { + assert_eq!(operands.len(), 1); + let operand = &operands[0]; + let src = try!(self.eval_operand(operand)); + let src_ty = self.operand_ty(operand); + try!(self.move_(src, dest, src_ty)); + } else { + assert_eq!(operands.len(), 0); + try!(self.memory.write_isize(dest, 0)); + } + } else { + panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); } } @@ -788,6 +821,10 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { panic!("field access on enum had no variant index"); } } + Layout::RawNullablePointer { .. } => { + assert_eq!(field.index(), 0); + return Ok(base); + } _ => panic!("field access on non-product type: {:?}", base_layout), }; @@ -802,6 +839,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { extra: LvalueExtra::DowncastVariant(variant), }); } + Layout::RawNullablePointer { .. } => return Ok(base), _ => panic!("variant downcast on non-aggregate type: {:?}", base_layout), }, diff --git a/src/memory.rs b/src/memory.rs index 9f4e2691bcce5..f48d5a9b8e8f7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -403,7 +403,7 @@ impl Memory { // Undefined bytes //////////////////////////////////////////////////////////////////////////////// - // FIXME(tsino): This is a very naive, slow version. + // FIXME(tsion): This is a very naive, slow version. fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. let mut v = Vec::with_capacity(size); diff --git a/tests/compile-fail/bugs/option_box_transmute_ptr.rs b/tests/run-pass/option_box_transmute_ptr.rs similarity index 54% rename from tests/compile-fail/bugs/option_box_transmute_ptr.rs rename to tests/run-pass/option_box_transmute_ptr.rs index 84161daf88ddb..55beb11edd449 100644 --- a/tests/compile-fail/bugs/option_box_transmute_ptr.rs +++ b/tests/run-pass/option_box_transmute_ptr.rs @@ -1,11 +1,13 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] +// This tests that the size of Option> is the same as *const i32. + #[miri_run] fn option_box_deref() -> i32 { let val = Some(Box::new(42)); unsafe { - let ptr: *const i32 = std::mem::transmute(val); //~ ERROR: pointer offset outside bounds of allocation + let ptr: *const i32 = std::mem::transmute::>, *const i32>(val); *ptr } } From d288472b297c4df82f293d2a8ea9c7098e3a70b3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 8 May 2016 19:29:00 -0600 Subject: [PATCH 0264/1096] Handle CEnum layouts with unsigned representations. --- src/interpreter.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 4ab2413e0ffe2..fe327b75452d8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -258,7 +258,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { let adt_layout = self.type_layout(self.lvalue_ty(discr)); match *adt_layout { - Layout::General { discr, .. } => { + Layout::General { discr, .. } | Layout::CEnum { discr, .. } => { let discr_size = discr.size().bytes(); let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); @@ -283,7 +283,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { TerminatorTarget::Block(targets[target as usize]) } - _ => panic!("attmpted to switch on non-aggregate type"), + _ => panic!("attempted to switch on non-aggregate type"), } } @@ -639,14 +639,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } Aggregate(ref kind, ref operands) => { + use rustc::ty::layout::Layout::*; match *dest_layout { - Layout::Univariant { ref variant, .. } => { + Univariant { ref variant, .. } => { let offsets = iter::once(0) .chain(variant.offset_after_field.iter().map(|s| s.bytes())); try!(self.assign_fields(dest, offsets, operands)); } - Layout::Array { .. } => { + Array { .. } => { let elem_size = match dest_ty.sty { ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", @@ -656,7 +657,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { try!(self.assign_fields(dest, offsets, operands)); } - Layout::General { discr, ref variants, .. } => { + General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let discr_size = discr.size().bytes() as usize; @@ -670,7 +671,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - Layout::RawNullablePointer { nndiscr, .. } => { + RawNullablePointer { nndiscr, .. } => { if let mir::AggregateKind::Adt(_, variant, _) = *kind { if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); @@ -687,6 +688,21 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } + CEnum { discr, signed, min, max } => { + assert_eq!(operands.len(), 0); + if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + if signed { + unimplemented!() + } else { + let val = adt_def.variants[variant].disr_val.to_u64().unwrap(); + let size = discr.size().bytes() as usize; + try!(self.memory.write_uint(dest, val, size)); + } + } else { + panic!("tried to assign {:?} to Layout::CEnum", kind); + } + } + _ => panic!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind), } From d825ef1bf11c1b4005e6fc2c7cd135695f84104b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 8 May 2016 19:30:17 -0600 Subject: [PATCH 0265/1096] Move StructWrappedNullablePointer-using test to bugs dir. --- .../bugs/struct_wrapped_nullable_pointer.rs | 23 +++++++++++++++++++ tests/run-pass/std.rs | 14 +---------- 2 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs diff --git a/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs b/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs new file mode 100644 index 0000000000000..880ca42d45557 --- /dev/null +++ b/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs @@ -0,0 +1,23 @@ +#![feature(custom_attribute, box_syntax)] +#![allow(dead_code, unused_attributes)] + +// error-pattern:can't handle destination layout StructWrappedNullablePointer + +use std::cell::RefCell; +use std::rc::Rc; + +struct Loop(Rc>>); + +#[miri_run] +fn rc_reference_cycle() -> Loop { + let a = Rc::new(RefCell::new(None)); + let b = a.clone(); + *a.borrow_mut() = Some(Loop(b)); + Loop(a) +} + +#[miri_run] +fn main() { + let x = rc_reference_cycle().0; + assert!(x.borrow().is_some()); +} diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index 1d4dc8befef8c..b5bb7c7dbdb9f 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::rc::Rc; use std::sync::Arc; @@ -29,16 +29,6 @@ fn arc() -> Arc { a } -struct Loop(Rc>>); - -#[miri_run] -fn rc_reference_cycle() -> Loop { - let a = Rc::new(RefCell::new(None)); - let b = a.clone(); - *a.borrow_mut() = Some(Loop(b)); - Loop(a) -} - #[miri_run] fn true_assert() { assert_eq!(1, 1); @@ -46,8 +36,6 @@ fn true_assert() { #[miri_run] fn main() { - //let x = rc_reference_cycle().0; - //assert!(x.borrow().is_some()); assert_eq!(*arc(), 42); assert_eq!(rc_cell().get(), 84); } From a6b9b165c3d1537cb6a662902c003f0c557de14a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 8 May 2016 19:49:07 -0600 Subject: [PATCH 0266/1096] Handle CEnum layouts with signed representations. --- src/interpreter.rs | 9 +++++---- tests/run-pass/c_enums.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 5dc7d51ea7f86..3e5260cfadb68 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -688,14 +688,15 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { } } - CEnum { discr, signed, min, max } => { + CEnum { discr, signed, .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + let val = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let size = discr.size().bytes() as usize; + if signed { - unimplemented!() + try!(self.memory.write_int(dest, val as i64, size)); } else { - let val = adt_def.variants[variant].disr_val.to_u64().unwrap(); - let size = discr.size().bytes() as usize; try!(self.memory.write_uint(dest, val, size)); } } else { diff --git a/tests/run-pass/c_enums.rs b/tests/run-pass/c_enums.rs index fa7d195454ebe..fd537a37b0439 100644 --- a/tests/run-pass/c_enums.rs +++ b/tests/run-pass/c_enums.rs @@ -7,11 +7,22 @@ enum Foo { Quux = 100, } +enum Signed { + Bar = -42, + Baz, + Quux = 100, +} + #[miri_run] fn foo() -> [u8; 3] { [Foo::Bar as u8, Foo::Baz as u8, Foo::Quux as u8] } +#[miri_run] +fn signed() -> [i8; 3] { + [Signed::Bar as i8, Signed::Baz as i8, Signed::Quux as i8] +} + #[miri_run] fn unsafe_match() -> bool { match unsafe { std::mem::transmute::(43) } { @@ -22,5 +33,7 @@ fn unsafe_match() -> bool { #[miri_run] fn main() { + // assert_eq!(foo(), [42, 43, 100]); + // assert_eq!(signed(), [-42, -41, 100]); assert!(unsafe_match()); } From 49b63495774bfa8328de3b3f940aa44e2a36a68c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 15:30:47 -0600 Subject: [PATCH 0267/1096] Update to a new nightly. --- .travis.yml | 2 +- README.md | 10 +++++----- src/bin/miri.rs | 7 ++++++- src/interpreter.rs | 2 +- src/memory.rs | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index e152db5422709..af5b3648d3ccb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: -- nightly-2016-04-21 +- nightly-2016-05-08 - nightly matrix: allow_failures: diff --git a/README.md b/README.md index 26d1278ceb846..919c68dc96aeb 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,26 @@ I currently recommend that you install [multirust][multirust] and then use it to install the current rustc nightly version that works with Miri: ```sh -multirust update nightly-2016-04-21 +multirust update nightly-2016-05-08 ``` ## Build ```sh -multirust run nightly-2016-04-21 cargo build +multirust run nightly-2016-05-08 cargo build ``` ## Run a test ```sh -multirust run nightly-2016-04-21 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-04-21 \ +multirust run nightly-2016-05-08 cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly-2016-05-08 \ test/filename.rs ``` If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), the `sysroot` path will also include your build target (e.g. -`$HOME/.multirust/toolchains/nightly-2016-04-21-x86_64-apple-darwin`). You can +`$HOME/.multirust/toolchains/nightly-2016-05-08-x86_64-apple-darwin`). You can see the current toolchain's directory by running `rustup which cargo` (ignoring the trailing `/bin/cargo`). diff --git a/src/bin/miri.rs b/src/bin/miri.rs index a03d0a46ca93b..49d49650ea5cc 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,6 +1,7 @@ #![feature(rustc_private, custom_attribute)] #![allow(unused_attributes)] +extern crate getopts; extern crate miri; extern crate rustc; extern crate rustc_driver; @@ -12,7 +13,11 @@ use rustc_driver::{driver, CompilerCalls}; struct MiriCompilerCalls; impl<'a> CompilerCalls<'a> for MiriCompilerCalls { - fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> { + fn build_controller( + &mut self, + _: &Session, + _: &getopts::Matches + ) -> driver::CompileController<'a> { let mut control = driver::CompileController::basic(); control.after_analysis.callback = Box::new(|state| { diff --git a/src/interpreter.rs b/src/interpreter.rs index 3e5260cfadb68..b9c9160015ea1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -805,6 +805,7 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *literal { Value { ref value } => Ok(try!(self.const_to_ptr(value))), Item { .. } => unimplemented!(), + Promoted { .. } => unimplemented!(), } } } @@ -1044,7 +1045,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { return CachedMir::Owned(mir.clone()); } - use rustc::middle::cstore::CrateStore; let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { panic!("no mir for {:?}", def_id); diff --git a/src/memory.rs b/src/memory.rs index 45f8b392c0060..8eccb66bd98fe 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,4 @@ -use byteorder::{ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, mem, ptr}; From 753971a4c597c60c55f2e3cd225d01fe5ad99d02 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 18:21:21 -0600 Subject: [PATCH 0268/1096] Handle promoted rvalues by recursing with call_nested. --- src/interpreter.rs | 99 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 29 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index b9c9160015ea1..a7a1ffb27b603 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -10,7 +10,7 @@ use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::{iter, mem}; use syntax::ast; @@ -23,7 +23,7 @@ use primval::{self, PrimVal}; const TRACE_EXECUTION: bool = false; -struct Interpreter<'a, 'tcx: 'a> { +struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: &'a TyCtxt<'tcx>, @@ -36,9 +36,6 @@ struct Interpreter<'a, 'tcx: 'a> { /// The virtual memory system. memory: Memory, - /// The virtual call stack. - stack: Vec>, - /// Another stack containing the type substitutions for the current function invocation. It /// exists separately from `stack` because it must contain the `Substs` for a function while /// *creating* the `Frame` for that same function. @@ -51,6 +48,26 @@ struct Interpreter<'a, 'tcx: 'a> { name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>, } +struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> { + gecx: &'a mut GlobalEvalContext<'b, 'tcx>, + + /// The virtual call stack. + stack: Vec>, +} + +impl<'a, 'b, 'mir, 'tcx> Deref for FnEvalContext<'a, 'b, 'mir, 'tcx> { + type Target = GlobalEvalContext<'b, 'tcx>; + fn deref(&self) -> &Self::Target { + self.gecx + } +} + +impl<'a, 'b, 'mir, 'tcx> DerefMut for FnEvalContext<'a, 'b, 'mir, 'tcx> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.gecx + } +} + /// A stack frame. struct Frame<'a, 'tcx: 'a> { /// The MIR for the function called on this frame. @@ -106,18 +123,26 @@ enum TerminatorTarget { Return, } -impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { +impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { - Interpreter { + GlobalEvalContext { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(), - stack: Vec::new(), substs_stack: Vec::new(), name_stack: Vec::new(), } } +} + +impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { + fn new(gecx: &'a mut GlobalEvalContext<'b, 'tcx>) -> Self { + FnEvalContext { + gecx: gecx, + stack: Vec::new(), + } + } fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { if let Err(ref e) = r { @@ -183,7 +208,24 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(()) } - fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, + fn call_nested(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult> { + let mut nested_fecx = FnEvalContext::new(self.gecx); + + let return_ptr = match mir.return_ty { + ty::FnConverging(ty) => { + let size = nested_fecx.type_size(ty); + Some(nested_fecx.memory.allocate(size)) + } + ty::FnDiverging => None, + }; + + let substs = nested_fecx.substs(); + nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); + try!(nested_fecx.run()); + Ok(return_ptr) + } + + fn push_stack_frame(&mut self, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { self.substs_stack.push(substs); @@ -805,7 +847,11 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { match *literal { Value { ref value } => Ok(try!(self.const_to_ptr(value))), Item { .. } => unimplemented!(), - Promoted { .. } => unimplemented!(), + Promoted { index } => { + let current_mir = self.mir(); + let mir = ¤t_mir.promoted[index]; + self.call_nested(mir).map(Option::unwrap) + } } } } @@ -1020,23 +1066,23 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> { Ok(val) } - fn frame(&self) -> &Frame<'a, 'tcx> { + fn frame(&self) -> &Frame<'mir, 'tcx> { self.stack.last().expect("no call frames exist") } - fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { + fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn mir(&self) -> &mir::Mir<'tcx> { - &self.frame().mir + fn mir(&self) -> CachedMir<'mir, 'tcx> { + self.frame().mir.clone() } fn substs(&self) -> &'tcx Substs<'tcx> { self.substs_stack.last().cloned().unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())) } - fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + fn load_mir(&self, def_id: DefId) -> CachedMir<'mir, 'tcx> { match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), None => { @@ -1200,22 +1246,17 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) println!("Interpreting: {}", item.name); - let mut miri = Interpreter::new(tcx, mir_map); - let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => { - let size = miri.type_size(ty); - Some(miri.memory.allocate(size)) + let mut gecx = GlobalEvalContext::new(tcx, mir_map); + let mut fecx = FnEvalContext::new(&mut gecx); + match fecx.call_nested(mir) { + Ok(Some(return_ptr)) => fecx.memory.dump(return_ptr.alloc_id), + Ok(None) => println!("(diverging function returned)"), + Err(_e) => { + // TODO(tsion): Detect whether the error was already reported or not. + // tcx.sess.err(&e.to_string()); } - ty::FnDiverging => None, - }; - let substs = miri.tcx.mk_substs(Substs::empty()); - miri.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); - if let Err(_e) = miri.run() { - // TODO(tsion): Detect whether the error was already reported or not. - // tcx.sess.err(&e.to_string()); - } else if let Some(ret) = return_ptr { - miri.memory.dump(ret.alloc_id); } + println!(""); } } From 4856997168fc23998cf2e6c31d52e9673a151451 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 18:35:22 -0600 Subject: [PATCH 0269/1096] Only test on the latest nightly version. --- .travis.yml | 1 - README.md | 22 ++++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index af5b3648d3ccb..d56ca04817e13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: rust rust: -- nightly-2016-05-08 - nightly matrix: allow_failures: diff --git a/README.md b/README.md index 919c68dc96aeb..99f909d116314 100644 --- a/README.md +++ b/README.md @@ -12,31 +12,37 @@ undergraduate research course at the [University of Saskatchewan][usask]. ## Download Rust nightly I currently recommend that you install [multirust][multirust] and then use it to -install the current rustc nightly version that works with Miri: +install the current rustc nightly version: ```sh -multirust update nightly-2016-05-08 +multirust update nightly ``` ## Build ```sh -multirust run nightly-2016-05-08 cargo build +multirust run nightly cargo build ``` +If Miri fails to build, it's likely because a change in the latest nightly +compiler broke it. You could try an older nightly with `multirust update +nightly-` where `` is a few days or weeks ago, e.g. `2016-05-20` for +May 20th. Otherwise, you could notify me in an issue or on IRC. Or, if you know +how to fix it, you could send a PR. :simple_smile: + ## Run a test ```sh -multirust run nightly-2016-05-08 cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly-2016-05-08 \ +multirust run nightly cargo run -- \ + --sysroot $HOME/.multirust/toolchains/nightly \ test/filename.rs ``` If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), the `sysroot` path will also include your build target (e.g. -`$HOME/.multirust/toolchains/nightly-2016-05-08-x86_64-apple-darwin`). You can -see the current toolchain's directory by running `rustup which cargo` (ignoring -the trailing `/bin/cargo`). +`$HOME/.multirust/toolchains/nightly-x86_64-apple-darwin`). You can see the +current toolchain's directory by running `rustup which cargo` (ignoring the +trailing `/bin/cargo`). If you installed without using multirust or rustup, you'll need to adjust the command to run your cargo and set the `sysroot` to the directory where your From 4792b8a873826d07c82d1c56933fc7d97779f62e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 18:36:40 -0600 Subject: [PATCH 0270/1096] Fix the all-important smiley. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99f909d116314..b09363df96485 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ If Miri fails to build, it's likely because a change in the latest nightly compiler broke it. You could try an older nightly with `multirust update nightly-` where `` is a few days or weeks ago, e.g. `2016-05-20` for May 20th. Otherwise, you could notify me in an issue or on IRC. Or, if you know -how to fix it, you could send a PR. :simple_smile: +how to fix it, you could send a PR. :smile: ## Run a test From b85944456224ee7a824486415cb093ae518dd0cb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 18:52:44 -0600 Subject: [PATCH 0271/1096] Do or do not. --- src/interpreter.rs | 247 ++++++++++++++++++++++----------------------- src/lib.rs | 1 + src/memory.rs | 62 ++++++------ 3 files changed, 154 insertions(+), 156 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a7a1ffb27b603..d86dbdbe6877f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -186,14 +186,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.log(0, || print!("{:?}", stmt)); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.eval_assignment(lvalue, rvalue); - try!(self.maybe_report(stmt.span, result)); + self.maybe_report(stmt.span, result)?; } let terminator = block_data.terminator(); self.log(0, || print!("{:?}", terminator.kind)); let result = self.eval_terminator(terminator); - match try!(self.maybe_report(terminator.span, result)) { + match self.maybe_report(terminator.span, result)? { TerminatorTarget::Block(block) => current_block = block, TerminatorTarget::Return => { self.pop_stack_frame(); @@ -221,7 +221,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let substs = nested_fecx.substs(); nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); - try!(nested_fecx.run()); + nested_fecx.run()?; Ok(return_ptr) } @@ -267,25 +267,25 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Goto { target } => TerminatorTarget::Block(target), If { ref cond, targets: (then_target, else_target) } => { - let cond_ptr = try!(self.eval_operand(cond)); - let cond_val = try!(self.memory.read_bool(cond_ptr)); + let cond_ptr = self.eval_operand(cond)?; + let cond_val = self.memory.read_bool(cond_ptr)?; TerminatorTarget::Block(if cond_val { then_target } else { else_target }) } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = try!(self.eval_lvalue(discr)).to_ptr(); + let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self .type_layout(self.lvalue_ty(discr)) .size(&self.tcx.data_layout) .bytes() as usize; - let discr_val = try!(self.memory.read_uint(discr_ptr, discr_size)); + let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; for (index, val_const) in values.iter().enumerate() { - let ptr = try!(self.const_to_ptr(val_const)); - let val = try!(self.memory.read_uint(ptr, discr_size)); + let ptr = self.const_to_ptr(val_const)?; + let val = self.memory.read_uint(ptr, discr_size)?; if discr_val == val { target_block = targets[index]; break; @@ -296,13 +296,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } Switch { ref discr, ref targets, adt_def } => { - let adt_ptr = try!(self.eval_lvalue(discr)).to_ptr(); + let adt_ptr = self.eval_lvalue(discr)?.to_ptr(); let adt_layout = self.type_layout(self.lvalue_ty(discr)); match *adt_layout { Layout::General { discr, .. } | Layout::CEnum { discr, .. } => { let discr_size = discr.size().bytes(); - let discr_val = try!(self.memory.read_uint(adt_ptr, discr_size as usize)); + let discr_val = self.memory.read_uint(adt_ptr, discr_size as usize)?; let matching = adt_def.variants.iter() .position(|v| discr_val == v.disr_val.to_u64_unchecked()); @@ -333,7 +333,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { self.frame_mut().next_block = target; - return_ptr = Some(try!(self.eval_lvalue(lv)).to_ptr()); + return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); } let func_ty = self.operand_ty(func); @@ -346,15 +346,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match fn_ty.sig.0.output { ty::FnConverging(ty) => { let size = self.type_size(ty); - try!(self.call_intrinsic(&name, substs, args, - return_ptr.unwrap(), size)) + self.call_intrinsic(&name, substs, args, + return_ptr.unwrap(), size)? } ty::FnDiverging => unimplemented!(), } } - Abi::C => - try!(self.call_c_abi(def_id, args, return_ptr.unwrap())), + Abi::C => self.call_c_abi(def_id, args, return_ptr.unwrap())?, Abi::Rust | Abi::RustCall => { // TODO(tsion): Adjust the first argument when calling a Fn or @@ -369,7 +368,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let mut arg_srcs = Vec::new(); for arg in args { - let src = try!(self.eval_operand(arg)); + let src = self.eval_operand(arg)?; let src_ty = self.operand_ty(arg); arg_srcs.push((src, src_ty)); } @@ -377,7 +376,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { if fn_ty.abi == Abi::RustCall && !args.is_empty() { arg_srcs.pop(); let last_arg = args.last().unwrap(); - let last = try!(self.eval_operand(last_arg)); + let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { @@ -401,7 +400,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; - try!(self.move_(src, dest, src_ty)); + self.move_(src, dest, src_ty)?; } TerminatorTarget::Call @@ -416,9 +415,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } Drop { ref value, target, .. } => { - let ptr = try!(self.eval_lvalue(value)).to_ptr(); + let ptr = self.eval_lvalue(value)?.to_ptr(); let ty = self.lvalue_ty(value); - try!(self.drop(ptr, ty)); + self.drop(ptr, ty)?; TerminatorTarget::Block(target) } @@ -441,13 +440,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { ty::TyBox(contents_ty) => { match self.memory.read_ptr(ptr) { Ok(contents_ptr) => { - try!(self.drop(contents_ptr, contents_ty)); + self.drop(contents_ptr, contents_ty)?; self.log(1, || print!("deallocating box")); - try!(self.memory.deallocate(contents_ptr)); + self.memory.deallocate(contents_ptr)?; } Err(EvalError::ReadBytesAsPointer) => { let size = self.memory.pointer_size; - let possible_drop_fill = try!(self.memory.read_bytes(ptr, size)); + let possible_drop_fill = self.memory.read_bytes(ptr, size)?; if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { return Ok(()); } else { @@ -465,7 +464,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // Filling drop. // FIXME(tsion): Trait objects (with no static size) probably get filled, too. let size = self.type_size(ty); - try!(self.memory.drop_fill(ptr, size)); + self.memory.drop_fill(ptr, size)?; Ok(()) } @@ -481,7 +480,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); - let args = try!(args_res); + let args = args_res?; match name { "assume" => {} @@ -489,71 +488,71 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.type_size(elem_ty); - let src = try!(self.memory.read_ptr(args[0])); - let dest = try!(self.memory.read_ptr(args[1])); - let count = try!(self.memory.read_isize(args[2])); - try!(self.memory.copy(src, dest, count as usize * elem_size)); + let src = self.memory.read_ptr(args[0])?; + let dest = self.memory.read_ptr(args[1])?; + let count = self.memory.read_isize(args[2])?; + self.memory.copy(src, dest, count as usize * elem_size)?; } "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); let arg_size = self.type_size(arg_ty); - try!(self.memory.drop_fill(args[0], arg_size)); + self.memory.drop_fill(args[0], arg_size)?; } - "init" => try!(self.memory.write_repeat(dest, 0, dest_size)), + "init" => self.memory.write_repeat(dest, 0, dest_size)?, "min_align_of" => { - try!(self.memory.write_int(dest, 1, dest_size)); + self.memory.write_int(dest, 1, dest_size)?; } "move_val_init" => { let ty = *substs.types.get(subst::FnSpace, 0); - let ptr = try!(self.memory.read_ptr(args[0])); - try!(self.move_(args[1], ptr, ty)); + let ptr = self.memory.read_ptr(args[0])?; + self.move_(args[1], ptr, ty)?; } // FIXME(tsion): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - let left = try!(self.memory.read_int(args[0], size)); - let right = try!(self.memory.read_int(args[1], size)); + let left = self.memory.read_int(args[0], size)?; + let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { ::std::intrinsics::add_with_overflow::(left, right) }; - try!(self.memory.write_int(dest, n, size)); - try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); + self.memory.write_int(dest, n, size)?; + self.memory.write_bool(dest.offset(size as isize), overflowed)?; } // FIXME(tsion): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - let left = try!(self.memory.read_int(args[0], size)); - let right = try!(self.memory.read_int(args[1], size)); + let left = self.memory.read_int(args[0], size)?; + let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { ::std::intrinsics::mul_with_overflow::(left, right) }; - try!(self.memory.write_int(dest, n, size)); - try!(self.memory.write_bool(dest.offset(size as isize), overflowed)); + self.memory.write_int(dest, n, size)?; + self.memory.write_bool(dest.offset(size as isize), overflowed)?; } "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args[0]; - let offset = try!(self.memory.read_isize(args[1])); + let offset = self.memory.read_isize(args[1])?; match self.memory.read_ptr(ptr_arg) { Ok(ptr) => { let result_ptr = ptr.offset(offset as isize * pointee_size); - try!(self.memory.write_ptr(dest, result_ptr)); + self.memory.write_ptr(dest, result_ptr)?; } Err(EvalError::ReadBytesAsPointer) => { - let addr = try!(self.memory.read_isize(ptr_arg)); + let addr = self.memory.read_isize(ptr_arg)?; let result_addr = addr + offset * pointee_size as i64; - try!(self.memory.write_isize(dest, result_addr)); + self.memory.write_isize(dest, result_addr)?; } Err(e) => return Err(e), } @@ -563,23 +562,23 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); - let left = try!(self.memory.read_int(args[0], size)); - let right = try!(self.memory.read_int(args[1], size)); + let left = self.memory.read_int(args[0], size)?; + let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); - try!(self.memory.write_int(dest, n, size)); + self.memory.write_int(dest, n, size)?; } "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty) as u64; - try!(self.memory.write_uint(dest, size, dest_size)); + self.memory.write_uint(dest, size, dest_size)?; } "transmute" => { let ty = *substs.types.get(subst::FnSpace, 0); - try!(self.move_(args[0], dest, ty)); + self.move_(args[0], dest, ty)?; } - "uninit" => try!(self.memory.mark_definedness(dest, dest_size, false)), + "uninit" => self.memory.mark_definedness(dest, dest_size, false)?, name => panic!("can't handle intrinsic: {}", name), } @@ -606,20 +605,20 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); - let args = try!(args_res); + let args = args_res?; match &link_name[..] { "__rust_allocate" => { - let size = try!(self.memory.read_usize(args[0])); + let size = self.memory.read_usize(args[0])?; let ptr = self.memory.allocate(size as usize); - try!(self.memory.write_ptr(dest, ptr)); + self.memory.write_ptr(dest, ptr)?; } "__rust_reallocate" => { - let ptr = try!(self.memory.read_ptr(args[0])); - let size = try!(self.memory.read_usize(args[2])); - try!(self.memory.reallocate(ptr, size as usize)); - try!(self.memory.write_ptr(dest, ptr)); + let ptr = self.memory.read_ptr(args[0])?; + let size = self.memory.read_usize(args[2])?; + self.memory.reallocate(ptr, size as usize)?; + self.memory.write_ptr(dest, ptr)?; } _ => panic!("can't call C ABI function: {}", link_name), @@ -638,10 +637,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { operands: &[mir::Operand<'tcx>], ) -> EvalResult<()> { for (offset, operand) in offsets.into_iter().zip(operands) { - let src = try!(self.eval_operand(operand)); + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); let field_dest = dest.offset(offset as isize); - try!(self.move_(src, field_dest, src_ty)); + self.move_(src, field_dest, src_ty)?; } Ok(()) } @@ -649,35 +648,35 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) -> EvalResult<()> { - let dest = try!(self.eval_lvalue(lvalue)).to_ptr(); + let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { - let src = try!(self.eval_operand(operand)); - try!(self.move_(src, dest, dest_ty)); + let src = self.eval_operand(operand)?; + self.move_(src, dest, dest_ty)?; } BinaryOp(bin_op, ref left, ref right) => { - let left_ptr = try!(self.eval_operand(left)); + let left_ptr = self.eval_operand(left)?; let left_ty = self.operand_ty(left); - let left_val = try!(self.read_primval(left_ptr, left_ty)); + let left_val = self.read_primval(left_ptr, left_ty)?; - let right_ptr = try!(self.eval_operand(right)); + let right_ptr = self.eval_operand(right)?; let right_ty = self.operand_ty(right); - let right_val = try!(self.read_primval(right_ptr, right_ty)); + let right_val = self.read_primval(right_ptr, right_ty)?; - let val = try!(primval::binary_op(bin_op, left_val, right_val)); - try!(self.memory.write_primval(dest, val)); + let val = primval::binary_op(bin_op, left_val, right_val)?; + self.memory.write_primval(dest, val)?; } UnaryOp(un_op, ref operand) => { - let ptr = try!(self.eval_operand(operand)); + let ptr = self.eval_operand(operand)?; let ty = self.operand_ty(operand); - let val = try!(self.read_primval(ptr, ty)); - try!(self.memory.write_primval(dest, primval::unary_op(un_op, val))); + let val = self.read_primval(ptr, ty)?; + self.memory.write_primval(dest, primval::unary_op(un_op, val))?; } Aggregate(ref kind, ref operands) => { @@ -686,7 +685,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Univariant { ref variant, .. } => { let offsets = iter::once(0) .chain(variant.offset_after_field.iter().map(|s| s.bytes())); - try!(self.assign_fields(dest, offsets, operands)); + self.assign_fields(dest, offsets, operands)?; } Array { .. } => { @@ -696,18 +695,18 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); - try!(self.assign_fields(dest, offsets, operands)); + self.assign_fields(dest, offsets, operands)?; } General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let discr_size = discr.size().bytes() as usize; - try!(self.memory.write_uint(dest, discr_val, discr_size)); + self.memory.write_uint(dest, discr_val, discr_size)?; let offsets = variants[variant].offset_after_field.iter() .map(|s| s.bytes()); - try!(self.assign_fields(dest, offsets, operands)); + self.assign_fields(dest, offsets, operands)?; } else { panic!("tried to assign {:?} to Layout::General", kind); } @@ -718,12 +717,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); let operand = &operands[0]; - let src = try!(self.eval_operand(operand)); + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - try!(self.move_(src, dest, src_ty)); + self.move_(src, dest, src_ty)?; } else { assert_eq!(operands.len(), 0); - try!(self.memory.write_isize(dest, 0)); + self.memory.write_isize(dest, 0)?; } } else { panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -737,9 +736,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let size = discr.size().bytes() as usize; if signed { - try!(self.memory.write_int(dest, val as i64, size)); + self.memory.write_int(dest, val as i64, size)?; } else { - try!(self.memory.write_uint(dest, val, size)); + self.memory.write_uint(dest, val, size)?; } } else { panic!("tried to assign {:?} to Layout::CEnum", kind); @@ -757,15 +756,15 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - let src = try!(self.eval_operand(operand)); + let src = self.eval_operand(operand)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - try!(self.memory.copy(src, elem_dest, elem_size)); + self.memory.copy(src, elem_dest, elem_size)?; } } Len(ref lvalue) => { - let src = try!(self.eval_lvalue(lvalue)); + let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let len = match ty.sty { ty::TyArray(_, n) => n as u64, @@ -776,17 +775,17 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { }, _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), }; - try!(self.memory.write_usize(dest, len)); + self.memory.write_usize(dest, len)?; } Ref(_, _, ref lvalue) => { - let lv = try!(self.eval_lvalue(lvalue)); - try!(self.memory.write_ptr(dest, lv.ptr)); + let lv = self.eval_lvalue(lvalue)?; + self.memory.write_ptr(dest, lv.ptr)?; match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { let len_ptr = dest.offset(self.memory.pointer_size as isize); - try!(self.memory.write_usize(len_ptr, len)); + self.memory.write_usize(len_ptr, len)?; } LvalueExtra::DowncastVariant(..) => panic!("attempted to take a reference to an enum downcast lvalue"), @@ -796,24 +795,24 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Box(ty) => { let size = self.type_size(ty); let ptr = self.memory.allocate(size); - try!(self.memory.write_ptr(dest, ptr)); + self.memory.write_ptr(dest, ptr)?; } Cast(kind, ref operand, dest_ty) => { - let src = try!(self.eval_operand(operand)); + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); use rustc::mir::repr::CastKind::*; match kind { Unsize => { - try!(self.move_(src, dest, src_ty)); + self.move_(src, dest, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let len_ptr = dest.offset(self.memory.pointer_size as isize); - try!(self.memory.write_usize(len_ptr, length as u64)); + self.memory.write_usize(len_ptr, length as u64)?; } _ => panic!("can't handle cast: {:?}", rvalue), @@ -823,7 +822,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Misc => { // FIXME(tsion): Wrong for almost everything. let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - try!(self.memory.copy(src, dest, size)); + self.memory.copy(src, dest, size)?; } _ => panic!("can't handle cast: {:?}", rvalue), @@ -840,12 +839,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => - Ok(try!(self.eval_lvalue(lvalue)).to_ptr()), + Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()), Constant(mir::Constant { ref literal, .. }) => { use rustc::mir::repr::Literal::*; match *literal { - Value { ref value } => Ok(try!(self.const_to_ptr(value))), + Value { ref value } => Ok(self.const_to_ptr(value)?), Item { .. } => unimplemented!(), Promoted { index } => { let current_mir = self.mir(); @@ -869,7 +867,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Static(_def_id) => unimplemented!(), Projection(ref proj) => { - let base = try!(self.eval_lvalue(&proj.base)); + let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -909,11 +907,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - let ptr = try!(self.memory.read_ptr(base.ptr)); + let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { let len_ptr = base.ptr.offset(self.memory.pointer_size as isize); - let len = try!(self.memory.read_usize(len_ptr)); + let len = self.memory.read_usize(len_ptr)?; LvalueExtra::Length(len) } ty::TyTrait(_) => unimplemented!(), @@ -928,8 +926,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; - let n_ptr = try!(self.eval_operand(operand)); - let n = try!(self.memory.read_usize(n_ptr)); + let n_ptr = self.eval_operand(operand)?; + let n = self.memory.read_usize(n_ptr)?; base.ptr.offset(n as isize * elem_size as isize) } @@ -949,29 +947,29 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Integral(int) => { // TODO(tsion): Check int constant type. let ptr = self.memory.allocate(8); - try!(self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)); + self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)?; Ok(ptr) } Str(ref s) => { let psize = self.memory.pointer_size; let static_ptr = self.memory.allocate(s.len()); let ptr = self.memory.allocate(psize * 2); - try!(self.memory.write_bytes(static_ptr, s.as_bytes())); - try!(self.memory.write_ptr(ptr, static_ptr)); - try!(self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)); + self.memory.write_bytes(static_ptr, s.as_bytes())?; + self.memory.write_ptr(ptr, static_ptr)?; + self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; Ok(ptr) } ByteStr(ref bs) => { let psize = self.memory.pointer_size; let static_ptr = self.memory.allocate(bs.len()); let ptr = self.memory.allocate(psize); - try!(self.memory.write_bytes(static_ptr, bs)); - try!(self.memory.write_ptr(ptr, static_ptr)); + self.memory.write_bytes(static_ptr, bs)?; + self.memory.write_ptr(ptr, static_ptr)?; Ok(ptr) } Bool(b) => { let ptr = self.memory.allocate(1); - try!(self.memory.write_bool(ptr, b)); + self.memory.write_bool(ptr, b)?; Ok(ptr) } Char(_c) => unimplemented!(), @@ -1003,9 +1001,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> { let size = self.type_size(ty); - try!(self.memory.copy(src, dest, size)); + self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { - try!(self.memory.drop_fill(src, size)); + self.memory.drop_fill(src, size)?; } Ok(()) } @@ -1031,19 +1029,19 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { use syntax::ast::{IntTy, UintTy}; let val = match ty.sty { - ty::TyBool => PrimVal::Bool(try!(self.memory.read_bool(ptr))), - ty::TyInt(IntTy::I8) => PrimVal::I8(try!(self.memory.read_int(ptr, 1)) as i8), - ty::TyInt(IntTy::I16) => PrimVal::I16(try!(self.memory.read_int(ptr, 2)) as i16), - ty::TyInt(IntTy::I32) => PrimVal::I32(try!(self.memory.read_int(ptr, 4)) as i32), - ty::TyInt(IntTy::I64) => PrimVal::I64(try!(self.memory.read_int(ptr, 8)) as i64), - ty::TyUint(UintTy::U8) => PrimVal::U8(try!(self.memory.read_uint(ptr, 1)) as u8), - ty::TyUint(UintTy::U16) => PrimVal::U16(try!(self.memory.read_uint(ptr, 2)) as u16), - ty::TyUint(UintTy::U32) => PrimVal::U32(try!(self.memory.read_uint(ptr, 4)) as u32), - ty::TyUint(UintTy::U64) => PrimVal::U64(try!(self.memory.read_uint(ptr, 8)) as u64), + ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), + ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), + ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), + ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), + ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), + ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), + ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), + ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), + ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), // TODO(tsion): Pick the PrimVal dynamically. - ty::TyInt(IntTy::Is) => PrimVal::I64(try!(self.memory.read_isize(ptr))), - ty::TyUint(UintTy::Us) => PrimVal::U64(try!(self.memory.read_usize(ptr))), + ty::TyInt(IntTy::Is) => PrimVal::I64(self.memory.read_isize(ptr)?), + ty::TyUint(UintTy::Us) => PrimVal::U64(self.memory.read_usize(ptr)?), ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { @@ -1051,8 +1049,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match self.memory.read_ptr(ptr) { Ok(p) => PrimVal::AbstractPtr(p), Err(EvalError::ReadBytesAsPointer) => { - let n = try!(self.memory.read_usize(ptr)); - PrimVal::IntegerPtr(n) + PrimVal::IntegerPtr(self.memory.read_usize(ptr)?) } Err(e) => return Err(e), } diff --git a/src/lib.rs b/src/lib.rs index 0bf7dfb87d14c..623ed14be76ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ collections_bound, core_intrinsics, filling_drop, + question_mark, rustc_private, )] diff --git a/src/memory.rs b/src/memory.rs index 8eccb66bd98fe..bf53c599bd775 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -83,7 +83,7 @@ impl Memory { panic!() } - let alloc = try!(self.get_mut(ptr.alloc_id)); + let alloc = self.get_mut(ptr.alloc_id)?; let size = alloc.bytes.len(); if new_size > size { let amount = new_size - size; @@ -181,7 +181,7 @@ impl Memory { //////////////////////////////////////////////////////////////////////////////// fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { - let alloc = try!(self.get(ptr.alloc_id)); + let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds); } @@ -189,7 +189,7 @@ impl Memory { } fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { - let alloc = try!(self.get_mut(ptr.alloc_id)); + let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds); } @@ -197,16 +197,16 @@ impl Memory { } fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { - if try!(self.relocations(ptr, size)).count() != 0 { + if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } - try!(self.check_defined(ptr, size)); + self.check_defined(ptr, size)?; self.get_bytes_unchecked(ptr, size) } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { - try!(self.clear_relocations(ptr, size)); - try!(self.mark_definedness(ptr, size, true)); + self.clear_relocations(ptr, size)?; + self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) } @@ -215,10 +215,10 @@ impl Memory { //////////////////////////////////////////////////////////////////////////////// pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - try!(self.check_relocation_edges(src, size)); + self.check_relocation_edges(src, size)?; - let src_bytes = try!(self.get_bytes_unchecked_mut(src, size)).as_mut_ptr(); - let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr(); + let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); + let dest_bytes = self.get_bytes_mut(dest, size)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and @@ -231,8 +231,8 @@ impl Memory { } } - try!(self.copy_undef_mask(src, dest, size)); - try!(self.copy_relocations(src, dest, size)); + self.copy_undef_mask(src, dest, size)?; + self.copy_relocations(src, dest, size)?; Ok(()) } @@ -242,13 +242,13 @@ impl Memory { } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, src.len())); + let bytes = self.get_bytes_mut(ptr, src.len())?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<()> { - let bytes = try!(self.get_bytes_mut(ptr, count)); + let bytes = self.get_bytes_mut(ptr, count)?; for b in bytes { *b = val; } Ok(()) } @@ -259,10 +259,10 @@ impl Memory { pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { let size = self.pointer_size; - try!(self.check_defined(ptr, size)); - let offset = try!(self.get_bytes_unchecked(ptr, size)) + self.check_defined(ptr, size)?; + let offset = self.get_bytes_unchecked(ptr, size)? .read_uint::(size).unwrap() as usize; - let alloc = try!(self.get(ptr.alloc_id)); + let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), None => Err(EvalError::ReadBytesAsPointer), @@ -272,10 +272,10 @@ impl Memory { pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<()> { { let size = self.pointer_size; - let mut bytes = try!(self.get_bytes_mut(dest, size)); + let mut bytes = self.get_bytes_mut(dest, size)?; bytes.write_uint::(ptr.offset as u64, size).unwrap(); } - try!(self.get_mut(dest.alloc_id)).relocations.insert(dest.offset, ptr.alloc_id); + self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); Ok(()) } @@ -297,7 +297,7 @@ impl Memory { } pub fn read_bool(&self, ptr: Pointer) -> EvalResult { - let bytes = try!(self.get_bytes(ptr, 1)); + let bytes = self.get_bytes(ptr, 1)?; match bytes[0] { 0 => Ok(false), 1 => Ok(true), @@ -352,12 +352,12 @@ impl Memory { { let start = ptr.offset.saturating_sub(self.pointer_size - 1); let end = start + size; - Ok(try!(self.get(ptr.alloc_id)).relocations.range(Included(&start), Excluded(&end))) + Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { // Find all relocations overlapping the given range. - let keys: Vec<_> = try!(self.relocations(ptr, size)).map(|(&k, _)| k).collect(); + let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { return Ok(()); } // Find the start and end of the given range and its outermost relocations. @@ -366,7 +366,7 @@ impl Memory { let first = *keys.first().unwrap(); let last = *keys.last().unwrap() + self.pointer_size; - let alloc = try!(self.get_mut(ptr.alloc_id)); + let alloc = self.get_mut(ptr.alloc_id)?; // Mark parts of the outermost relocations as undefined if they partially fall outside the // given range. @@ -380,8 +380,8 @@ impl Memory { } fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { - let overlapping_start = try!(self.relocations(ptr, 0)).count(); - let overlapping_end = try!(self.relocations(ptr.offset(size as isize), 0)).count(); + let overlapping_start = self.relocations(ptr, 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size as isize), 0)?.count(); if overlapping_start + overlapping_end != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -389,13 +389,13 @@ impl Memory { } fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { - let relocations: Vec<_> = try!(self.relocations(src, size)) + let relocations: Vec<_> = self.relocations(src, size)? .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. (offset + dest.offset - src.offset, alloc_id) }) .collect(); - try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations); + self.get_mut(dest.alloc_id)?.relocations.extend(relocations); Ok(()) } @@ -408,17 +408,17 @@ impl Memory { // The bits have to be saved locally before writing to dest in case src and dest overlap. let mut v = Vec::with_capacity(size); for i in 0..size { - let defined = try!(self.get(src.alloc_id)).undef_mask.get(src.offset + i); + let defined = self.get(src.alloc_id)?.undef_mask.get(src.offset + i); v.push(defined); } for (i, defined) in v.into_iter().enumerate() { - try!(self.get_mut(dest.alloc_id)).undef_mask.set(dest.offset + i, defined); + self.get_mut(dest.alloc_id)?.undef_mask.set(dest.offset + i, defined); } Ok(()) } fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { - let alloc = try!(self.get(ptr.alloc_id)); + let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); } @@ -428,7 +428,7 @@ impl Memory { pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<()> { - let mut alloc = try!(self.get_mut(ptr.alloc_id)); + let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) } From ddfbb655e1b4793022265088c0548611758efba7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 20:03:13 -0600 Subject: [PATCH 0272/1096] Handle statics. --- src/interpreter.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d86dbdbe6877f..8fc302637f81e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -846,6 +846,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { .. } => unimplemented!(), Promoted { index } => { + // TODO(tsion): Mark constants and statics as read-only and cache their + // values. let current_mir = self.mir(); let mir = ¤t_mir.promoted[index]; self.call_nested(mir).map(Option::unwrap) @@ -864,7 +866,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], - Static(_def_id) => unimplemented!(), + Static(def_id) => { + // TODO(tsion): Mark constants and statics as read-only and cache their values. + let mir = self.load_mir(def_id); + self.call_nested(&mir)?.unwrap() + } Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; From 382dc0ccb26b3803d1e6f5dfeaf6e5b2c8940b73 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 20:08:37 -0600 Subject: [PATCH 0273/1096] Update for my github username change. --- Cargo.toml | 2 +- README.md | 2 +- src/interpreter.rs | 44 +++++++++++++++++++------------------- src/memory.rs | 14 ++++++------ src/primval.rs | 2 +- tests/run-pass/std.rs | 6 +++--- tex/report/miri-report.tex | 4 ++-- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9fa9f145c72b9..f9ae9e7d4e619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Scott Olson "] description = "An experimental interpreter for Rust MIR." license = "ISC" name = "miri" -repository = "https://github.com/tsion/miri" +repository = "https://github.com/solson/miri" version = "0.1.0" [[bin]] diff --git a/README.md b/README.md index b09363df96485..a553d046f1fdd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ An experimental interpreter for [Rust][rust]'s [mid-level intermediate representation][mir] (MIR). This project began as part of my work for the undergraduate research course at the [University of Saskatchewan][usask]. -[![Build Status](https://travis-ci.org/tsion/miri.svg?branch=master)](https://travis-ci.org/tsion/miri) +[![Build Status](https://travis-ci.org/solson/miri.svg?branch=master)](https://travis-ci.org/solson/miri) ## Download Rust nightly diff --git a/src/interpreter.rs b/src/interpreter.rs index 8fc302637f81e..d1a395bbf3438 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -21,7 +21,7 @@ use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal}; -const TRACE_EXECUTION: bool = false; +const TRACE_EXECUTION: bool = true; struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -41,7 +41,7 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// *creating* the `Frame` for that same function. substs_stack: Vec<&'tcx Substs<'tcx>>, - // TODO(tsion): Merge with `substs_stack`. Also try restructuring `Frame` to accomodate. + // TODO(solson): Merge with `substs_stack`. Also try restructuring `Frame` to accomodate. /// A stack of the things necessary to print good strack traces: /// * Function DefIds and Substs to print proper substituted function names. /// * Spans pointing to specific function calls in the source. @@ -101,7 +101,7 @@ struct Lvalue { enum LvalueExtra { None, Length(u64), - // TODO(tsion): Vtable(memory::AllocId), + // TODO(solson): Vtable(memory::AllocId), DowncastVariant(usize), } @@ -148,7 +148,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { if let Err(ref e) = r { let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); for &(def_id, substs, span) in self.name_stack.iter().rev() { - // FIXME(tsion): Find a way to do this without this Display impl hack. + // FIXME(solson): Find a way to do this without this Display impl hack. use rustc::util::ppaux; use std::fmt; struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); @@ -254,7 +254,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn pop_stack_frame(&mut self) { let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); - // TODO(tsion): Deallocate local variables. + // TODO(solson): Deallocate local variables. self.substs_stack.pop(); } @@ -356,7 +356,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Abi::C => self.call_c_abi(def_id, args, return_ptr.unwrap())?, Abi::Rust | Abi::RustCall => { - // TODO(tsion): Adjust the first argument when calling a Fn or + // TODO(solson): Adjust the first argument when calling a Fn or // FnMut closure via FnOnce::call_once. // Only trait methods can have a Self parameter. @@ -434,7 +434,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } self.log(1, || print!("need to drop {:?}", ty)); - // TODO(tsion): Call user-defined Drop::drop impls. + // TODO(solson): Call user-defined Drop::drop impls. match ty.sty { ty::TyBox(contents_ty) => { @@ -457,12 +457,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - // TODO(tsion): Implement drop for other relevant types (e.g. aggregates). + // TODO(solson): Implement drop for other relevant types (e.g. aggregates). _ => {} } // Filling drop. - // FIXME(tsion): Trait objects (with no static size) probably get filled, too. + // FIXME(solson): Trait objects (with no static size) probably get filled, too. let size = self.type_size(ty); self.memory.drop_fill(ptr, size)?; @@ -512,7 +512,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.move_(args[1], ptr, ty)?; } - // FIXME(tsion): Handle different integer types correctly. + // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); @@ -525,7 +525,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_bool(dest.offset(size as isize), overflowed)?; } - // FIXME(tsion): Handle different integer types correctly. + // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); @@ -558,7 +558,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - // FIXME(tsion): Handle different integer types correctly. Use primvals? + // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty); @@ -820,7 +820,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } Misc => { - // FIXME(tsion): Wrong for almost everything. + // FIXME(solson): Wrong for almost everything. let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; self.memory.copy(src, dest, size)?; } @@ -846,7 +846,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { .. } => unimplemented!(), Promoted { index } => { - // TODO(tsion): Mark constants and statics as read-only and cache their + // TODO(solson): Mark constants and statics as read-only and cache their // values. let current_mir = self.mir(); let mir = ¤t_mir.promoted[index]; @@ -867,7 +867,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], Static(def_id) => { - // TODO(tsion): Mark constants and statics as read-only and cache their values. + // TODO(solson): Mark constants and statics as read-only and cache their values. let mir = self.load_mir(def_id); self.call_nested(&mir)?.unwrap() } @@ -945,13 +945,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } - // TODO(tsion): Try making const_to_primval instead. + // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { use rustc::middle::const_val::ConstVal::*; match *const_val { Float(_f) => unimplemented!(), Integral(int) => { - // TODO(tsion): Check int constant type. + // TODO(solson): Check int constant type. let ptr = self.memory.allocate(8); self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)?; Ok(ptr) @@ -1023,12 +1023,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn type_layout(&self, ty: ty::Ty<'tcx>) -> &'tcx Layout { - // TODO(tsion): Is this inefficient? Needs investigation. + // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty); let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); - // TODO(tsion): Report this error properly. + // TODO(solson): Report this error properly. ty.layout(&infcx).unwrap() } @@ -1045,7 +1045,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - // TODO(tsion): Pick the PrimVal dynamically. + // TODO(solson): Pick the PrimVal dynamically. ty::TyInt(IntTy::Is) => PrimVal::I64(self.memory.read_isize(ptr)?), ty::TyUint(UintTy::Us) => PrimVal::U64(self.memory.read_usize(ptr)?), @@ -1255,7 +1255,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) Ok(Some(return_ptr)) => fecx.memory.dump(return_ptr.alloc_id), Ok(None) => println!("(diverging function returned)"), Err(_e) => { - // TODO(tsion): Detect whether the error was already reported or not. + // TODO(solson): Detect whether the error was already reported or not. // tcx.sess.err(&e.to_string()); } } @@ -1266,7 +1266,7 @@ pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) } } -// TODO(tsion): Upstream these methods into rustc::ty::layout. +// TODO(solson): Upstream these methods into rustc::ty::layout. trait IntegerExt { fn size(self) -> Size; diff --git a/src/memory.rs b/src/memory.rs index bf53c599bd775..a42fd54669b63 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -54,7 +54,7 @@ impl Memory { alloc_map: HashMap::new(), next_id: AllocId(0), - // FIXME(tsion): This should work for both 4 and 8, but it currently breaks some things + // FIXME(solson): This should work for both 4 and 8, but it currently breaks some things // when set to 4. pointer_size: 8, } @@ -75,11 +75,11 @@ impl Memory { } } - // TODO(tsion): Track which allocations were returned from __rust_allocate and report an error + // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> { if ptr.offset != 0 { - // TODO(tsion): Report error about non-__rust_allocate'd pointer. + // TODO(solson): Report error about non-__rust_allocate'd pointer. panic!() } @@ -99,15 +99,15 @@ impl Memory { Ok(()) } - // TODO(tsion): See comment on `reallocate`. + // TODO(solson): See comment on `reallocate`. pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> { if ptr.offset != 0 { - // TODO(tsion): Report error about non-__rust_allocate'd pointer. + // TODO(solson): Report error about non-__rust_allocate'd pointer. panic!() } if self.alloc_map.remove(&ptr.alloc_id).is_none() { - // TODO(tsion): Report error about erroneous free. This is blocked on properly tracking + // TODO(solson): Report error about erroneous free. This is blocked on properly tracking // already-dropped state since this if-statement is entered even in safe code without // it. } @@ -403,7 +403,7 @@ impl Memory { // Undefined bytes //////////////////////////////////////////////////////////////////////////////// - // FIXME(tsion): This is a very naive, slow version. + // FIXME(solson): This is a very naive, slow version. fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. let mut v = Vec::with_capacity(size); diff --git a/src/primval.rs b/src/primval.rs index ad96fbe7d4199..19faadc962e16 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -29,7 +29,7 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul BitAnd => $v($l & $r), BitOr => $v($l | $r), - // TODO(tsion): Can have differently-typed RHS. + // TODO(solson): Can have differently-typed RHS. Shl => $v($l << $r), Shr => $v($l >> $r), diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index b5bb7c7dbdb9f..1ec3c5e0bb98a 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute, box_syntax)] #![allow(dead_code, unused_attributes)] -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::sync::Arc; @@ -13,8 +13,8 @@ fn rc_cell() -> Rc> { r } -// TODO(tsion): borrow code needs to evaluate string statics via Lvalue::Static -// TODO(tsion): also requires destructors to run for the second borrow to work +// TODO(solson): also requires destructors to run for the second borrow to work +// TODO(solson): needs StructWrappedNullablePointer support // #[miri_run] // fn rc_refcell() -> i32 { // let r = Rc::new(RefCell::new(42)); diff --git a/tex/report/miri-report.tex b/tex/report/miri-report.tex index 9ef9652eed1d3..f8bb37b911330 100644 --- a/tex/report/miri-report.tex +++ b/tex/report/miri-report.tex @@ -97,7 +97,7 @@ \section{First implementation} \subsection{Basic operation} To investigate the possibility of executing Rust at compile-time I wrote an interpreter for MIR -called Miri\footnote{\url{https://github.com/tsion/miri}}. The structure of the interpreter closely +called Miri\footnote{\url{https://github.com/solson/miri}}. The structure of the interpreter closely mirrors the structure of MIR itself. It starts executing a function by iterating the statement list in the starting basic block, translating the lvalue into a pointer and using the rvalue to decide what to write into that pointer. Evaluating the rvalue may involve reads (such as for the two sides @@ -271,7 +271,7 @@ \section{Deterministic execution} potentially unsafe code. When Miri encounters an unrecoverable error, it reports it via the Rust compiler's usual error reporting mechanism, pointing to the part of the original code where the error occurred. Below is an example from Miri's -repository.\footnote{\href{https://github.com/tsion/miri/blob/master/test/errors.rs}{miri/test/errors.rs}} +repository.\footnote{\href{https://github.com/solson/miri/blob/master/test/errors.rs}{miri/test/errors.rs}} \begin{minted}[autogobble]{rust} let b = Box::new(42); From 6d9a74885827e6e3e8e2fce857e2e45c80da7c09 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 20:44:42 -0600 Subject: [PATCH 0274/1096] Handle size_of_val for sized types. --- src/interpreter.rs | 10 ++++++++++ tests/compile-fail/bugs/memcmp.rs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d1a395bbf3438..55f94ce41dc33 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -574,6 +574,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_uint(dest, size, dest_size)?; } + "size_of_val" => { + let ty = *substs.types.get(subst::FnSpace, 0); + if self.type_is_sized(ty) { + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, dest_size)?; + } else { + panic!("unimplemented: size_of_val::<{:?}>", ty); + } + } + "transmute" => { let ty = *substs.types.get(subst::FnSpace, 0); self.move_(args[0], dest, ty)?; diff --git a/tests/compile-fail/bugs/memcmp.rs b/tests/compile-fail/bugs/memcmp.rs index d854f21ca317e..3fd41e7b3dd2e 100644 --- a/tests/compile-fail/bugs/memcmp.rs +++ b/tests/compile-fail/bugs/memcmp.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -// error-pattern:can't handle intrinsic: size_of_val +// error-pattern:can't call C ABI function: memcmp #[miri_run] fn memcmp() { From b9c37124be40c82d5bc870c87dbe91f05a979b16 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 21:01:12 -0600 Subject: [PATCH 0275/1096] Handle size_of_val for slice types. --- src/interpreter.rs | 12 +++++++++++- tests/run-pass/arrays.rs | 2 +- tests/run-pass/calls.rs | 9 +-------- tests/run-pass/intrinsics.rs | 13 +++++++++++++ 4 files changed, 26 insertions(+), 10 deletions(-) create mode 100755 tests/run-pass/intrinsics.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 55f94ce41dc33..3c91998b4a768 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -580,7 +580,17 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { - panic!("unimplemented: size_of_val::<{:?}>", ty); + match ty.sty { + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty) as u64; + let ptr_size = self.memory.pointer_size as isize; + let n = self.memory.read_usize(args[0].offset(ptr_size))?; + self.memory.write_uint(dest, n * elem_size, dest_size)?; + } + + _ => panic!("unimplemented: size_of_val::<{:?}>", ty), + } } } diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index 1fbf24b46684e..d433310a6a89d 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -46,7 +46,7 @@ fn slice_index() -> u8 { #[miri_run] fn main() { - //assert_eq!(empty_array(), []); + // assert_eq!(empty_array(), []); assert_eq!(index_unsafe(), 20); assert_eq!(index(), 20); assert_eq!(slice_index(), 106); diff --git a/tests/run-pass/calls.rs b/tests/run-pass/calls.rs index 68b3581456275..be975320bba93 100644 --- a/tests/run-pass/calls.rs +++ b/tests/run-pass/calls.rs @@ -33,17 +33,10 @@ fn cross_crate_fn_call() -> i64 { if 1i32.is_positive() { 1 } else { 0 } } -// Test one of the simplest intrinsics. -#[miri_run] -fn test_size_of() -> usize { - ::std::mem::size_of::>() -} - #[miri_run] fn main() { assert_eq!(call(), 2); assert_eq!(factorial_recursive(), 3628800); - //assert_eq!(call_generic(), (42, true)); + assert_eq!(call_generic(), (42, true)); assert_eq!(cross_crate_fn_call(), 1); - //assert_eq!(test_size_of(), 8); } diff --git a/tests/run-pass/intrinsics.rs b/tests/run-pass/intrinsics.rs new file mode 100755 index 0000000000000..5666e191fd373 --- /dev/null +++ b/tests/run-pass/intrinsics.rs @@ -0,0 +1,13 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +use std::mem::{size_of, size_of_val}; + +#[miri_run] +fn main() { + assert_eq!(size_of::>(), 8); + assert_eq!(size_of_val(&()), 0); + assert_eq!(size_of_val(&42), 4); + assert_eq!(size_of_val(&[] as &[i32]), 0); + assert_eq!(size_of_val(&[1, 2, 3] as &[i32]), 12); +} From 78caee20c7e15f286147fc1a35fb0682cb046ab9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 21:03:53 -0600 Subject: [PATCH 0276/1096] Add test for size_of_val::. --- tests/run-pass/intrinsics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-pass/intrinsics.rs b/tests/run-pass/intrinsics.rs index 5666e191fd373..ef7fa0d986135 100755 --- a/tests/run-pass/intrinsics.rs +++ b/tests/run-pass/intrinsics.rs @@ -10,4 +10,5 @@ fn main() { assert_eq!(size_of_val(&42), 4); assert_eq!(size_of_val(&[] as &[i32]), 0); assert_eq!(size_of_val(&[1, 2, 3] as &[i32]), 12); + assert_eq!(size_of_val("foobar"), 6); } From 2d325034093a336df20f22f7edb3df9ef83cac47 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 21:53:20 -0600 Subject: [PATCH 0277/1096] Support C ABI memcmp function. --- src/interpreter.rs | 37 +++++++++++++++++++++++++++---- tests/compile-fail/bugs/memcmp.rs | 11 --------- tests/run-pass/arrays.rs | 4 +--- tests/run-pass/c_enums.rs | 4 ++-- 4 files changed, 36 insertions(+), 20 deletions(-) delete mode 100644 tests/compile-fail/bugs/memcmp.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 3c91998b4a768..24f0e859bd824 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -346,14 +346,22 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match fn_ty.sig.0.output { ty::FnConverging(ty) => { let size = self.type_size(ty); - self.call_intrinsic(&name, substs, args, - return_ptr.unwrap(), size)? + let ret = return_ptr.unwrap(); + self.call_intrinsic(&name, substs, args, ret, size)? } ty::FnDiverging => unimplemented!(), } } - Abi::C => self.call_c_abi(def_id, args, return_ptr.unwrap())?, + Abi::C => { + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty); + self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? + } + ty::FnDiverging => unimplemented!(), + } + } Abi::Rust | Abi::RustCall => { // TODO(solson): Adjust the first argument when calling a Fn or @@ -613,7 +621,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { &mut self, def_id: DefId, args: &[mir::Operand<'tcx>], - dest: Pointer + dest: Pointer, + dest_size: usize, ) -> EvalResult { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); @@ -641,6 +650,26 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_ptr(dest, ptr)?; } + "memcmp" => { + let left = self.memory.read_ptr(args[0])?; + let right = self.memory.read_ptr(args[1])?; + let n = self.memory.read_usize(args[2])? as usize; + + let result = { + let left_bytes = self.memory.read_bytes(left, n)?; + let right_bytes = self.memory.read_bytes(right, n)?; + + use std::cmp::Ordering::*; + match left_bytes.cmp(right_bytes) { + Less => -1, + Equal => 0, + Greater => 1, + } + }; + + self.memory.write_int(dest, result, dest_size)?; + } + _ => panic!("can't call C ABI function: {}", link_name), } diff --git a/tests/compile-fail/bugs/memcmp.rs b/tests/compile-fail/bugs/memcmp.rs deleted file mode 100644 index 3fd41e7b3dd2e..0000000000000 --- a/tests/compile-fail/bugs/memcmp.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -// error-pattern:can't call C ABI function: memcmp - -#[miri_run] -fn memcmp() { - assert_eq!("", ""); -} - -fn main() {} diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index d433310a6a89d..db4f999a8b043 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -46,13 +46,11 @@ fn slice_index() -> u8 { #[miri_run] fn main() { - // assert_eq!(empty_array(), []); + assert_eq!(empty_array(), []); assert_eq!(index_unsafe(), 20); assert_eq!(index(), 20); assert_eq!(slice_index(), 106); - /* assert_eq!(big_array(), [5, 4, 3, 2, 1]); assert_eq!(array_array(), [[5, 4], [3, 2], [1, 0]]); assert_eq!(array_repeat(), [42; 8]); - */ } diff --git a/tests/run-pass/c_enums.rs b/tests/run-pass/c_enums.rs index fd537a37b0439..60790fef439fc 100644 --- a/tests/run-pass/c_enums.rs +++ b/tests/run-pass/c_enums.rs @@ -33,7 +33,7 @@ fn unsafe_match() -> bool { #[miri_run] fn main() { - // assert_eq!(foo(), [42, 43, 100]); - // assert_eq!(signed(), [-42, -41, 100]); + assert_eq!(foo(), [42, 43, 100]); + assert_eq!(signed(), [-42, -41, 100]); assert!(unsafe_match()); } From f63206ed2f37c70273bc75822a538c2c22a017f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 9 May 2016 23:41:57 -0600 Subject: [PATCH 0278/1096] Handle discriminant_value intrinsic. --- src/interpreter.rs | 100 ++++++++++-------- tests/compile-fail/bugs/discriminant_value.rs | 9 -- tests/run-pass/sums.rs | 12 +-- 3 files changed, 64 insertions(+), 57 deletions(-) delete mode 100644 tests/compile-fail/bugs/discriminant_value.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 24f0e859bd824..9d8a9e6110bf2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -297,35 +297,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Switch { ref discr, ref targets, adt_def } => { let adt_ptr = self.eval_lvalue(discr)?.to_ptr(); - let adt_layout = self.type_layout(self.lvalue_ty(discr)); - - match *adt_layout { - Layout::General { discr, .. } | Layout::CEnum { discr, .. } => { - let discr_size = discr.size().bytes(); - let discr_val = self.memory.read_uint(adt_ptr, discr_size as usize)?; - - let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u64_unchecked()); - - match matching { - Some(i) => TerminatorTarget::Block(targets[i]), - None => return Err(EvalError::InvalidDiscriminant), - } - } - - Layout::RawNullablePointer { nndiscr, .. } => { - let is_null = match self.memory.read_usize(adt_ptr) { - Ok(0) => true, - Ok(_) | Err(EvalError::ReadPointerAsBytes) => false, - Err(e) => return Err(e), - }; - - assert!(nndiscr == 0 || nndiscr == 1); - let target = if is_null { 1 - nndiscr } else { nndiscr }; - TerminatorTarget::Block(targets[target as usize]) - } - - _ => panic!("attempted to switch on non-aggregate type"), + let adt_ty = self.lvalue_ty(discr); + let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; + let matching = adt_def.variants.iter() + .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + + match matching { + Some(i) => TerminatorTarget::Block(targets[i]), + None => return Err(EvalError::InvalidDiscriminant), } } @@ -477,6 +456,36 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } + fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: ty::Ty<'tcx>) -> EvalResult { + use rustc::ty::layout::Layout::*; + let adt_layout = self.type_layout(adt_ty); + + let discr_val = match *adt_layout { + General { discr, .. } | CEnum { discr, .. } => { + let discr_size = discr.size().bytes(); + self.memory.read_uint(adt_ptr, discr_size as usize)? + } + + RawNullablePointer { nndiscr, .. } => { + let not_null = match self.memory.read_usize(adt_ptr) { + Ok(0) => false, + Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, + Err(e) => return Err(e), + }; + assert!(nndiscr == 0 || nndiscr == 1); + if not_null { nndiscr } else { 1 - nndiscr } + } + + StructWrappedNullablePointer { .. } => unimplemented!(), + + // The discriminant_value intrinsic returns 0 for non-sum types. + Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | + Vector { .. } => 0, + }; + + Ok(discr_val) + } + fn call_intrinsic( &mut self, name: &str, @@ -491,6 +500,19 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let args = args_res?; match name { + // FIXME(solson): Handle different integer types correctly. + "add_with_overflow" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.type_size(ty); + let left = self.memory.read_int(args[0], size)?; + let right = self.memory.read_int(args[1], size)?; + let (n, overflowed) = unsafe { + ::std::intrinsics::add_with_overflow::(left, right) + }; + self.memory.write_int(dest, n, size)?; + self.memory.write_bool(dest.offset(size as isize), overflowed)?; + } + "assume" => {} "copy_nonoverlapping" => { @@ -502,6 +524,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.copy(src, dest, count as usize * elem_size)?; } + "discriminant_value" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let adt_ptr = self.memory.read_ptr(args[0])?; + let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + self.memory.write_uint(dest, discr_val, dest_size)?; + } + "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); let arg_size = self.type_size(arg_ty); @@ -520,19 +549,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.move_(args[1], ptr, ty)?; } - // FIXME(solson): Handle different integer types correctly. - "add_with_overflow" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); - let left = self.memory.read_int(args[0], size)?; - let right = self.memory.read_int(args[1], size)?; - let (n, overflowed) = unsafe { - ::std::intrinsics::add_with_overflow::(left, right) - }; - self.memory.write_int(dest, n, size)?; - self.memory.write_bool(dest.offset(size as isize), overflowed)?; - } - // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); diff --git a/tests/compile-fail/bugs/discriminant_value.rs b/tests/compile-fail/bugs/discriminant_value.rs deleted file mode 100644 index f0a487e20df72..0000000000000 --- a/tests/compile-fail/bugs/discriminant_value.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -// error-pattern:can't handle intrinsic: discriminant_value - -#[miri_run] -fn main() { - assert_eq!(None::, None); -} diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index d6eddc69fc9a5..9b7ddb43b7c78 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -57,12 +57,12 @@ fn two_nones() -> (Option, Option) { #[miri_run] fn main() { - //assert_eq!(two_nones(), (None, None)); + assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); assert_eq!(match_opt_none(), 42); - //assert_eq!(return_some(), Some(42)); - //assert_eq!(return_none(), None); - //assert_eq!(return_false(), MyBool::False); - //assert_eq!(return_true(), MyBool::True); - //assert_eq!(return_unit(), Unit::Unit); + assert_eq!(return_some(), Some(42)); + assert_eq!(return_none(), None); + assert_eq!(return_false(), MyBool::False(())); + assert_eq!(return_true(), MyBool::True(())); + assert_eq!(return_unit(), Unit::Unit(())); } From 82dfa7278ba529f22e24c7d1dfad8b50f40f9819 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 10 May 2016 11:50:11 -0600 Subject: [PATCH 0279/1096] Correct license in Cargo.toml. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f9ae9e7d4e619..9b013809ba6dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Scott Olson "] description = "An experimental interpreter for Rust MIR." -license = "ISC" +license = "MIT/Apache-2.0" name = "miri" repository = "https://github.com/solson/miri" version = "0.1.0" From 3ba923701f0b3c7e062c7ffce92fec62bb00de90 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 13 May 2016 22:34:50 -0600 Subject: [PATCH 0280/1096] Update for changes in rustc nightly. --- src/interpreter.rs | 85 ++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 9d8a9e6110bf2..f30e681cb4dd8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,3 @@ -use rustc::infer; use rustc::middle::const_val; use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; @@ -25,7 +24,7 @@ const TRACE_EXECUTION: bool = true; struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. - tcx: &'a TyCtxt<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, /// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items. mir_map: &'a MirMap<'tcx>, @@ -124,7 +123,7 @@ enum TerminatorTarget { } impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { - fn new(tcx: &'a TyCtxt<'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { + fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { GlobalEvalContext { tcx: tcx, mir_map: mir_map, @@ -367,7 +366,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let last_ty = self.operand_ty(last_arg); let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { - (&ty::TyTuple(ref fields), + (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { let offsets = iter::once(0) .chain(variant.offset_after_field.iter() @@ -1063,7 +1062,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { let substituted = ty.subst(self.tcx, self.substs()); - infer::normalize_associated_type(self.tcx, &substituted) + self.tcx.normalize_associated_type(&substituted) } fn type_needs_drop(&self, ty: ty::Ty<'tcx>) -> bool { @@ -1080,7 +1079,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { - ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) + ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } fn type_size(&self, ty: ty::Ty<'tcx>) -> usize { @@ -1091,10 +1090,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty); - let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); - - // TODO(solson): Report this error properly. - ty.layout(&infcx).unwrap() + self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + // TODO(solson): Report this error properly. + ty.layout(&infcx).unwrap() + }) } pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { @@ -1173,30 +1172,31 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - let infcx = infer::normalizing_infer_ctxt(self.tcx, &self.tcx.tables, ProjectionMode::Any); - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // Currently, we use a fulfillment context to completely resolve all nested obligations. - // This is because they can inform the inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infer::drain_fulfillment_cx_or_panic( - DUMMY_SP, &infcx, &mut fulfill_cx, &vtable - ) + self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // Currently, we use a fulfillment context to completely resolve all nested obligations. + // This is because they can inform the inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) + }) } /// Trait method, which has to be resolved to an impl method. - pub fn trait_method(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) - -> (DefId, &'tcx Substs<'tcx>) - { + pub fn trait_method( + &self, + def_id: DefId, + substs: &'tcx Substs<'tcx> + ) -> (DefId, &'tcx Substs<'tcx>) { let method_item = self.tcx.impl_or_trait_item(def_id); let trait_id = method_item.container().id(); let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); @@ -1279,8 +1279,8 @@ pub struct ImplMethod<'tcx> { } /// Locates the applicable definition of a method, given its name. -pub fn get_impl_method<'tcx>( - tcx: &TyCtxt<'tcx>, +pub fn get_impl_method<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_def_id: DefId, substs: &'tcx Substs<'tcx>, name: ast::Name, @@ -1289,23 +1289,34 @@ pub fn get_impl_method<'tcx>( let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { Some(node_item) => { + let substs = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let substs = traits::translate_substs(&infcx, impl_def_id, + substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("trans::meth::get_impl_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); ImplMethod { method: node_item.item, - substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node), + substs: substs, is_provided: node_item.node.is_from_trait(), } } None => { - bug!("method {:?} not found in {:?}", name, impl_def_id); + bug!("method {:?} not found in {:?}", name, impl_def_id) } } } -pub fn interpret_start_points<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) { +pub fn interpret_start_points<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir_map: &MirMap<'tcx>, +) { for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { use syntax::attr::AttrMetaMethods; From 42b490377548ac0723828d7f441e5885e68afb4b Mon Sep 17 00:00:00 2001 From: qres Date: Sat, 14 May 2016 15:13:06 +1000 Subject: [PATCH 0281/1096] fix tuple example in slides --- tex/final-presentation/slides.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tex/final-presentation/slides.tex b/tex/final-presentation/slides.tex index 76c25466e0743..c5e1f51ba19c6 100644 --- a/tex/final-presentation/slides.tex +++ b/tex/final-presentation/slides.tex @@ -391,7 +391,7 @@ bb0: { var0 = arg0.0; // get the 1st part of the pair var1 = arg0.1; // get the 2nd part of the pair - return = (var0, var1); // build a new pair in the result + return = (var1, var0); // build a new pair in the result goto -> bb1; } From 8961063c605702435d1b1f699b6883b9722af87c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 25 May 2016 00:37:52 -0600 Subject: [PATCH 0282/1096] Handle some cases of StructWrappedNullablePointer. ... plus a bunch of minor refactorings. --- src/interpreter.rs | 161 ++++++++++++++---- .../bugs/struct_wrapped_nullable_pointer.rs | 23 --- 2 files changed, 130 insertions(+), 54 deletions(-) delete mode 100644 tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index f30e681cb4dd8..a7df8748ae402 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -6,7 +6,7 @@ use rustc::traits::{self, ProjectionMode}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; use std::ops::{Deref, DerefMut}; @@ -413,7 +413,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(target) } - fn drop(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> { + fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { if !self.type_needs_drop(ty) { self.log(1, || print!("no need to drop {:?}", ty)); return Ok(()); @@ -455,7 +455,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: ty::Ty<'tcx>) -> EvalResult { + fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty); @@ -466,16 +466,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } RawNullablePointer { nndiscr, .. } => { - let not_null = match self.memory.read_usize(adt_ptr) { - Ok(0) => false, - Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, - Err(e) => return Err(e), - }; - assert!(nndiscr == 0 || nndiscr == 1); - if not_null { nndiscr } else { 1 - nndiscr } + self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? } - StructWrappedNullablePointer { .. } => unimplemented!(), + StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { + let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield); + let nonnull = adt_ptr.offset(offset.bytes() as isize); + self.read_nonnull_discriminant_value(nonnull, nndiscr)? + } // The discriminant_value intrinsic returns 0 for non-sum types. Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | @@ -485,6 +483,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(discr_val) } + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult { + let not_null = match self.memory.read_usize(ptr) { + Ok(0) => false, + Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, + Err(e) => return Err(e), + }; + assert!(nndiscr == 0 || nndiscr == 1); + Ok(if not_null { nndiscr } else { 1 - nndiscr }) + } + fn call_intrinsic( &mut self, name: &str, @@ -793,6 +801,23 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } + StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield } => { + if let mir::AggregateKind::Adt(_, variant, _) = *kind { + if nndiscr == variant as u64 { + let offsets = iter::once(0) + .chain(nonnull.offset_after_field.iter().map(|s| s.bytes())); + try!(self.assign_fields(dest, offsets, operands)); + } else { + assert_eq!(operands.len(), 0); + let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield); + let dest = dest.offset(offset.bytes() as isize); + try!(self.memory.write_isize(dest, 0)); + } + } else { + panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); + } + } + CEnum { discr, signed, .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { @@ -900,6 +925,73 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } + fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> Size { + // Skip the constant 0 at the start meant for LLVM GEP. + let mut path = discrfield.iter().skip(1).map(|&i| i as usize); + + // Handle the field index for the outer non-null variant. + let inner_ty = match ty.sty { + ty::TyEnum(adt_def, substs) => { + let variant = &adt_def.variants[nndiscr as usize]; + let index = path.next().unwrap(); + let field = &variant.fields[index]; + field.ty(self.tcx, substs) + } + _ => panic!( + "non-enum for StructWrappedNullablePointer: {}", + ty, + ), + }; + + self.field_path_offset(inner_ty, path) + } + + fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> Size { + let mut offset = Size::from_bytes(0); + + // Skip the initial 0 intended for LLVM GEP. + for field_index in path { + let field_offset = self.get_field_offset(ty, field_index); + ty = self.get_field_ty(ty, field_index); + offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); + } + + offset + } + + fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> Ty<'tcx> { + match ty.sty { + ty::TyStruct(adt_def, substs) => { + adt_def.struct_variant().fields[field_index].ty(self.tcx, substs) + } + + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyBox(ty) => { + assert_eq!(field_index, 0); + ty + } + _ => panic!("can't handle type: {:?}", ty), + } + } + + fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> Size { + let layout = self.type_layout(ty); + + use rustc::ty::layout::Layout::*; + match *layout { + Univariant { .. } => { + assert_eq!(field_index, 0); + Size::from_bytes(0) + } + FatPointer { .. } => { + let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size; + Size::from_bytes(bytes as u64) + } + _ => panic!("can't handle type: {:?}, with layout: {:?}", ty, layout), + } + } + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { use rustc::mir::repr::Operand::*; match *op { @@ -944,19 +1036,21 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, _) => { + use rustc::ty::layout::Layout::*; let variant = match *base_layout { - Layout::Univariant { ref variant, .. } => variant, - Layout::General { ref variants, .. } => { + Univariant { ref variant, .. } => variant, + General { ref variants, .. } => { if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { &variants[variant_idx] } else { panic!("field access on enum had no variant index"); } } - Layout::RawNullablePointer { .. } => { + RawNullablePointer { .. } => { assert_eq!(field.index(), 0); return Ok(base); } + StructWrappedNullablePointer { ref nonnull, .. } => nonnull, _ => panic!("field access on non-product type: {:?}", base_layout), }; @@ -964,15 +1058,20 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { base.ptr.offset(offset as isize) }, - Downcast(_, variant) => match *base_layout { - Layout::General { discr, .. } => { - return Ok(Lvalue { - ptr: base.ptr.offset(discr.size().bytes() as isize), - extra: LvalueExtra::DowncastVariant(variant), - }); + Downcast(_, variant) => { + use rustc::ty::layout::Layout::*; + match *base_layout { + General { discr, .. } => { + return Ok(Lvalue { + ptr: base.ptr.offset(discr.size().bytes() as isize), + extra: LvalueExtra::DowncastVariant(variant), + }); + } + RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { + return Ok(base); + } + _ => panic!("variant downcast on non-aggregate: {:?}", base_layout), } - Layout::RawNullablePointer { .. } => return Ok(base), - _ => panic!("variant downcast on non-aggregate type: {:?}", base_layout), }, Deref => { @@ -1052,24 +1151,24 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> ty::Ty<'tcx> { + fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx)) } - fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> ty::Ty<'tcx> { + fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(self.mir().operand_ty(self.tcx, operand)) } - fn monomorphize(&self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { + fn monomorphize(&self, ty: Ty<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, self.substs()); self.tcx.normalize_associated_type(&substituted) } - fn type_needs_drop(&self, ty: ty::Ty<'tcx>) -> bool { + fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) } - fn move_(&mut self, src: Pointer, dest: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> { + fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { let size = self.type_size(ty); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { @@ -1078,15 +1177,15 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool { + fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - fn type_size(&self, ty: ty::Ty<'tcx>) -> usize { + fn type_size(&self, ty: Ty<'tcx>) -> usize { self.type_layout(ty).size(&self.tcx.data_layout).bytes() as usize } - fn type_layout(&self, ty: ty::Ty<'tcx>) -> &'tcx Layout { + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty); @@ -1096,7 +1195,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { }) } - pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult { + pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { use syntax::ast::{IntTy, UintTy}; let val = match ty.sty { ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), diff --git a/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs b/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs deleted file mode 100644 index 880ca42d45557..0000000000000 --- a/tests/compile-fail/bugs/struct_wrapped_nullable_pointer.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![feature(custom_attribute, box_syntax)] -#![allow(dead_code, unused_attributes)] - -// error-pattern:can't handle destination layout StructWrappedNullablePointer - -use std::cell::RefCell; -use std::rc::Rc; - -struct Loop(Rc>>); - -#[miri_run] -fn rc_reference_cycle() -> Loop { - let a = Rc::new(RefCell::new(None)); - let b = a.clone(); - *a.borrow_mut() = Some(Loop(b)); - Loop(a) -} - -#[miri_run] -fn main() { - let x = rc_reference_cycle().0; - assert!(x.borrow().is_some()); -} From c55b3666eaeb74c33eea5d4b6a8cc4b974912d9e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 27 May 2016 16:12:17 +0200 Subject: [PATCH 0283/1096] clippy nit --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index a7df8748ae402..d83c8eb2da8ff 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1365,7 +1365,7 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { fn deref(&self) -> &mir::Mir<'tcx> { match *self { CachedMir::Ref(r) => r, - CachedMir::Owned(ref rc) => &rc, + CachedMir::Owned(ref rc) => rc, } } } From 3ec813e4e508f3608e51c278626ea838d0351c2d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 13:40:20 +0200 Subject: [PATCH 0284/1096] add benchmarks --- benches/smoke.rs | 76 +++++++++++++++++++++++++++++++++++++++++ benches/smoke_helper.rs | 7 ++++ 2 files changed, 83 insertions(+) create mode 100644 benches/smoke.rs create mode 100644 benches/smoke_helper.rs diff --git a/benches/smoke.rs b/benches/smoke.rs new file mode 100644 index 0000000000000..992f435a501ce --- /dev/null +++ b/benches/smoke.rs @@ -0,0 +1,76 @@ +#![feature(custom_attribute, test)] +#![feature(rustc_private)] +#![allow(unused_attributes)] + +extern crate getopts; +extern crate miri; +extern crate rustc; +extern crate rustc_driver; + +use miri::interpreter; +use rustc::session::Session; +use rustc_driver::{driver, CompilerCalls}; +use std::cell::RefCell; +use std::rc::Rc; + +extern crate test; +use test::Bencher; + +mod smoke_helper; + +#[bench] +fn noop(bencher: &mut Bencher) { + bencher.iter(|| { + smoke_helper::main(); + }) +} + +/* +// really slow +#[bench] +fn noop_miri_full(bencher: &mut Bencher) { + let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + bencher.iter(|| { + let mut process = std::process::Command::new("target/release/miri"); + process.arg("benches/smoke_helper.rs") + .arg("--sysroot").arg(&path); + let output = process.output().unwrap(); + if !output.status.success() { + println!("{}", String::from_utf8(output.stdout).unwrap()); + println!("{}", String::from_utf8(output.stderr).unwrap()); + panic!("failed to run miri"); + } + }) +} +*/ + +#[bench] +fn noop_miri_interpreter(bencher: &mut Bencher) { + let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + rustc_driver::run_compiler(&[ + "miri".to_string(), "benches/smoke_helper.rs".to_string(), "--sysroot".to_string(), path.to_string(), + ], &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher)))); +} + +struct MiriCompilerCalls<'a>(Rc>); + +impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { + fn build_controller( + &mut self, + _: &Session, + _: &getopts::Matches + ) -> driver::CompileController<'a> { + let mut control: driver::CompileController<'a> = driver::CompileController::basic(); + + let bencher = self.0.clone(); + + control.after_analysis.callback = Box::new(move |state| { + state.session.abort_if_errors(); + bencher.borrow_mut().iter(|| { + interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + }) + }); + + control + } +} diff --git a/benches/smoke_helper.rs b/benches/smoke_helper.rs new file mode 100644 index 0000000000000..e8691f244c02f --- /dev/null +++ b/benches/smoke_helper.rs @@ -0,0 +1,7 @@ +#![feature(custom_attribute)] +#![allow(unused_attributes)] + +#[miri_run] +#[inline(never)] +pub fn main() { +} From cecae8050eb30f282c574867ffd84e9a2bbf8e45 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 13:40:46 +0200 Subject: [PATCH 0285/1096] remove unnecessary printlns for benchmarks --- src/interpreter.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index d83c8eb2da8ff..8e8565b50c55c 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1422,12 +1422,16 @@ pub fn interpret_start_points<'a, 'tcx>( if attr.check_name("miri_run") { let item = tcx.map.expect_item(id); - println!("Interpreting: {}", item.name); + if TRACE_EXECUTION { + println!("Interpreting: {}", item.name); + } let mut gecx = GlobalEvalContext::new(tcx, mir_map); let mut fecx = FnEvalContext::new(&mut gecx); match fecx.call_nested(mir) { - Ok(Some(return_ptr)) => fecx.memory.dump(return_ptr.alloc_id), + Ok(Some(return_ptr)) => if TRACE_EXECUTION { + fecx.memory.dump(return_ptr.alloc_id); + }, Ok(None) => println!("(diverging function returned)"), Err(_e) => { // TODO(solson): Detect whether the error was already reported or not. @@ -1435,7 +1439,9 @@ pub fn interpret_start_points<'a, 'tcx>( } } - println!(""); + if TRACE_EXECUTION { + println!(""); + } } } } From 8e1fa8c13c7cd1a2145414ef4e4fa4a3dc8d1159 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 14:05:50 +0200 Subject: [PATCH 0286/1096] add more benchmarks --- benches/fibonacci.rs | 36 ++++++++++++++++++++ benches/fibonacci_helper.rs | 16 +++++++++ benches/fibonacci_helper_iterative.rs | 19 +++++++++++ benches/miri_helper.rs | 47 +++++++++++++++++++++++++++ benches/smoke.rs | 41 ++--------------------- 5 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 benches/fibonacci.rs create mode 100644 benches/fibonacci_helper.rs create mode 100644 benches/fibonacci_helper_iterative.rs create mode 100644 benches/miri_helper.rs diff --git a/benches/fibonacci.rs b/benches/fibonacci.rs new file mode 100644 index 0000000000000..1f8a2aafc9ed9 --- /dev/null +++ b/benches/fibonacci.rs @@ -0,0 +1,36 @@ +#![feature(custom_attribute, test)] +#![feature(rustc_private)] +#![allow(unused_attributes)] + +extern crate test; +use test::Bencher; + +mod fibonacci_helper; + +#[bench] +fn fib(bencher: &mut Bencher) { + bencher.iter(|| { + fibonacci_helper::main(); + }) +} + +mod miri_helper; + +#[bench] +fn fib_miri(bencher: &mut Bencher) { + miri_helper::run("fibonacci_helper", bencher); +} + +mod fibonacci_helper_iterative; + +#[bench] +fn fib_iter(bencher: &mut Bencher) { + bencher.iter(|| { + fibonacci_helper_iterative::main(); + }) +} + +#[bench] +fn fib_iter_miri(bencher: &mut Bencher) { + miri_helper::run("fibonacci_helper_iterative", bencher); +} diff --git a/benches/fibonacci_helper.rs b/benches/fibonacci_helper.rs new file mode 100644 index 0000000000000..cddfff9c2c92d --- /dev/null +++ b/benches/fibonacci_helper.rs @@ -0,0 +1,16 @@ +#![feature(custom_attribute)] +#![allow(unused_attributes)] + +#[miri_run] +#[inline(never)] +pub fn main() { + assert_eq!(fib(10), 55); +} + +fn fib(n: usize) -> usize { + if n <= 2 { + 1 + } else { + fib(n - 1) + fib(n - 2) + } +} diff --git a/benches/fibonacci_helper_iterative.rs b/benches/fibonacci_helper_iterative.rs new file mode 100644 index 0000000000000..486d8c2e8a868 --- /dev/null +++ b/benches/fibonacci_helper_iterative.rs @@ -0,0 +1,19 @@ +#![feature(custom_attribute)] +#![allow(unused_attributes)] + +#[miri_run] +#[inline(never)] +pub fn main() { + assert_eq!(fib(10), 55); +} + +fn fib(n: usize) -> usize { + let mut a = 0; + let mut b = 1; + for _ in 0..n { + let c = a; + a = b; + b = c + b; + } + a +} diff --git a/benches/miri_helper.rs b/benches/miri_helper.rs new file mode 100644 index 0000000000000..54c15a27ed884 --- /dev/null +++ b/benches/miri_helper.rs @@ -0,0 +1,47 @@ +#![feature(custom_attribute, test)] +#![feature(rustc_private)] +#![allow(unused_attributes)] + +extern crate getopts; +extern crate miri; +extern crate rustc; +extern crate rustc_driver; +extern crate test; + +use self::miri::interpreter; +use self::rustc::session::Session; +use self::rustc_driver::{driver, CompilerCalls}; +use std::cell::RefCell; +use std::rc::Rc; +use std::env::var; +use test::Bencher; + +pub struct MiriCompilerCalls<'a>(Rc>); + +pub fn run(filename: &str, bencher: &mut Bencher) { + let path = var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + rustc_driver::run_compiler(&[ + "miri".to_string(), format!("benches/{}.rs", filename), "--sysroot".to_string(), path.to_string(), + ], &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher)))); +} + +impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { + fn build_controller( + &mut self, + _: &Session, + _: &getopts::Matches + ) -> driver::CompileController<'a> { + let mut control: driver::CompileController<'a> = driver::CompileController::basic(); + + let bencher = self.0.clone(); + + control.after_analysis.callback = Box::new(move |state| { + state.session.abort_if_errors(); + bencher.borrow_mut().iter(|| { + interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + }) + }); + + control + } +} diff --git a/benches/smoke.rs b/benches/smoke.rs index 992f435a501ce..43baf486df397 100644 --- a/benches/smoke.rs +++ b/benches/smoke.rs @@ -2,17 +2,6 @@ #![feature(rustc_private)] #![allow(unused_attributes)] -extern crate getopts; -extern crate miri; -extern crate rustc; -extern crate rustc_driver; - -use miri::interpreter; -use rustc::session::Session; -use rustc_driver::{driver, CompilerCalls}; -use std::cell::RefCell; -use std::rc::Rc; - extern crate test; use test::Bencher; @@ -44,33 +33,9 @@ fn noop_miri_full(bencher: &mut Bencher) { } */ +mod miri_helper; + #[bench] fn noop_miri_interpreter(bencher: &mut Bencher) { - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - rustc_driver::run_compiler(&[ - "miri".to_string(), "benches/smoke_helper.rs".to_string(), "--sysroot".to_string(), path.to_string(), - ], &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher)))); -} - -struct MiriCompilerCalls<'a>(Rc>); - -impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { - fn build_controller( - &mut self, - _: &Session, - _: &getopts::Matches - ) -> driver::CompileController<'a> { - let mut control: driver::CompileController<'a> = driver::CompileController::basic(); - - let bencher = self.0.clone(); - - control.after_analysis.callback = Box::new(move |state| { - state.session.abort_if_errors(); - bencher.borrow_mut().iter(|| { - interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); - }) - }); - - control - } + miri_helper::run("smoke_helper", bencher); } From 55ce704ae75f0005b0bc7821e7e23fe07db0b5ab Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 15:27:10 +0200 Subject: [PATCH 0287/1096] don't generate test suites --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 9b013809ba6dc..66bbc729e15fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,10 @@ version = "0.1.0" [[bin]] doc = false name = "miri" +test = false + +[lib] +test = false [dependencies] byteorder = "0.4.2" From b78ca5f7e1e07d1f6e6980a1af0d7f4315978362 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 15:27:52 +0200 Subject: [PATCH 0288/1096] replace `panic!`s with `Result` --- src/error.rs | 6 ++++++ src/interpreter.rs | 51 +++++++++++++++++++++++----------------------- src/memory.rs | 6 +++--- src/primval.rs | 37 ++++++++++++++++----------------- 4 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/error.rs b/src/error.rs index 65dd187fa0b60..ec75ab4452695 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,6 @@ use std::error::Error; use std::fmt; +use rustc::mir::repr as mir; #[derive(Clone, Debug)] pub enum EvalError { @@ -11,6 +12,8 @@ pub enum EvalError { ReadBytesAsPointer, InvalidPointerMath, ReadUndefBytes, + InvalidBoolOp(mir::BinOp), + Unimplemented(String), } pub type EvalResult = Result; @@ -34,6 +37,9 @@ impl Error for EvalError { "attempted to do math or a comparison on pointers into different allocations", EvalError::ReadUndefBytes => "attempted to read undefined bytes", + EvalError::InvalidBoolOp(_) => + "invalid boolean operation", + EvalError::Unimplemented(ref msg) => msg, } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 8e8565b50c55c..8734427f07f57 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -392,11 +392,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { TerminatorTarget::Call } - abi => panic!("can't handle function with {:?} ABI", abi), + abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), } } - _ => panic!("can't handle callee of type {:?}", func_ty), + _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), } } @@ -470,7 +470,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield); + let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); self.read_nonnull_discriminant_value(nonnull, nndiscr)? } @@ -620,7 +620,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_uint(dest, n * elem_size, dest_size)?; } - _ => panic!("unimplemented: size_of_val::<{:?}>", ty), + _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), } } } @@ -631,7 +631,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } "uninit" => self.memory.mark_definedness(dest, dest_size, false)?, - name => panic!("can't handle intrinsic: {}", name), + name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), } // Since we pushed no stack frame, the main loop will act @@ -693,7 +693,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_int(dest, result, dest_size)?; } - _ => panic!("can't call C ABI function: {}", link_name), + _ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))), } // Since we pushed no stack frame, the main loop will act @@ -748,7 +748,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let ptr = self.eval_operand(operand)?; let ty = self.operand_ty(operand); let val = self.read_primval(ptr, ty)?; - self.memory.write_primval(dest, primval::unary_op(un_op, val))?; + self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?; } Aggregate(ref kind, ref operands) => { @@ -809,7 +809,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { try!(self.assign_fields(dest, offsets, operands)); } else { assert_eq!(operands.len(), 0); - let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield); + let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; let dest = dest.offset(offset.bytes() as isize); try!(self.memory.write_isize(dest, 0)); } @@ -834,8 +834,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - _ => panic!("can't handle destination layout {:?} when assigning {:?}", - dest_layout, kind), + _ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))), } } @@ -904,7 +903,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.write_usize(len_ptr, length as u64)?; } - _ => panic!("can't handle cast: {:?}", rvalue), + _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), } } @@ -914,7 +913,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.memory.copy(src, dest, size)?; } - _ => panic!("can't handle cast: {:?}", rvalue), + _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), } } @@ -925,7 +924,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> Size { + fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -946,49 +945,49 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.field_path_offset(inner_ty, path) } - fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> Size { + fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult { let mut offset = Size::from_bytes(0); // Skip the initial 0 intended for LLVM GEP. for field_index in path { - let field_offset = self.get_field_offset(ty, field_index); - ty = self.get_field_ty(ty, field_index); + let field_offset = self.get_field_offset(ty, field_index)?; + ty = self.get_field_ty(ty, field_index)?; offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); } - offset + Ok(offset) } - fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> Ty<'tcx> { + fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult> { match ty.sty { ty::TyStruct(adt_def, substs) => { - adt_def.struct_variant().fields[field_index].ty(self.tcx, substs) + Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { assert_eq!(field_index, 0); - ty + Ok(ty) } - _ => panic!("can't handle type: {:?}", ty), + _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}", ty))), } } - fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> Size { + fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { let layout = self.type_layout(ty); use rustc::ty::layout::Layout::*; match *layout { Univariant { .. } => { assert_eq!(field_index, 0); - Size::from_bytes(0) + Ok(Size::from_bytes(0)) } FatPointer { .. } => { let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size; - Size::from_bytes(bytes as u64) + Ok(Size::from_bytes(bytes as u64)) } - _ => panic!("can't handle type: {:?}, with layout: {:?}", ty, layout), + _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))), } } @@ -1223,7 +1222,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Err(e) => return Err(e), } } else { - panic!("unimplemented: primitive read of fat pointer type: {:?}", ty); + return Err(EvalError::Unimplemented(format!("unimplemented: primitive read of fat pointer type: {:?}", ty))); } } diff --git a/src/memory.rs b/src/memory.rs index a42fd54669b63..d69e07ae2a2b8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -80,7 +80,7 @@ impl Memory { pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> { if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. - panic!() + return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } let alloc = self.get_mut(ptr.alloc_id)?; @@ -90,7 +90,7 @@ impl Memory { alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - unimplemented!() + return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation"))); // alloc.bytes.truncate(new_size); // alloc.undef_mask.len = new_size; // TODO: potentially remove relocations @@ -103,7 +103,7 @@ impl Memory { pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> { if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. - panic!() + return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } if self.alloc_map.remove(&ptr.alloc_id).is_none() { diff --git a/src/primval.rs b/src/primval.rs index 19faadc962e16..0b1658739d9d6 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -74,8 +74,7 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul BitOr => l | r, BitXor => l ^ r, BitAnd => l & r, - Add | Sub | Mul | Div | Rem | Shl | Shr => - panic!("invalid binary operation on booleans: {:?}", bin_op), + Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)), }) } @@ -99,33 +98,33 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul Le => Bool(l <= r), Gt => Bool(l > r), Ge => Bool(l >= r), - _ => unimplemented!(), + _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))), } } - _ => unimplemented!(), + (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))), }; Ok(val) } -pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal { +pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> EvalResult { use rustc::mir::repr::UnOp::*; use self::PrimVal::*; match (un_op, val) { - (Not, Bool(b)) => Bool(!b), - (Not, I8(n)) => I8(!n), - (Neg, I8(n)) => I8(-n), - (Not, I16(n)) => I16(!n), - (Neg, I16(n)) => I16(-n), - (Not, I32(n)) => I32(!n), - (Neg, I32(n)) => I32(-n), - (Not, I64(n)) => I64(!n), - (Neg, I64(n)) => I64(-n), - (Not, U8(n)) => U8(!n), - (Not, U16(n)) => U16(!n), - (Not, U32(n)) => U32(!n), - (Not, U64(n)) => U64(!n), - _ => unimplemented!(), + (Not, Bool(b)) => Ok(Bool(!b)), + (Not, I8(n)) => Ok(I8(!n)), + (Neg, I8(n)) => Ok(I8(-n)), + (Not, I16(n)) => Ok(I16(!n)), + (Neg, I16(n)) => Ok(I16(-n)), + (Not, I32(n)) => Ok(I32(!n)), + (Neg, I32(n)) => Ok(I32(-n)), + (Not, I64(n)) => Ok(I64(!n)), + (Neg, I64(n)) => Ok(I64(-n)), + (Not, U8(n)) => Ok(U8(!n)), + (Not, U16(n)) => Ok(U16(!n)), + (Not, U32(n)) => Ok(U32(!n)), + (Not, U64(n)) => Ok(U64(!n)), + _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))), } } From 12c2e5fab27daaff5dada1fbc18d0b193a488ecd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 May 2016 12:05:25 +0200 Subject: [PATCH 0289/1096] 4byte pointers --- src/error.rs | 13 ++++++++--- src/interpreter.rs | 42 ++++++++++++++++++++---------------- src/memory.rs | 19 ++++++++++------ tests/compile-fail/errors.rs | 6 ++++-- tests/compiletest.rs | 25 +++++++++++++-------- tests/run-pass/strings.rs | 5 +++++ 6 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/error.rs b/src/error.rs index ec75ab4452695..c82b7fa1ca61a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,11 @@ pub enum EvalError { DanglingPointerDeref, InvalidBool, InvalidDiscriminant, - PointerOutOfBounds, + PointerOutOfBounds { + offset: usize, + size: usize, + len: usize, + }, ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, @@ -27,7 +31,7 @@ impl Error for EvalError { "invalid boolean value read", EvalError::InvalidDiscriminant => "invalid enum discriminant value read", - EvalError::PointerOutOfBounds => + EvalError::PointerOutOfBounds { .. } => "pointer offset outside bounds of allocation", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", @@ -48,6 +52,9 @@ impl Error for EvalError { impl fmt::Display for EvalError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) + match *self { + EvalError::PointerOutOfBounds { offset, size, len } => write!(f, "pointer offset ({} + {}) outside bounds ({}) of allocation", offset, size, len), + _ => write!(f, "{}", self.description()), + } } } diff --git a/src/interpreter.rs b/src/interpreter.rs index 8734427f07f57..bd8a5012b2e71 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -128,7 +128,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - memory: Memory::new(), + memory: Memory::new(tcx.sess + .target + .uint_type + .bit_width() + .expect("Session::target::uint_type was usize")/8), substs_stack: Vec::new(), name_stack: Vec::new(), } @@ -1196,23 +1200,25 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { use syntax::ast::{IntTy, UintTy}; - let val = match ty.sty { - ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), - ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), - ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), - ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), - ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), - ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), - ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), - ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), - ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - - // TODO(solson): Pick the PrimVal dynamically. - ty::TyInt(IntTy::Is) => PrimVal::I64(self.memory.read_isize(ptr)?), - ty::TyUint(UintTy::Us) => PrimVal::U64(self.memory.read_usize(ptr)?), - - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + let val = match (self.memory.pointer_size, &ty.sty) { + (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), + (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), + (2, &ty::TyInt(IntTy::Is)) | + (_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), + (4, &ty::TyInt(IntTy::Is)) | + (_, &ty::TyInt(IntTy::I32)) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), + (8, &ty::TyInt(IntTy::Is)) | + (_, &ty::TyInt(IntTy::I64)) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), + (_, &ty::TyUint(UintTy::U8)) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), + (2, &ty::TyUint(UintTy::Us)) | + (_, &ty::TyUint(UintTy::U16)) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), + (4, &ty::TyUint(UintTy::Us)) | + (_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), + (8, &ty::TyUint(UintTy::Us)) | + (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + + (_, &ty::TyRef(_, ty::TypeAndMut { ty, .. })) | + (_, &ty::TyRawPtr(ty::TypeAndMut { ty, .. })) => { if self.type_is_sized(ty) { match self.memory.read_ptr(ptr) { Ok(p) => PrimVal::AbstractPtr(p), diff --git a/src/memory.rs b/src/memory.rs index d69e07ae2a2b8..7cc6a9a8e9d1f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -49,14 +49,11 @@ pub struct Memory { } impl Memory { - pub fn new() -> Self { + pub fn new(pointer_size: usize) -> Self { Memory { alloc_map: HashMap::new(), next_id: AllocId(0), - - // FIXME(solson): This should work for both 4 and 8, but it currently breaks some things - // when set to 4. - pointer_size: 8, + pointer_size: pointer_size, } } @@ -183,7 +180,11 @@ impl Memory { fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { - return Err(EvalError::PointerOutOfBounds); + return Err(EvalError::PointerOutOfBounds { + offset: ptr.offset, + size: size, + len: alloc.bytes.len(), + }); } Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } @@ -191,7 +192,11 @@ impl Memory { fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { - return Err(EvalError::PointerOutOfBounds); + return Err(EvalError::PointerOutOfBounds { + offset: ptr.offset, + size: size, + len: alloc.bytes.len(), + }); } Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } diff --git a/tests/compile-fail/errors.rs b/tests/compile-fail/errors.rs index 6ef9800a1ad09..1f7e3ce88c6d5 100644 --- a/tests/compile-fail/errors.rs +++ b/tests/compile-fail/errors.rs @@ -6,7 +6,9 @@ fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { let mut p = &42; unsafe { let ptr: *mut _ = &mut p; - *(ptr as *mut u32) = 123; + *(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause + // "attempted to interpret some raw bytes as a pointer address" instead of + // "attempted to read undefined bytes" } *p //~ ERROR: attempted to read undefined bytes } @@ -34,7 +36,7 @@ fn undefined_byte_read() -> u8 { #[miri_run] fn out_of_bounds_read() -> u8 { let v: Vec = vec![1, 2]; - unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset outside bounds of allocation + unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset (5 + 1) outside bounds (2) of allocation } #[miri_run] diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 51634fd15bffb..184e282413242 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,17 +3,24 @@ extern crate compiletest_rs as compiletest; use std::path::PathBuf; fn run_mode(mode: &'static str) { - let mut config = compiletest::default_config(); - config.rustc_path = "target/debug/miri".into(); - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - config.target_rustcflags = Some(format!("--sysroot {}", path)); - config.host_rustcflags = Some(format!("--sysroot {}", path)); - let cfg_mode = mode.parse().ok().expect("Invalid mode"); + // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that + let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; - config.mode = cfg_mode; - config.src_base = PathBuf::from(format!("tests/{}", mode)); + for &target in targets { + let mut config = compiletest::default_config(); + config.rustc_path = "target/debug/miri".into(); + let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); + config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target); + let path = format!("--sysroot {}", path); + config.target_rustcflags = Some(path.clone()); + config.host_rustcflags = Some(path); + let cfg_mode = mode.parse().ok().expect("Invalid mode"); - compiletest::run_tests(&config); + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.target = target.to_owned(); + compiletest::run_tests(&config); + } } #[test] diff --git a/tests/run-pass/strings.rs b/tests/run-pass/strings.rs index 3d71fa4e09f87..8092335116988 100644 --- a/tests/run-pass/strings.rs +++ b/tests/run-pass/strings.rs @@ -21,4 +21,9 @@ fn hello_bytes_fat() -> &'static [u8] { b"Hello, world!" } +#[miri_run] +fn fat_pointer_on_32_bit() { + Some(5).expect("foo"); +} + fn main() {} From 29516c3129dbea594ea9b68f9bdf665dda909f44 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 11:22:37 +0200 Subject: [PATCH 0290/1096] improve out of bounds error message --- src/error.rs | 10 +++++++--- src/memory.rs | 8 ++++---- tests/compile-fail/errors.rs | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/error.rs b/src/error.rs index c82b7fa1ca61a..7d252658ba2d6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ use std::error::Error; use std::fmt; use rustc::mir::repr as mir; +use memory::Pointer; #[derive(Clone, Debug)] pub enum EvalError { @@ -8,9 +9,9 @@ pub enum EvalError { InvalidBool, InvalidDiscriminant, PointerOutOfBounds { - offset: usize, + ptr: Pointer, size: usize, - len: usize, + allocation_size: usize, }, ReadPointerAsBytes, ReadBytesAsPointer, @@ -53,7 +54,10 @@ impl Error for EvalError { impl fmt::Display for EvalError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - EvalError::PointerOutOfBounds { offset, size, len } => write!(f, "pointer offset ({} + {}) outside bounds ({}) of allocation", offset, size, len), + EvalError::PointerOutOfBounds { ptr, size, allocation_size } => { + write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}", + ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) + }, _ => write!(f, "{}", self.description()), } } diff --git a/src/memory.rs b/src/memory.rs index 7cc6a9a8e9d1f..6cae30d39434e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -181,9 +181,9 @@ impl Memory { let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { - offset: ptr.offset, + ptr: ptr, size: size, - len: alloc.bytes.len(), + allocation_size: alloc.bytes.len(), }); } Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) @@ -193,9 +193,9 @@ impl Memory { let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { - offset: ptr.offset, + ptr: ptr, size: size, - len: alloc.bytes.len(), + allocation_size: alloc.bytes.len(), }); } Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) diff --git a/tests/compile-fail/errors.rs b/tests/compile-fail/errors.rs index 1f7e3ce88c6d5..0cadd76cccf34 100644 --- a/tests/compile-fail/errors.rs +++ b/tests/compile-fail/errors.rs @@ -36,7 +36,7 @@ fn undefined_byte_read() -> u8 { #[miri_run] fn out_of_bounds_read() -> u8 { let v: Vec = vec![1, 2]; - unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset (5 + 1) outside bounds (2) of allocation + unsafe { *v.get_unchecked(5) } //~ ERROR: memory access of 5..6 outside bounds of allocation 11 which has size 2 } #[miri_run] From f910019da169a000d8b7bb0d031b4de2a0e420a9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 11:24:23 +0200 Subject: [PATCH 0291/1096] add a note to Memory::new mentioning tcx.data_layout --- src/memory.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/memory.rs b/src/memory.rs index 6cae30d39434e..728d5310f8611 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -49,6 +49,7 @@ pub struct Memory { } impl Memory { + // FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.) pub fn new(pointer_size: usize) -> Self { Memory { alloc_map: HashMap::new(), From b3a175f730aff58ae41254254bbcd55d81ed9d47 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 May 2016 16:39:52 +0200 Subject: [PATCH 0292/1096] add dependencies and run cargo update --- Cargo.lock | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 ++ 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4827e7fd8a6a7..c5518b1238a27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,18 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -13,14 +24,107 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "env_logger" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lazy_static" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "log" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log_settings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.1.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + diff --git a/Cargo.toml b/Cargo.toml index 66bbc729e15fa..5a8211230fa86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,9 @@ test = false [dependencies] byteorder = "0.4.2" +env_logger = "0.3.3" +log = "0.3.6" +log_settings = "0.1.1" [dev-dependencies] compiletest_rs = "0.1.1" From 4f3f2020edbce8d9bcb618d11f8f7fc5eb8dff76 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 May 2016 18:09:52 +0200 Subject: [PATCH 0293/1096] add the `log` crate + `env_logger` to be able to choose the log granularity at runtime --- src/bin/miri.rs | 24 ++++++++++++++++++++++++ src/interpreter.rs | 40 +++++++++++++++------------------------- src/lib.rs | 2 ++ 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 49d49650ea5cc..13025cc668be8 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -5,6 +5,9 @@ extern crate getopts; extern crate miri; extern crate rustc; extern crate rustc_driver; +extern crate env_logger; +extern crate log_settings; +extern crate log; use miri::interpreter; use rustc::session::Session; @@ -31,6 +34,27 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { #[miri_run] fn main() { + init_logger(); let args: Vec = std::env::args().collect(); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); } + +#[miri_run] +fn init_logger() { + let format = |record: &log::LogRecord| { + // prepend spaces to indent the final string + let indentation = log_settings::settings().indentation; + let spaces = " "; + let indentation = &spaces[..std::cmp::min(indentation, spaces.len())]; + format!("{} -{} {}", record.level(), indentation, record.args()) + }; + + let mut builder = env_logger::LogBuilder::new(); + builder.format(format).filter(None, log::LogLevelFilter::Info); + + if std::env::var("RUST_LOG").is_ok() { + builder.parse(&std::env::var("RUST_LOG").unwrap()); + } + + builder.init().unwrap(); +} diff --git a/src/interpreter.rs b/src/interpreter.rs index bd8a5012b2e71..0f9cc899193d6 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -20,8 +20,6 @@ use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal}; -const TRACE_EXECUTION: bool = true; - struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -168,32 +166,24 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { r } - fn log(&self, extra_indent: usize, f: F) where F: FnOnce() { - let indent = self.stack.len() + extra_indent; - if !TRACE_EXECUTION { return; } - for _ in 0..indent { print!(" "); } - f(); - println!(""); - } - fn run(&mut self) -> EvalResult<()> { 'outer: while !self.stack.is_empty() { let mut current_block = self.frame().next_block; loop { - self.log(0, || print!("// {:?}", current_block)); + trace!("// {:?}", current_block); let current_mir = self.mir().clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { - self.log(0, || print!("{:?}", stmt)); + trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.eval_assignment(lvalue, rvalue); self.maybe_report(stmt.span, result)?; } let terminator = block_data.terminator(); - self.log(0, || print!("{:?}", terminator.kind)); + trace!("{:?}", terminator.kind); let result = self.eval_terminator(terminator); match self.maybe_report(terminator.span, result)? { @@ -245,6 +235,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); + ::log_settings::settings().indentation += 1; + self.stack.push(Frame { mir: mir.clone(), next_block: mir::START_BLOCK, @@ -256,6 +248,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn pop_stack_frame(&mut self) { + ::log_settings::settings().indentation -= 1; let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); // TODO(solson): Deallocate local variables. self.substs_stack.pop(); @@ -419,10 +412,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { if !self.type_needs_drop(ty) { - self.log(1, || print!("no need to drop {:?}", ty)); + debug!("no need to drop {:?}", ty); return Ok(()); } - self.log(1, || print!("need to drop {:?}", ty)); + trace!("-need to drop {:?}", ty); // TODO(solson): Call user-defined Drop::drop impls. @@ -431,7 +424,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match self.memory.read_ptr(ptr) { Ok(contents_ptr) => { self.drop(contents_ptr, contents_ty)?; - self.log(1, || print!("deallocating box")); + trace!("-deallocating box"); self.memory.deallocate(contents_ptr)?; } Err(EvalError::ReadBytesAsPointer) => { @@ -1421,32 +1414,29 @@ pub fn interpret_start_points<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &MirMap<'tcx>, ) { + let initial_indentation = ::log_settings::settings().indentation; for (&id, mir) in &mir_map.map { for attr in tcx.map.attrs(id) { use syntax::attr::AttrMetaMethods; if attr.check_name("miri_run") { let item = tcx.map.expect_item(id); - if TRACE_EXECUTION { - println!("Interpreting: {}", item.name); - } + ::log_settings::settings().indentation = initial_indentation; + + debug!("Interpreting: {}", item.name); let mut gecx = GlobalEvalContext::new(tcx, mir_map); let mut fecx = FnEvalContext::new(&mut gecx); match fecx.call_nested(mir) { - Ok(Some(return_ptr)) => if TRACE_EXECUTION { + Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) { fecx.memory.dump(return_ptr.alloc_id); }, - Ok(None) => println!("(diverging function returned)"), + Ok(None) => warn!("diverging function returned"), Err(_e) => { // TODO(solson): Detect whether the error was already reported or not. // tcx.sess.err(&e.to_string()); } } - - if TRACE_EXECUTION { - println!(""); - } } } } diff --git a/src/lib.rs b/src/lib.rs index 623ed14be76ea..80d89c164ac5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ #[macro_use] extern crate rustc; extern crate rustc_mir; extern crate syntax; +#[macro_use] extern crate log; +extern crate log_settings; // From crates.io. extern crate byteorder; From 49dfd82fd363a6379af9a6c453e2217e58a0fc6d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 May 2016 16:41:26 +0200 Subject: [PATCH 0294/1096] fallout because compiletest tried to use rustc's log crate --- tests/run-fail/inception.rs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/run-fail/inception.rs b/tests/run-fail/inception.rs index 25eb72aa04c23..f0fb4113f1f76 100644 --- a/tests/run-fail/inception.rs +++ b/tests/run-fail/inception.rs @@ -9,14 +9,32 @@ fn run_miri(file: &str, sysroot: &str) -> Output { let libpath = libpath.to_str().unwrap(); let libpath2 = path.join("target").join("debug").join("deps"); let libpath2 = libpath2.to_str().unwrap(); + let mut args = vec![ + "run".to_string(), "--".to_string(), + "--sysroot".to_string(), sysroot.to_string(), + "-L".to_string(), libpath.to_string(), + "-L".to_string(), libpath2.to_string(), + file.to_string() + ]; + for file in std::fs::read_dir("target/debug/deps").unwrap() { + let file = file.unwrap(); + if file.file_type().unwrap().is_file() { + let path = file.path(); + if let Some(ext) = path.extension() { + if ext == "rlib" { + let name = path.file_stem().unwrap().to_str().unwrap(); + if let Some(dash) = name.rfind('-') { + if name.starts_with("lib") { + args.push("--extern".to_string()); + args.push(format!("{}={}", &name[3..dash], path.to_str().unwrap())); + } + } + } + } + } + } Command::new("cargo") - .args(&[ - "run", "--", - "--sysroot", sysroot, - "-L", libpath, - "-L", libpath2, - file - ]) + .args(&args) .output() .unwrap_or_else(|e| panic!("failed to execute process: {}", e)) } From f1e4ef6c6f90240903b6e40ad92a51593b0ba39f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 May 2016 16:51:30 +0200 Subject: [PATCH 0295/1096] re-introduce the module name to the logs and show vertical bars --- src/bin/miri.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 13025cc668be8..dfb325642eb58 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -44,9 +44,9 @@ fn init_logger() { let format = |record: &log::LogRecord| { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - let spaces = " "; + let spaces = " | | | | | | | | "; let indentation = &spaces[..std::cmp::min(indentation, spaces.len())]; - format!("{} -{} {}", record.level(), indentation, record.args()) + format!("{}:{}|{} {}", record.level(), record.location().module_path(), indentation, record.args()) }; let mut builder = env_logger::LogBuilder::new(); From f9a54161354821daef7ba2798460a1b93ab0f766 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 17:32:57 +0200 Subject: [PATCH 0296/1096] use MIRI_LOG instead of RUST_LOG, because rustc's output is very verbose --- src/bin/miri.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index dfb325642eb58..8039cd18f82c6 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -52,8 +52,8 @@ fn init_logger() { let mut builder = env_logger::LogBuilder::new(); builder.format(format).filter(None, log::LogLevelFilter::Info); - if std::env::var("RUST_LOG").is_ok() { - builder.parse(&std::env::var("RUST_LOG").unwrap()); + if std::env::var("MIRI_LOG").is_ok() { + builder.parse(&std::env::var("MIRI_LOG").unwrap()); } builder.init().unwrap(); From b7df4fdc75f208ced6d8ebec6d8f44f0bd5a6102 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 17:58:50 +0200 Subject: [PATCH 0297/1096] use a number for the maximum indentation instead of relying on str::len() --- src/bin/miri.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8039cd18f82c6..85e4f6348690d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -44,8 +44,8 @@ fn init_logger() { let format = |record: &log::LogRecord| { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - let spaces = " | | | | | | | | "; - let indentation = &spaces[..std::cmp::min(indentation, spaces.len())]; + let spaces = " | | | | | | | | |"; + let indentation = &spaces[..std::cmp::min(indentation, 44)]; format!("{}:{}|{} {}", record.level(), record.location().module_path(), indentation, record.args()) }; From c62cce3116803a448991037d1adc9043a77c31d0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 18:18:01 +0200 Subject: [PATCH 0298/1096] wraparound when reaching indentation lvl 40 --- src/bin/miri.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 85e4f6348690d..33fb8393a7b42 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -44,9 +44,11 @@ fn init_logger() { let format = |record: &log::LogRecord| { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - let spaces = " | | | | | | | | |"; - let indentation = &spaces[..std::cmp::min(indentation, 44)]; - format!("{}:{}|{} {}", record.level(), record.location().module_path(), indentation, record.args()) + let spaces = " "; + let depth = indentation / spaces.len(); + let indentation = indentation % spaces.len(); + let indentation = &spaces[..indentation]; + format!("{}:{}{:2}{} {}", record.level(), record.location().module_path(), depth, indentation, record.args()) }; let mut builder = env_logger::LogBuilder::new(); From af41c54301ef2abee639f0a8b355ba5a8c0ba258 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 18:33:29 +0200 Subject: [PATCH 0299/1096] use format! compile time magics for indentation --- src/bin/miri.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 33fb8393a7b42..df1763ae530f9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -41,14 +41,19 @@ fn main() { #[miri_run] fn init_logger() { + const NSPACES: usize = 40; let format = |record: &log::LogRecord| { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - let spaces = " "; - let depth = indentation / spaces.len(); - let indentation = indentation % spaces.len(); - let indentation = &spaces[..indentation]; - format!("{}:{}{:2}{} {}", record.level(), record.location().module_path(), depth, indentation, record.args()) + let depth = indentation / NSPACES; + let indentation = indentation % NSPACES; + format!("{lvl}:{module}{depth:2}{indent: Date: Wed, 1 Jun 2016 18:42:57 +0200 Subject: [PATCH 0300/1096] remove intermediate vars --- src/bin/miri.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index df1763ae530f9..34a8c1b2be879 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -45,13 +45,11 @@ fn init_logger() { let format = |record: &log::LogRecord| { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - let depth = indentation / NSPACES; - let indentation = indentation % NSPACES; - format!("{lvl}:{module}{depth:2}{indent: Date: Tue, 31 May 2016 18:26:03 +0200 Subject: [PATCH 0301/1096] can't evaluate failed assertions yet --- src/interpreter.rs | 2 +- tests/compile-fail/unimplemented.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/unimplemented.rs diff --git a/src/interpreter.rs b/src/interpreter.rs index 0f9cc899193d6..0b004c78e4f9d 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -996,7 +996,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { .. } => unimplemented!(), + Item { .. } => Err(EvalError::Unimplemented(format!("function pointers are unimplemented"))), Promoted { index } => { // TODO(solson): Mark constants and statics as read-only and cache their // values. diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs new file mode 100644 index 0000000000000..010883b7d617f --- /dev/null +++ b/tests/compile-fail/unimplemented.rs @@ -0,0 +1,16 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +//error-pattern:function pointers are unimplemented + +static mut X: usize = 5; + +#[miri_run] +fn static_mut() { + unsafe { + X = 6; + assert_eq!(X, 6); + } +} + +fn main() {} From 8398781132ae6d9062178db4f206e0262fc1013d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 14:33:37 +0200 Subject: [PATCH 0302/1096] remove one layer of indirection when interpreting const/static/main functions --- src/interpreter.rs | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 0b004c78e4f9d..bd51e2d2dbf14 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -135,6 +135,23 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { name_stack: Vec::new(), } } + + fn call(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult> { + let mut nested_fecx = FnEvalContext::new(self); + + let return_ptr = match mir.return_ty { + ty::FnConverging(ty) => { + let size = nested_fecx.type_size(ty); + Some(nested_fecx.memory.allocate(size)) + } + ty::FnDiverging => None, + }; + + let substs = nested_fecx.substs(); + nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); + nested_fecx.run()?; + Ok(return_ptr) + } } impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { @@ -201,23 +218,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn call_nested(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult> { - let mut nested_fecx = FnEvalContext::new(self.gecx); - - let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => { - let size = nested_fecx.type_size(ty); - Some(nested_fecx.memory.allocate(size)) - } - ty::FnDiverging => None, - }; - - let substs = nested_fecx.substs(); - nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); - nested_fecx.run()?; - Ok(return_ptr) - } - fn push_stack_frame(&mut self, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { @@ -1002,7 +1002,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // values. let current_mir = self.mir(); let mir = ¤t_mir.promoted[index]; - self.call_nested(mir).map(Option::unwrap) + self.gecx.call(mir).map(Option::unwrap) } } } @@ -1021,7 +1021,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Static(def_id) => { // TODO(solson): Mark constants and statics as read-only and cache their values. let mir = self.load_mir(def_id); - self.call_nested(&mir)?.unwrap() + self.gecx.call(&mir)?.unwrap() } Projection(ref proj) => { @@ -1426,10 +1426,9 @@ pub fn interpret_start_points<'a, 'tcx>( debug!("Interpreting: {}", item.name); let mut gecx = GlobalEvalContext::new(tcx, mir_map); - let mut fecx = FnEvalContext::new(&mut gecx); - match fecx.call_nested(mir) { + match gecx.call(mir) { Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) { - fecx.memory.dump(return_ptr.alloc_id); + gecx.memory.dump(return_ptr.alloc_id); }, Ok(None) => warn!("diverging function returned"), Err(_e) => { From 5a8b0ab5798704fce6ae7fe8fd7073bd48fd244b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 13:55:16 +0200 Subject: [PATCH 0303/1096] don't clone the MIR Rc in every iteration --- src/interpreter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index bd51e2d2dbf14..c8e6d0bbe1f2b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -186,10 +186,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn run(&mut self) -> EvalResult<()> { 'outer: while !self.stack.is_empty() { let mut current_block = self.frame().next_block; + let current_mir = self.mir(); loop { trace!("// {:?}", current_block); - let current_mir = self.mir().clone(); // Cloning a reference. let block_data = current_mir.basic_block_data(current_block); for stmt in &block_data.statements { From 2405c81c65586df5c65ae8670151b5237edf4398 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 17:05:20 +0200 Subject: [PATCH 0304/1096] stepwise interpretation --- src/interpreter/iterator.rs | 92 ++++++++++++++++++++++ src/{interpreter.rs => interpreter/mod.rs} | 38 +++------ src/lib.rs | 1 + 3 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 src/interpreter/iterator.rs rename src/{interpreter.rs => interpreter/mod.rs} (98%) diff --git a/src/interpreter/iterator.rs b/src/interpreter/iterator.rs new file mode 100644 index 0000000000000..7793c0df28215 --- /dev/null +++ b/src/interpreter/iterator.rs @@ -0,0 +1,92 @@ +use super::{ + FnEvalContext, + CachedMir, + TerminatorTarget, +}; +use error::EvalResult; +use rustc::mir::repr as mir; + +pub enum Event<'a, 'tcx: 'a> { + Assignment(&'a mir::Statement<'tcx>), + Terminator(&'a mir::Terminator<'tcx>), + Done, +} + +pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ + fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, + block: mir::BasicBlock, + stmt: usize, + mir: CachedMir<'mir, 'tcx>, + process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, +} + +impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { + pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { + Stepper { + block: fncx.frame().next_block, + mir: fncx.mir(), + fncx: fncx, + stmt: 0, + process: Self::dummy, + } + } + fn dummy(&mut self) -> EvalResult<()> { Ok(()) } + fn statement(&mut self) -> EvalResult<()> { + let block_data = self.mir.basic_block_data(self.block); + let stmt = &block_data.statements[self.stmt]; + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + let result = self.fncx.eval_assignment(lvalue, rvalue); + self.fncx.maybe_report(stmt.span, result)?; + self.stmt += 1; + Ok(()) + } + fn terminator(&mut self) -> EvalResult<()> { + self.stmt = 0; + let term = { + let block_data = self.mir.basic_block_data(self.block); + let terminator = block_data.terminator(); + let result = self.fncx.eval_terminator(terminator); + self.fncx.maybe_report(terminator.span, result)? + }; + match term { + TerminatorTarget::Block(block) => { + self.block = block; + }, + TerminatorTarget::Return => { + self.fncx.pop_stack_frame(); + self.fncx.name_stack.pop(); + if !self.fncx.stack.is_empty() { + self.block = self.fncx.frame().next_block; + self.mir = self.fncx.mir(); + } + }, + TerminatorTarget::Call => { + self.block = self.fncx.frame().next_block; + self.mir = self.fncx.mir(); + }, + } + Ok(()) + } + pub fn step<'step>(&'step mut self) -> EvalResult> { + (self.process)(self)?; + + if self.fncx.stack.is_empty() { + // fuse the iterator + self.process = Self::dummy; + return Ok(Event::Done); + } + + let basic_block = self.mir.basic_block_data(self.block); + + if let Some(stmt) = basic_block.statements.get(self.stmt) { + self.process = Self::statement; + return Ok(Event::Assignment(&stmt)); + } + + self.process = Self::terminator; + Ok(Event::Terminator(basic_block.terminator())) + } + pub fn block(&self) -> mir::BasicBlock { + self.block + } +} diff --git a/src/interpreter.rs b/src/interpreter/mod.rs similarity index 98% rename from src/interpreter.rs rename to src/interpreter/mod.rs index c8e6d0bbe1f2b..8b93edd888c6c 100644 --- a/src/interpreter.rs +++ b/src/interpreter/mod.rs @@ -20,6 +20,8 @@ use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal}; +mod iterator; + struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -184,38 +186,22 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn run(&mut self) -> EvalResult<()> { - 'outer: while !self.stack.is_empty() { - let mut current_block = self.frame().next_block; - let current_mir = self.mir(); + let mut stepper = iterator::Stepper::new(self); + 'outer: loop { + use self::iterator::Event::*; + trace!("// {:?}", stepper.block()); loop { - trace!("// {:?}", current_block); - let block_data = current_mir.basic_block_data(current_block); - - for stmt in &block_data.statements { - trace!("{:?}", stmt); - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let result = self.eval_assignment(lvalue, rvalue); - self.maybe_report(stmt.span, result)?; - } - - let terminator = block_data.terminator(); - trace!("{:?}", terminator.kind); - - let result = self.eval_terminator(terminator); - match self.maybe_report(terminator.span, result)? { - TerminatorTarget::Block(block) => current_block = block, - TerminatorTarget::Return => { - self.pop_stack_frame(); - self.name_stack.pop(); + match stepper.step()? { + Assignment(statement) => trace!("{:?}", statement), + Terminator(terminator) => { + trace!("{:?}", terminator.kind); continue 'outer; - } - TerminatorTarget::Call => continue 'outer, + }, + Done => return Ok(()), } } } - - Ok(()) } fn push_stack_frame(&mut self, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, diff --git a/src/lib.rs b/src/lib.rs index 80d89c164ac5f..4e06a3ce38d56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ filling_drop, question_mark, rustc_private, + pub_restricted, )] // From rustc. From 77e1ec2b4995358ce4b02683dd491653a23a97b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 19:01:40 +0200 Subject: [PATCH 0305/1096] style: spaces between functions --- src/interpreter/iterator.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interpreter/iterator.rs b/src/interpreter/iterator.rs index 7793c0df28215..4c8d0c7292b4f 100644 --- a/src/interpreter/iterator.rs +++ b/src/interpreter/iterator.rs @@ -30,7 +30,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx process: Self::dummy, } } + fn dummy(&mut self) -> EvalResult<()> { Ok(()) } + fn statement(&mut self) -> EvalResult<()> { let block_data = self.mir.basic_block_data(self.block); let stmt = &block_data.statements[self.stmt]; @@ -40,6 +42,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.stmt += 1; Ok(()) } + fn terminator(&mut self) -> EvalResult<()> { self.stmt = 0; let term = { @@ -67,6 +70,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } Ok(()) } + pub fn step<'step>(&'step mut self) -> EvalResult> { (self.process)(self)?; @@ -86,6 +90,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.process = Self::terminator; Ok(Event::Terminator(basic_block.terminator())) } + pub fn block(&self) -> mir::BasicBlock { self.block } From 0c269a500cae8fce43dcd1a01dcf1b9654e6757f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 19:17:18 +0200 Subject: [PATCH 0306/1096] rename `iterator` module to `stepper` --- src/interpreter/mod.rs | 6 +++--- src/interpreter/{iterator.rs => stepper.rs} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename src/interpreter/{iterator.rs => stepper.rs} (100%) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8b93edd888c6c..a7dedc9ddc65b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -20,7 +20,7 @@ use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal}; -mod iterator; +mod stepper; struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -186,9 +186,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn run(&mut self) -> EvalResult<()> { - let mut stepper = iterator::Stepper::new(self); + let mut stepper = stepper::Stepper::new(self); 'outer: loop { - use self::iterator::Event::*; + use self::stepper::Event::*; trace!("// {:?}", stepper.block()); loop { diff --git a/src/interpreter/iterator.rs b/src/interpreter/stepper.rs similarity index 100% rename from src/interpreter/iterator.rs rename to src/interpreter/stepper.rs From 52111783771d9e967a63995b3e2d7c6538fc6b52 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Jun 2016 19:20:23 +0200 Subject: [PATCH 0307/1096] note that not all literal items are function pointers --- src/interpreter/mod.rs | 2 +- tests/compile-fail/unimplemented.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7dedc9ddc65b..5592783d86329 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -982,7 +982,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { .. } => Err(EvalError::Unimplemented(format!("function pointers are unimplemented"))), + Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))), Promoted { index } => { // TODO(solson): Mark constants and statics as read-only and cache their // values. diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 010883b7d617f..3b99fda9477dc 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:function pointers are unimplemented +//error-pattern:literal items (e.g. mentions of function items) are unimplemented static mut X: usize = 5; From 6ac64f19af6280d58d68118f09fd65950fb4aa6b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Jun 2016 17:05:17 +0200 Subject: [PATCH 0308/1096] also step through promoteds, constants and statics --- src/interpreter/mod.rs | 42 +++++---- src/interpreter/stepper.rs | 136 +++++++++++++++++++++++++--- tests/compile-fail/unimplemented.rs | 10 +- tests/run-pass/bug.rs | 14 +++ 4 files changed, 163 insertions(+), 39 deletions(-) create mode 100644 tests/run-pass/bug.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5592783d86329..ec830fd008f88 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -20,6 +20,8 @@ use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal}; +use std::collections::HashMap; + mod stepper; struct GlobalEvalContext<'a, 'tcx: 'a> { @@ -45,6 +47,9 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// * Function DefIds and Substs to print proper substituted function names. /// * Spans pointing to specific function calls in the source. name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>, + + /// Precomputed statics and constants + statics: DefIdMap, } struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> { @@ -88,6 +93,9 @@ struct Frame<'a, 'tcx: 'a> { /// The offset of the first temporary in `self.locals`. temp_offset: usize, + + /// List of precomputed promoted constants + promoted: HashMap, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -122,6 +130,13 @@ enum TerminatorTarget { Return, } +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +enum ConstantId { + Promoted { index: usize }, + Static { def_id: DefId }, +} + + impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { GlobalEvalContext { @@ -135,10 +150,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { .expect("Session::target::uint_type was usize")/8), substs_stack: Vec::new(), name_stack: Vec::new(), + statics: DefIdMap(), } } - fn call(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult> { + fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { let mut nested_fecx = FnEvalContext::new(self); let return_ptr = match mir.return_ty { @@ -150,6 +166,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { }; let substs = nested_fecx.substs(); + nested_fecx.name_stack.push((def_id, substs, mir.span)); nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); nested_fecx.run()?; Ok(return_ptr) @@ -193,9 +210,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { loop { match stepper.step()? { - Assignment(statement) => trace!("{:?}", statement), - Terminator(terminator) => { - trace!("{:?}", terminator.kind); + Assignment => trace!("{:?}", stepper.stmt()), + Terminator => { + trace!("{:?}", stepper.term().kind); continue 'outer; }, Done => return Ok(()), @@ -230,6 +247,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { locals: locals, var_offset: num_args, temp_offset: num_args + num_vars, + promoted: HashMap::new(), }); } @@ -983,13 +1001,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))), - Promoted { index } => { - // TODO(solson): Mark constants and statics as read-only and cache their - // values. - let current_mir = self.mir(); - let mir = ¤t_mir.promoted[index]; - self.gecx.call(mir).map(Option::unwrap) - } + Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")), } } } @@ -1004,11 +1016,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], - Static(def_id) => { - // TODO(solson): Mark constants and statics as read-only and cache their values. - let mir = self.load_mir(def_id); - self.gecx.call(&mir)?.unwrap() - } + Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached"), Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; @@ -1412,7 +1420,7 @@ pub fn interpret_start_points<'a, 'tcx>( debug!("Interpreting: {}", item.name); let mut gecx = GlobalEvalContext::new(tcx, mir_map); - match gecx.call(mir) { + match gecx.call(mir, tcx.map.local_def_id(id)) { Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) { gecx.memory.dump(return_ptr.alloc_id); }, diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 4c8d0c7292b4f..0543b40c8f5c3 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -2,49 +2,61 @@ use super::{ FnEvalContext, CachedMir, TerminatorTarget, + ConstantId, }; use error::EvalResult; use rustc::mir::repr as mir; +use rustc::ty::{self, subst}; +use rustc::mir::visit::Visitor; +use syntax::codemap::Span; +use memory::Pointer; +use std::rc::Rc; -pub enum Event<'a, 'tcx: 'a> { - Assignment(&'a mir::Statement<'tcx>), - Terminator(&'a mir::Terminator<'tcx>), +pub enum Event { + Assignment, + Terminator, Done, } pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, block: mir::BasicBlock, - stmt: usize, + // a stack of statement positions + stmt: Vec, mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, + // a stack of constants + constants: Vec>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { - Stepper { + let mut stepper = Stepper { block: fncx.frame().next_block, mir: fncx.mir(), fncx: fncx, - stmt: 0, + stmt: vec![0], process: Self::dummy, - } + constants: Vec::new(), + }; + stepper.extract_constants(); + stepper } fn dummy(&mut self) -> EvalResult<()> { Ok(()) } fn statement(&mut self) -> EvalResult<()> { let block_data = self.mir.basic_block_data(self.block); - let stmt = &block_data.statements[self.stmt]; + let stmt = &block_data.statements[*self.stmt.last().unwrap()]; let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); self.fncx.maybe_report(stmt.span, result)?; - self.stmt += 1; + *self.stmt.last_mut().unwrap() += 1; Ok(()) } fn terminator(&mut self) -> EvalResult<()> { - self.stmt = 0; + *self.stmt.last_mut().unwrap() = 0; let term = { let block_data = self.mir.basic_block_data(self.block); let terminator = block_data.terminator(); @@ -58,6 +70,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx TerminatorTarget::Return => { self.fncx.pop_stack_frame(); self.fncx.name_stack.pop(); + self.stmt.pop(); + assert!(self.constants.last().unwrap().is_empty()); + self.constants.pop(); if !self.fncx.stack.is_empty() { self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); @@ -66,12 +81,24 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx TerminatorTarget::Call => { self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); + self.stmt.push(0); + self.extract_constants(); }, } Ok(()) } - pub fn step<'step>(&'step mut self) -> EvalResult> { + fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer { + match ty { + ty::FnConverging(ty) => { + let size = self.fncx.type_size(ty); + self.fncx.memory.allocate(size) + } + ty::FnDiverging => panic!("there's no such thing as an unreachable static"), + } + } + + pub fn step(&mut self) -> EvalResult { (self.process)(self)?; if self.fncx.stack.is_empty() { @@ -80,18 +107,97 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx return Ok(Event::Done); } + match self.constants.last_mut().unwrap().pop() { + Some((ConstantId::Promoted { index }, span)) => { + trace!("adding promoted constant {}", index); + let mir = self.mir.promoted[index].clone(); + let return_ptr = self.alloc(mir.return_ty); + self.fncx.frame_mut().promoted.insert(index, return_ptr); + let substs = self.fncx.substs(); + // FIXME: somehow encode that this is a promoted constant's frame + println!("{}, {}, {}", self.fncx.stack.len(), self.fncx.name_stack.len(), self.fncx.substs_stack.len()); + let def_id = self.fncx.name_stack.last().unwrap().0; + self.fncx.name_stack.push((def_id, substs, span)); + self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); + self.stmt.push(0); + self.constants.push(Vec::new()); + self.block = self.fncx.frame().next_block; + self.mir = self.fncx.mir(); + }, + Some((ConstantId::Static { def_id }, span)) => { + trace!("adding static {:?}", def_id); + let mir = self.fncx.load_mir(def_id); + let return_ptr = self.alloc(mir.return_ty); + self.fncx.gecx.statics.insert(def_id, return_ptr); + let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); + self.fncx.name_stack.push((def_id, substs, span)); + self.fncx.push_stack_frame(mir, substs, Some(return_ptr)); + self.stmt.push(0); + self.constants.push(Vec::new()); + self.block = self.fncx.frame().next_block; + self.mir = self.fncx.mir(); + }, + None => {}, + } + let basic_block = self.mir.basic_block_data(self.block); - if let Some(stmt) = basic_block.statements.get(self.stmt) { + if basic_block.statements.len() > *self.stmt.last().unwrap() { self.process = Self::statement; - return Ok(Event::Assignment(&stmt)); + return Ok(Event::Assignment); } self.process = Self::terminator; - Ok(Event::Terminator(basic_block.terminator())) + Ok(Event::Terminator) } - + + /// returns the basic block index of the currently processed block pub fn block(&self) -> mir::BasicBlock { self.block } + + /// returns the statement that will be processed next + pub fn stmt(&self) -> &mir::Statement { + let block_data = self.mir.basic_block_data(self.block); + &block_data.statements[*self.stmt.last().unwrap()] + } + + /// returns the terminator of the current block + pub fn term(&self) -> &mir::Terminator { + let block_data = self.mir.basic_block_data(self.block); + block_data.terminator() + } + + fn extract_constants(&mut self) { + let mut extractor = ConstantExtractor { + constants: Vec::new(), + }; + extractor.visit_mir(&self.mir); + self.constants.push(extractor.constants); + } +} + +struct ConstantExtractor { + constants: Vec<(ConstantId, Span)>, +} + +impl<'tcx> Visitor<'tcx> for ConstantExtractor { + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { + self.super_constant(constant); + match constant.literal { + // already computed by rustc + mir::Literal::Value { .. } => {} + mir::Literal::Item { .. } => {}, // FIXME: unimplemented + mir::Literal::Promoted { index } => { + self.constants.push((ConstantId::Promoted { index: index }, constant.span)); + } + } + } + + fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) { + self.super_statement(block, stmt); + if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind { + self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span)); + } + } } diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 3b99fda9477dc..7ff4b1c191c13 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,16 +1,12 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:literal items (e.g. mentions of function items) are unimplemented +//error-pattern:static should have been cached -static mut X: usize = 5; #[miri_run] -fn static_mut() { - unsafe { - X = 6; - assert_eq!(X, 6); - } +fn failed_assertions() { + assert_eq!(5, 6); } fn main() {} diff --git a/tests/run-pass/bug.rs b/tests/run-pass/bug.rs new file mode 100644 index 0000000000000..3006da2c163de --- /dev/null +++ b/tests/run-pass/bug.rs @@ -0,0 +1,14 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +static mut X: usize = 5; + +#[miri_run] +fn static_mut() { + unsafe { + X = 6; + assert_eq!(X, 6); + } +} + +fn main() {} From 97bc1d4beeb510415856c1d878d390d6c050ba57 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Jun 2016 17:36:05 +0200 Subject: [PATCH 0309/1096] add a const fn test --- tests/run-pass/calls.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/run-pass/calls.rs b/tests/run-pass/calls.rs index be975320bba93..ba68fb44a6c39 100644 --- a/tests/run-pass/calls.rs +++ b/tests/run-pass/calls.rs @@ -1,4 +1,4 @@ -#![feature(custom_attribute)] +#![feature(custom_attribute, const_fn)] #![allow(dead_code, unused_attributes)] #[miri_run] @@ -33,6 +33,15 @@ fn cross_crate_fn_call() -> i64 { if 1i32.is_positive() { 1 } else { 0 } } +const fn foo(i: i64) -> i64 { *&i + 1 } + +#[miri_run] +fn const_fn_call() -> i64 { + let x = 5 + foo(5); + assert_eq!(x, 11); + x +} + #[miri_run] fn main() { assert_eq!(call(), 2); From 38ae3526e56137d10a6c0e21d861016d6de0c698 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Jun 2016 18:03:22 +0200 Subject: [PATCH 0310/1096] remove a debug message that snuck into the commit --- src/interpreter/stepper.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 0543b40c8f5c3..e341a9efd8a4b 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -115,7 +115,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.fncx.frame_mut().promoted.insert(index, return_ptr); let substs = self.fncx.substs(); // FIXME: somehow encode that this is a promoted constant's frame - println!("{}, {}, {}", self.fncx.stack.len(), self.fncx.name_stack.len(), self.fncx.substs_stack.len()); let def_id = self.fncx.name_stack.last().unwrap().0; self.fncx.name_stack.push((def_id, substs, span)); self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); From 05f829cc9f2627c14f93bf70540311390d36f043 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Jun 2016 18:21:32 +0200 Subject: [PATCH 0311/1096] merge the three stacks in the interpreter --- src/interpreter/mod.rs | 58 ++++++++++++++++++-------------------- src/interpreter/stepper.rs | 9 ++---- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ec830fd008f88..7ae20f78dc480 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -37,17 +37,6 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// The virtual memory system. memory: Memory, - /// Another stack containing the type substitutions for the current function invocation. It - /// exists separately from `stack` because it must contain the `Substs` for a function while - /// *creating* the `Frame` for that same function. - substs_stack: Vec<&'tcx Substs<'tcx>>, - - // TODO(solson): Merge with `substs_stack`. Also try restructuring `Frame` to accomodate. - /// A stack of the things necessary to print good strack traces: - /// * Function DefIds and Substs to print proper substituted function names. - /// * Spans pointing to specific function calls in the source. - name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>, - /// Precomputed statics and constants statics: DefIdMap, } @@ -74,6 +63,15 @@ impl<'a, 'b, 'mir, 'tcx> DerefMut for FnEvalContext<'a, 'b, 'mir, 'tcx> { /// A stack frame. struct Frame<'a, 'tcx: 'a> { + /// The def_id of the current function + def_id: DefId, + + /// The span of the call site + span: codemap::Span, + + /// type substitutions for the current function invocation + substs: &'tcx Substs<'tcx>, + /// The MIR for the function called on this frame. mir: CachedMir<'a, 'tcx>, @@ -148,15 +146,16 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { .uint_type .bit_width() .expect("Session::target::uint_type was usize")/8), - substs_stack: Vec::new(), - name_stack: Vec::new(), statics: DefIdMap(), } } fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { + let substs = self.tcx.mk_substs(subst::Substs::empty()); + let mut nested_fecx = FnEvalContext::new(self); + nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); let return_ptr = match mir.return_ty { ty::FnConverging(ty) => { let size = nested_fecx.type_size(ty); @@ -164,10 +163,8 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } ty::FnDiverging => None, }; + nested_fecx.frame_mut().return_ptr = return_ptr; - let substs = nested_fecx.substs(); - nested_fecx.name_stack.push((def_id, substs, mir.span)); - nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr); nested_fecx.run()?; Ok(return_ptr) } @@ -184,7 +181,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { if let Err(ref e) = r { let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); - for &(def_id, substs, span) in self.name_stack.iter().rev() { + for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() { // FIXME(solson): Find a way to do this without this Display impl hack. use rustc::util::ppaux; use std::fmt; @@ -221,20 +218,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - fn push_stack_frame(&mut self, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, + fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { - self.substs_stack.push(substs); - let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); - let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty); - self.memory.allocate(size) - }).collect(); - let num_args = mir.arg_decls.len(); let num_vars = mir.var_decls.len(); @@ -244,18 +234,27 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { mir: mir.clone(), next_block: mir::START_BLOCK, return_ptr: return_ptr, - locals: locals, + locals: Vec::new(), var_offset: num_args, temp_offset: num_args + num_vars, promoted: HashMap::new(), + span: span, + def_id: def_id, + substs: substs, }); + + let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + let size = self.type_size(ty); + self.memory.allocate(size) + }).collect(); + + self.frame_mut().locals = locals; } fn pop_stack_frame(&mut self) { ::log_settings::settings().indentation -= 1; let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); // TODO(solson): Deallocate local variables. - self.substs_stack.pop(); } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) @@ -382,8 +381,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } let mir = self.load_mir(resolved_def_id); - self.name_stack.push((def_id, substs, terminator.span)); - self.push_stack_frame(mir, resolved_substs, return_ptr); + self.push_stack_frame(def_id, terminator.span, mir, resolved_substs, return_ptr); for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; @@ -1237,7 +1235,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn substs(&self) -> &'tcx Substs<'tcx> { - self.substs_stack.last().cloned().unwrap_or_else(|| self.tcx.mk_substs(Substs::empty())) + self.frame().substs } fn load_mir(&self, def_id: DefId) -> CachedMir<'mir, 'tcx> { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index e341a9efd8a4b..48773d6e0df75 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -69,7 +69,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx }, TerminatorTarget::Return => { self.fncx.pop_stack_frame(); - self.fncx.name_stack.pop(); self.stmt.pop(); assert!(self.constants.last().unwrap().is_empty()); self.constants.pop(); @@ -115,9 +114,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.fncx.frame_mut().promoted.insert(index, return_ptr); let substs = self.fncx.substs(); // FIXME: somehow encode that this is a promoted constant's frame - let def_id = self.fncx.name_stack.last().unwrap().0; - self.fncx.name_stack.push((def_id, substs, span)); - self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); + let def_id = self.fncx.frame().def_id; + self.fncx.push_stack_frame(def_id, span, CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; @@ -129,8 +127,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let return_ptr = self.alloc(mir.return_ty); self.fncx.gecx.statics.insert(def_id, return_ptr); let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); - self.fncx.name_stack.push((def_id, substs, span)); - self.fncx.push_stack_frame(mir, substs, Some(return_ptr)); + self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; From cc1ca73f5736a4aaa7a1ae164788051b8366fa3d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 15:48:56 +0200 Subject: [PATCH 0312/1096] jit interpretation of constants --- src/interpreter/mod.rs | 34 ++++-- src/interpreter/stepper.rs | 165 ++++++++++++++++++---------- tests/compile-fail/unimplemented.rs | 2 +- tests/run-pass/constants.rs | 11 ++ 4 files changed, 139 insertions(+), 73 deletions(-) create mode 100644 tests/run-pass/constants.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7ae20f78dc480..7e59b2674e87f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -129,9 +129,9 @@ enum TerminatorTarget { } #[derive(Clone, Debug, Eq, PartialEq, Hash)] -enum ConstantId { +enum ConstantId<'tcx> { Promoted { index: usize }, - Static { def_id: DefId }, + Static { def_id: DefId, substs: &'tcx Substs<'tcx> }, } @@ -156,13 +156,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { let mut nested_fecx = FnEvalContext::new(self); nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - let return_ptr = match mir.return_ty { - ty::FnConverging(ty) => { - let size = nested_fecx.type_size(ty); - Some(nested_fecx.memory.allocate(size)) - } - ty::FnDiverging => None, - }; + let return_ptr = nested_fecx.alloc_ret_ptr(mir.return_ty); nested_fecx.frame_mut().return_ptr = return_ptr; nested_fecx.run()?; @@ -178,6 +172,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } + fn alloc_ret_ptr(&mut self, ty: ty::FnOutput<'tcx>) -> Option { + match ty { + ty::FnConverging(ty) => { + let size = self.type_size(ty); + Some(self.memory.allocate(size)) + } + ty::FnDiverging => None, + } + } + fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { if let Err(ref e) = r { let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); @@ -207,6 +211,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { loop { match stepper.step()? { + Constant => trace!("next statement requires the computation of a constant"), Assignment => trace!("{:?}", stepper.stmt()), Terminator => { trace!("{:?}", stepper.term().kind); @@ -998,7 +1003,14 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))), + Item { def_id, substs } => { + let item_ty = self.tcx.lookup_item_type(def_id).subst(self.tcx, substs); + if item_ty.ty.is_fn() { + Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) + } else { + Ok(*self.statics.get(&def_id).expect("static should have been cached (rvalue)")) + } + }, Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")), } } @@ -1014,7 +1026,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], - Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached"), + Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached (lvalue)"), Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 48773d6e0df75..b84680f733783 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -6,13 +6,15 @@ use super::{ }; use error::EvalResult; use rustc::mir::repr as mir; -use rustc::ty::{self, subst}; -use rustc::mir::visit::Visitor; +use rustc::ty::subst::{self, Subst}; +use rustc::hir::def_id::DefId; +use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use memory::Pointer; use std::rc::Rc; pub enum Event { + Constant, Assignment, Terminator, Done, @@ -26,21 +28,19 @@ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, // a stack of constants - constants: Vec>, + constants: Vec, Span, Pointer, CachedMir<'mir, 'tcx>)>>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { - let mut stepper = Stepper { + Stepper { block: fncx.frame().next_block, mir: fncx.mir(), fncx: fncx, stmt: vec![0], process: Self::dummy, - constants: Vec::new(), - }; - stepper.extract_constants(); - stepper + constants: vec![Vec::new()], + } } fn dummy(&mut self) -> EvalResult<()> { Ok(()) } @@ -81,70 +81,86 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); self.stmt.push(0); - self.extract_constants(); + self.constants.push(Vec::new()); }, } Ok(()) } - fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer { - match ty { - ty::FnConverging(ty) => { - let size = self.fncx.type_size(ty); - self.fncx.memory.allocate(size) - } - ty::FnDiverging => panic!("there's no such thing as an unreachable static"), - } - } - - pub fn step(&mut self) -> EvalResult { - (self.process)(self)?; - - if self.fncx.stack.is_empty() { - // fuse the iterator - self.process = Self::dummy; - return Ok(Event::Done); - } - + fn constant(&mut self) -> EvalResult<()> { match self.constants.last_mut().unwrap().pop() { - Some((ConstantId::Promoted { index }, span)) => { - trace!("adding promoted constant {}", index); - let mir = self.mir.promoted[index].clone(); - let return_ptr = self.alloc(mir.return_ty); - self.fncx.frame_mut().promoted.insert(index, return_ptr); + Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => { + trace!("adding promoted constant {}, {:?}", index, span); let substs = self.fncx.substs(); // FIXME: somehow encode that this is a promoted constant's frame let def_id = self.fncx.frame().def_id; - self.fncx.push_stack_frame(def_id, span, CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr)); + self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, - Some((ConstantId::Static { def_id }, span)) => { - trace!("adding static {:?}", def_id); - let mir = self.fncx.load_mir(def_id); - let return_ptr = self.alloc(mir.return_ty); + Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => { + trace!("adding static {:?}, {:?}", def_id, span); self.fncx.gecx.statics.insert(def_id, return_ptr); - let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, - None => {}, + None => unreachable!(), + } + Ok(()) + } + + pub fn step(&mut self) -> EvalResult { + (self.process)(self)?; + + if self.fncx.stack.is_empty() { + // fuse the iterator + self.process = Self::dummy; + return Ok(Event::Done); + } + + if !self.constants.last().unwrap().is_empty() { + self.process = Self::constant; + return Ok(Event::Constant); } let basic_block = self.mir.basic_block_data(self.block); - if basic_block.statements.len() > *self.stmt.last().unwrap() { - self.process = Self::statement; - return Ok(Event::Assignment); + if let Some(ref stmt) = basic_block.statements.get(*self.stmt.last().unwrap()) { + assert!(self.constants.last().unwrap().is_empty()); + ConstantExtractor { + constants: &mut self.constants.last_mut().unwrap(), + span: stmt.span, + fncx: self.fncx, + mir: &self.mir, + }.visit_statement(self.block, stmt); + if self.constants.last().unwrap().is_empty() { + self.process = Self::statement; + return Ok(Event::Assignment); + } else { + self.process = Self::constant; + return Ok(Event::Constant); + } } - self.process = Self::terminator; - Ok(Event::Terminator) + let terminator = basic_block.terminator(); + ConstantExtractor { + constants: &mut self.constants.last_mut().unwrap(), + span: terminator.span, + fncx: self.fncx, + mir: &self.mir, + }.visit_terminator(self.block, terminator); + if self.constants.last().unwrap().is_empty() { + self.process = Self::terminator; + Ok(Event::Terminator) + } else { + self.process = Self::constant; + return Ok(Event::Constant); + } } /// returns the basic block index of the currently processed block @@ -163,37 +179,64 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let block_data = self.mir.basic_block_data(self.block); block_data.terminator() } +} - fn extract_constants(&mut self) { - let mut extractor = ConstantExtractor { - constants: Vec::new(), - }; - extractor.visit_mir(&self.mir); - self.constants.push(extractor.constants); - } +struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> { + constants: &'c mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, + span: Span, + mir: &'c mir::Mir<'tcx>, + fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>, } -struct ConstantExtractor { - constants: Vec<(ConstantId, Span)>, +impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { + fn constant(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { + if self.fncx.gecx.statics.contains_key(&def_id) { + return; + } + let cid = ConstantId::Static { + def_id: def_id, + substs: substs, + }; + let mir = self.fncx.load_mir(def_id); + let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static"); + self.constants.push((cid, span, ptr, mir)); + } } -impl<'tcx> Visitor<'tcx> for ConstantExtractor { +impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { self.super_constant(constant); match constant.literal { // already computed by rustc mir::Literal::Value { .. } => {} - mir::Literal::Item { .. } => {}, // FIXME: unimplemented + mir::Literal::Item { def_id, substs } => { + let item_ty = self.fncx.tcx.lookup_item_type(def_id).subst(self.fncx.tcx, substs); + if item_ty.ty.is_fn() { + // unimplemented + } else { + self.constant(def_id, substs, constant.span); + } + }, mir::Literal::Promoted { index } => { - self.constants.push((ConstantId::Promoted { index: index }, constant.span)); + if self.fncx.frame().promoted.contains_key(&index) { + return; + } + let mir = self.mir.promoted[index].clone(); + let return_ty = mir.return_ty; + let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static"); + self.fncx.frame_mut().promoted.insert(index, return_ptr); + let mir = CachedMir::Owned(Rc::new(mir)); + self.constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir)); } } } - fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) { - self.super_statement(block, stmt); - if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind { - self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span)); + fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { + self.super_lvalue(lvalue, context); + if let mir::Lvalue::Static(def_id) = *lvalue { + let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); + let span = self.span; + self.constant(def_id, substs, span); } } } diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 7ff4b1c191c13..754d3d9ee7e6d 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:static should have been cached +//error-pattern:unimplemented: mentions of function items #[miri_run] diff --git a/tests/run-pass/constants.rs b/tests/run-pass/constants.rs new file mode 100644 index 0000000000000..3d6b08c340df5 --- /dev/null +++ b/tests/run-pass/constants.rs @@ -0,0 +1,11 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +const A: usize = *&5; + +#[miri_run] +fn foo() -> usize { + A +} + +fn main() {} From f995db9ffba925590e4c78917362998be02fcbc1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 16:51:51 +0200 Subject: [PATCH 0313/1096] store the current block in the frame --- src/interpreter/mod.rs | 28 +++++++++++++++++++++------- src/interpreter/stepper.rs | 36 +++++++++++++----------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7e59b2674e87f..f20a958cd0e76 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -118,8 +118,8 @@ enum CachedMir<'mir, 'tcx: 'mir> { /// Represents the action to be taken in the main loop as a result of executing a terminator. enum TerminatorTarget { - /// Make a local jump to the given block. - Block(mir::BasicBlock), + /// Make a local jump to the next block + Block, /// Start executing from the new current frame. (For function calls.) Call, @@ -268,12 +268,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let target = match terminator.kind { Return => TerminatorTarget::Return, - Goto { target } => TerminatorTarget::Block(target), + Goto { target } => { + self.frame_mut().next_block = target; + TerminatorTarget::Block + }, If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; - TerminatorTarget::Block(if cond_val { then_target } else { else_target }) + self.frame_mut().next_block = if cond_val { then_target } else { else_target }; + TerminatorTarget::Block } SwitchInt { ref discr, ref values, ref targets, .. } => { @@ -296,7 +300,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - TerminatorTarget::Block(target_block) + self.frame_mut().next_block = target_block; + TerminatorTarget::Block } Switch { ref discr, ref targets, adt_def } => { @@ -307,7 +312,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { .position(|v| discr_val == v.disr_val.to_u64_unchecked()); match matching { - Some(i) => TerminatorTarget::Block(targets[i]), + Some(i) => { + self.frame_mut().next_block = targets[i]; + TerminatorTarget::Block + }, None => return Err(EvalError::InvalidDiscriminant), } } @@ -408,7 +416,8 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let ptr = self.eval_lvalue(value)?.to_ptr(); let ty = self.lvalue_ty(value); self.drop(ptr, ty)?; - TerminatorTarget::Block(target) + self.frame_mut().next_block = target; + TerminatorTarget::Block } Resume => unimplemented!(), @@ -1238,6 +1247,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { self.stack.last().expect("no call frames exist") } + fn basic_block(&self) -> &mir::BasicBlockData<'tcx> { + let frame = self.frame(); + frame.mir.basic_block_data(frame.next_block) + } + fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { self.stack.last_mut().expect("no call frames exist") } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index b84680f733783..4a0dedf8a0f9e 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -22,7 +22,6 @@ pub enum Event { pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, - block: mir::BasicBlock, // a stack of statement positions stmt: Vec, mir: CachedMir<'mir, 'tcx>, @@ -34,7 +33,6 @@ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { Stepper { - block: fncx.frame().next_block, mir: fncx.mir(), fncx: fncx, stmt: vec![0], @@ -46,7 +44,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn dummy(&mut self) -> EvalResult<()> { Ok(()) } fn statement(&mut self) -> EvalResult<()> { - let block_data = self.mir.basic_block_data(self.block); + let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); let stmt = &block_data.statements[*self.stmt.last().unwrap()]; let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); @@ -58,27 +56,23 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn terminator(&mut self) -> EvalResult<()> { *self.stmt.last_mut().unwrap() = 0; let term = { - let block_data = self.mir.basic_block_data(self.block); + let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); let terminator = block_data.terminator(); let result = self.fncx.eval_terminator(terminator); self.fncx.maybe_report(terminator.span, result)? }; match term { - TerminatorTarget::Block(block) => { - self.block = block; - }, + TerminatorTarget::Block => {}, TerminatorTarget::Return => { self.fncx.pop_stack_frame(); self.stmt.pop(); assert!(self.constants.last().unwrap().is_empty()); self.constants.pop(); if !self.fncx.stack.is_empty() { - self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); } }, TerminatorTarget::Call => { - self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); self.stmt.push(0); self.constants.push(Vec::new()); @@ -97,7 +91,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); - self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => { @@ -106,7 +99,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); self.stmt.push(0); self.constants.push(Vec::new()); - self.block = self.fncx.frame().next_block; self.mir = self.fncx.mir(); }, None => unreachable!(), @@ -128,7 +120,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx return Ok(Event::Constant); } - let basic_block = self.mir.basic_block_data(self.block); + let block = self.fncx.frame().next_block; + let basic_block = self.mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(*self.stmt.last().unwrap()) { assert!(self.constants.last().unwrap().is_empty()); @@ -137,7 +130,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx span: stmt.span, fncx: self.fncx, mir: &self.mir, - }.visit_statement(self.block, stmt); + }.visit_statement(block, stmt); if self.constants.last().unwrap().is_empty() { self.process = Self::statement; return Ok(Event::Assignment); @@ -153,7 +146,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx span: terminator.span, fncx: self.fncx, mir: &self.mir, - }.visit_terminator(self.block, terminator); + }.visit_terminator(block, terminator); if self.constants.last().unwrap().is_empty() { self.process = Self::terminator; Ok(Event::Terminator) @@ -163,21 +156,18 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } } - /// returns the basic block index of the currently processed block - pub fn block(&self) -> mir::BasicBlock { - self.block - } - /// returns the statement that will be processed next pub fn stmt(&self) -> &mir::Statement { - let block_data = self.mir.basic_block_data(self.block); - &block_data.statements[*self.stmt.last().unwrap()] + &self.fncx.basic_block().statements[*self.stmt.last().unwrap()] } /// returns the terminator of the current block pub fn term(&self) -> &mir::Terminator { - let block_data = self.mir.basic_block_data(self.block); - block_data.terminator() + self.fncx.basic_block().terminator() + } + + pub fn block(&self) -> mir::BasicBlock { + self.fncx.frame().next_block } } From 346560b31809e2ca5459eb221f093fd0ab1abea1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 16:57:47 +0200 Subject: [PATCH 0314/1096] factor out the statement index into the stackframe --- src/interpreter/mod.rs | 4 ++++ src/interpreter/stepper.rs | 19 +++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f20a958cd0e76..079ed6cf59af9 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -94,6 +94,9 @@ struct Frame<'a, 'tcx: 'a> { /// List of precomputed promoted constants promoted: HashMap, + + /// The index of the currently evaluated statment + stmt: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -246,6 +249,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { span: span, def_id: def_id, substs: substs, + stmt: 0, }); let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 4a0dedf8a0f9e..448fa83fa6766 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -22,8 +22,6 @@ pub enum Event { pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, - // a stack of statement positions - stmt: Vec, mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, // a stack of constants @@ -35,7 +33,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Stepper { mir: fncx.mir(), fncx: fncx, - stmt: vec![0], process: Self::dummy, constants: vec![Vec::new()], } @@ -45,16 +42,17 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn statement(&mut self) -> EvalResult<()> { let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); - let stmt = &block_data.statements[*self.stmt.last().unwrap()]; + let stmt = &block_data.statements[self.fncx.frame().stmt]; let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); self.fncx.maybe_report(stmt.span, result)?; - *self.stmt.last_mut().unwrap() += 1; + self.fncx.frame_mut().stmt += 1; Ok(()) } fn terminator(&mut self) -> EvalResult<()> { - *self.stmt.last_mut().unwrap() = 0; + // after a terminator we go to a new block + self.fncx.frame_mut().stmt = 0; let term = { let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); let terminator = block_data.terminator(); @@ -65,7 +63,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx TerminatorTarget::Block => {}, TerminatorTarget::Return => { self.fncx.pop_stack_frame(); - self.stmt.pop(); assert!(self.constants.last().unwrap().is_empty()); self.constants.pop(); if !self.fncx.stack.is_empty() { @@ -74,7 +71,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx }, TerminatorTarget::Call => { self.mir = self.fncx.mir(); - self.stmt.push(0); self.constants.push(Vec::new()); }, } @@ -89,7 +85,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx // FIXME: somehow encode that this is a promoted constant's frame let def_id = self.fncx.frame().def_id; self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.stmt.push(0); self.constants.push(Vec::new()); self.mir = self.fncx.mir(); }, @@ -97,7 +92,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx trace!("adding static {:?}, {:?}", def_id, span); self.fncx.gecx.statics.insert(def_id, return_ptr); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.stmt.push(0); self.constants.push(Vec::new()); self.mir = self.fncx.mir(); }, @@ -121,9 +115,10 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } let block = self.fncx.frame().next_block; + let stmt = self.fncx.frame().stmt; let basic_block = self.mir.basic_block_data(block); - if let Some(ref stmt) = basic_block.statements.get(*self.stmt.last().unwrap()) { + if let Some(ref stmt) = basic_block.statements.get(stmt) { assert!(self.constants.last().unwrap().is_empty()); ConstantExtractor { constants: &mut self.constants.last_mut().unwrap(), @@ -158,7 +153,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx /// returns the statement that will be processed next pub fn stmt(&self) -> &mir::Statement { - &self.fncx.basic_block().statements[*self.stmt.last().unwrap()] + &self.fncx.basic_block().statements[self.fncx.frame().stmt] } /// returns the terminator of the current block From 02eed64cc0c0fdd992713c5b5b1dfdd3813e09bf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 17:04:08 +0200 Subject: [PATCH 0315/1096] update documentation --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 079ed6cf59af9..d97ad5d476283 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -75,7 +75,7 @@ struct Frame<'a, 'tcx: 'a> { /// The MIR for the function called on this frame. mir: CachedMir<'a, 'tcx>, - /// The block this frame will execute when a function call returns back to this frame. + /// The block that is currently executed (or will be executed after the above call stacks return) next_block: mir::BasicBlock, /// A pointer for writing the return value of the current call if it's not a diverging call. From 4743842821ffa91546af7806e18f71821ff8f2da Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 17:08:51 +0200 Subject: [PATCH 0316/1096] move constants stack to stackframe --- src/interpreter/mod.rs | 6 +++++- src/interpreter/stepper.rs | 26 ++++++++------------------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d97ad5d476283..9701e1157a4f6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -14,7 +14,7 @@ use std::rc::Rc; use std::{iter, mem}; use syntax::ast; use syntax::attr; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; @@ -97,6 +97,9 @@ struct Frame<'a, 'tcx: 'a> { /// The index of the currently evaluated statment stmt: usize, + + // Constants that need to be evaluated before the next statement can be evaluated + constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'a, 'tcx>)>, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -250,6 +253,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { def_id: def_id, substs: substs, stmt: 0, + constants: Vec::new(), }); let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 448fa83fa6766..8abc7830f5182 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -24,8 +24,6 @@ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, - // a stack of constants - constants: Vec, Span, Pointer, CachedMir<'mir, 'tcx>)>>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { @@ -34,7 +32,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: fncx.mir(), fncx: fncx, process: Self::dummy, - constants: vec![Vec::new()], } } @@ -62,37 +59,33 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx match term { TerminatorTarget::Block => {}, TerminatorTarget::Return => { + assert!(self.fncx.frame().constants.is_empty()); self.fncx.pop_stack_frame(); - assert!(self.constants.last().unwrap().is_empty()); - self.constants.pop(); if !self.fncx.stack.is_empty() { self.mir = self.fncx.mir(); } }, TerminatorTarget::Call => { self.mir = self.fncx.mir(); - self.constants.push(Vec::new()); }, } Ok(()) } fn constant(&mut self) -> EvalResult<()> { - match self.constants.last_mut().unwrap().pop() { + match self.fncx.frame_mut().constants.pop() { Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => { trace!("adding promoted constant {}, {:?}", index, span); let substs = self.fncx.substs(); // FIXME: somehow encode that this is a promoted constant's frame let def_id = self.fncx.frame().def_id; self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.constants.push(Vec::new()); self.mir = self.fncx.mir(); }, Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => { trace!("adding static {:?}, {:?}", def_id, span); self.fncx.gecx.statics.insert(def_id, return_ptr); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.constants.push(Vec::new()); self.mir = self.fncx.mir(); }, None => unreachable!(), @@ -109,7 +102,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx return Ok(Event::Done); } - if !self.constants.last().unwrap().is_empty() { + if !self.fncx.frame().constants.is_empty() { self.process = Self::constant; return Ok(Event::Constant); } @@ -119,14 +112,13 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let basic_block = self.mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { - assert!(self.constants.last().unwrap().is_empty()); + assert!(self.fncx.frame().constants.is_empty()); ConstantExtractor { - constants: &mut self.constants.last_mut().unwrap(), span: stmt.span, fncx: self.fncx, mir: &self.mir, }.visit_statement(block, stmt); - if self.constants.last().unwrap().is_empty() { + if self.fncx.frame().constants.is_empty() { self.process = Self::statement; return Ok(Event::Assignment); } else { @@ -137,12 +129,11 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let terminator = basic_block.terminator(); ConstantExtractor { - constants: &mut self.constants.last_mut().unwrap(), span: terminator.span, fncx: self.fncx, mir: &self.mir, }.visit_terminator(block, terminator); - if self.constants.last().unwrap().is_empty() { + if self.fncx.frame().constants.is_empty() { self.process = Self::terminator; Ok(Event::Terminator) } else { @@ -167,7 +158,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> { - constants: &'c mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, span: Span, mir: &'c mir::Mir<'tcx>, fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>, @@ -184,7 +174,7 @@ impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { }; let mir = self.fncx.load_mir(def_id); let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static"); - self.constants.push((cid, span, ptr, mir)); + self.fncx.frame_mut().constants.push((cid, span, ptr, mir)); } } @@ -211,7 +201,7 @@ impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mi let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static"); self.fncx.frame_mut().promoted.insert(index, return_ptr); let mir = CachedMir::Owned(Rc::new(mir)); - self.constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir)); + self.fncx.frame_mut().constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir)); } } } From dc85b1142111401f33a797fc449d8e36291f4444 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 17:14:36 +0200 Subject: [PATCH 0317/1096] stop producing executable files in the root folder during benchmarks... --- benches/miri_helper.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benches/miri_helper.rs b/benches/miri_helper.rs index 54c15a27ed884..e085373a36dc0 100644 --- a/benches/miri_helper.rs +++ b/benches/miri_helper.rs @@ -10,7 +10,7 @@ extern crate test; use self::miri::interpreter; use self::rustc::session::Session; -use self::rustc_driver::{driver, CompilerCalls}; +use self::rustc_driver::{driver, CompilerCalls, Compilation}; use std::cell::RefCell; use std::rc::Rc; use std::env::var; @@ -35,6 +35,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { let bencher = self.0.clone(); + control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(move |state| { state.session.abort_if_errors(); bencher.borrow_mut().iter(|| { From 4c833a54d2ff4e180d8d2e9dd81274ad9c8cc367 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jun 2016 17:41:36 +0200 Subject: [PATCH 0318/1096] globally cache statics and promoteds --- src/interpreter/mod.rs | 48 +++++++++++++++++++++++++++++--------- src/interpreter/stepper.rs | 47 ++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9701e1157a4f6..1b8be7bebd62d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -37,8 +37,8 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// The virtual memory system. memory: Memory, - /// Precomputed statics and constants - statics: DefIdMap, + /// Precomputed statics, constants and promoteds + statics: HashMap, Pointer>, } struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> { @@ -92,9 +92,6 @@ struct Frame<'a, 'tcx: 'a> { /// The offset of the first temporary in `self.locals`. temp_offset: usize, - /// List of precomputed promoted constants - promoted: HashMap, - /// The index of the currently evaluated statment stmt: usize, @@ -136,10 +133,28 @@ enum TerminatorTarget { #[derive(Clone, Debug, Eq, PartialEq, Hash)] enum ConstantId<'tcx> { - Promoted { index: usize }, + Promoted { def_id: DefId, substs: &'tcx Substs<'tcx>, index: usize }, Static { def_id: DefId, substs: &'tcx Substs<'tcx> }, } +impl<'tcx> ConstantId<'tcx> { + fn substs(&self) -> &'tcx Substs<'tcx> { + use self::ConstantId::*; + match *self { + Promoted { substs, .. } | + Static { substs, .. } => substs + } + } + + fn def_id(&self) -> DefId { + use self::ConstantId::*; + match *self { + Promoted { def_id, .. } | + Static { def_id, .. } => def_id, + } + } +} + impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { @@ -152,7 +167,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { .uint_type .bit_width() .expect("Session::target::uint_type was usize")/8), - statics: DefIdMap(), + statics: HashMap::new(), } } @@ -248,7 +263,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { locals: Vec::new(), var_offset: num_args, temp_offset: num_args + num_vars, - promoted: HashMap::new(), span: span, def_id: def_id, substs: substs, @@ -1025,10 +1039,18 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { if item_ty.ty.is_fn() { Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) } else { - Ok(*self.statics.get(&def_id).expect("static should have been cached (rvalue)")) + let cid = ConstantId::Static{ def_id: def_id, substs: substs }; + Ok(*self.statics.get(&cid).expect("static should have been cached (rvalue)")) } }, - Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")), + Promoted { index } => { + let cid = ConstantId::Promoted { + def_id: self.frame().def_id, + substs: self.substs(), + index: index, + }; + Ok(*self.statics.get(&cid).expect("a promoted constant hasn't been precomputed")) + }, } } } @@ -1043,7 +1065,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Var(i) => self.frame().locals[self.frame().var_offset + i as usize], Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], - Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached (lvalue)"), + Static(def_id) => { + let substs = self.tcx.mk_substs(subst::Substs::empty()); + let cid = ConstantId::Static{ def_id: def_id, substs: substs }; + *self.gecx.statics.get(&cid).expect("static should have been cached (lvalue)") + }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 8abc7830f5182..1a2fbe43408a0 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -10,7 +10,6 @@ use rustc::ty::subst::{self, Subst}; use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; -use memory::Pointer; use std::rc::Rc; pub enum Event { @@ -73,23 +72,11 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } fn constant(&mut self) -> EvalResult<()> { - match self.fncx.frame_mut().constants.pop() { - Some((ConstantId::Promoted { index }, span, return_ptr, mir)) => { - trace!("adding promoted constant {}, {:?}", index, span); - let substs = self.fncx.substs(); - // FIXME: somehow encode that this is a promoted constant's frame - let def_id = self.fncx.frame().def_id; - self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.mir = self.fncx.mir(); - }, - Some((ConstantId::Static { def_id, substs }, span, return_ptr, mir)) => { - trace!("adding static {:?}, {:?}", def_id, span); - self.fncx.gecx.statics.insert(def_id, return_ptr); - self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.mir = self.fncx.mir(); - }, - None => unreachable!(), - } + let (cid, span, return_ptr, mir) = self.fncx.frame_mut().constants.pop().expect("state machine broken"); + let def_id = cid.def_id(); + let substs = cid.substs(); + self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); + self.mir = self.fncx.mir(); Ok(()) } @@ -164,16 +151,17 @@ struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b } impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { - fn constant(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { - if self.fncx.gecx.statics.contains_key(&def_id) { - return; - } + fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { let cid = ConstantId::Static { def_id: def_id, substs: substs, }; + if self.fncx.gecx.statics.contains_key(&cid) { + return; + } let mir = self.fncx.load_mir(def_id); let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static"); + self.fncx.statics.insert(cid.clone(), ptr); self.fncx.frame_mut().constants.push((cid, span, ptr, mir)); } } @@ -189,19 +177,24 @@ impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mi if item_ty.ty.is_fn() { // unimplemented } else { - self.constant(def_id, substs, constant.span); + self.static_item(def_id, substs, constant.span); } }, mir::Literal::Promoted { index } => { - if self.fncx.frame().promoted.contains_key(&index) { + let cid = ConstantId::Promoted { + def_id: self.fncx.frame().def_id, + substs: self.fncx.substs(), + index: index, + }; + if self.fncx.statics.contains_key(&cid) { return; } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static"); - self.fncx.frame_mut().promoted.insert(index, return_ptr); let mir = CachedMir::Owned(Rc::new(mir)); - self.fncx.frame_mut().constants.push((ConstantId::Promoted { index: index }, constant.span, return_ptr, mir)); + self.fncx.statics.insert(cid.clone(), return_ptr); + self.fncx.frame_mut().constants.push((cid, constant.span, return_ptr, mir)); } } } @@ -211,7 +204,7 @@ impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mi if let mir::Lvalue::Static(def_id) = *lvalue { let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); let span = self.span; - self.constant(def_id, substs, span); + self.static_item(def_id, substs, span); } } } From 4d44a970a39d4bd6ec517b3958e180def9040e3d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 09:38:59 +0200 Subject: [PATCH 0319/1096] move some methods from FnEvalContext to GlobalEvalContext --- src/interpreter/mod.rs | 256 +++++++++++++++++++++++++---------------- 1 file changed, 160 insertions(+), 96 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1b8be7bebd62d..5072270f1aa7b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -173,16 +173,150 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { let substs = self.tcx.mk_substs(subst::Substs::empty()); + let return_ptr = self.alloc_ret_ptr(mir.return_ty, substs); let mut nested_fecx = FnEvalContext::new(self); nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - let return_ptr = nested_fecx.alloc_ret_ptr(mir.return_ty); + nested_fecx.frame_mut().return_ptr = return_ptr; nested_fecx.run()?; Ok(return_ptr) } + + fn alloc_ret_ptr(&mut self, ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + match ty { + ty::FnConverging(ty) => { + let size = self.type_size(ty, substs); + Some(self.memory.allocate(size)) + } + ty::FnDiverging => None, + } + } + // TODO(solson): Try making const_to_primval instead. + fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { + use rustc::middle::const_val::ConstVal::*; + match *const_val { + Float(_f) => unimplemented!(), + Integral(int) => { + // TODO(solson): Check int constant type. + let ptr = self.memory.allocate(8); + self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)?; + Ok(ptr) + } + Str(ref s) => { + let psize = self.memory.pointer_size; + let static_ptr = self.memory.allocate(s.len()); + let ptr = self.memory.allocate(psize * 2); + self.memory.write_bytes(static_ptr, s.as_bytes())?; + self.memory.write_ptr(ptr, static_ptr)?; + self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; + Ok(ptr) + } + ByteStr(ref bs) => { + let psize = self.memory.pointer_size; + let static_ptr = self.memory.allocate(bs.len()); + let ptr = self.memory.allocate(psize); + self.memory.write_bytes(static_ptr, bs)?; + self.memory.write_ptr(ptr, static_ptr)?; + Ok(ptr) + } + Bool(b) => { + let ptr = self.memory.allocate(1); + self.memory.write_bool(ptr, b)?; + Ok(ptr) + } + Char(_c) => unimplemented!(), + Struct(_node_id) => unimplemented!(), + Tuple(_node_id) => unimplemented!(), + Function(_def_id) => unimplemented!(), + Array(_, _) => unimplemented!(), + Repeat(_, _) => unimplemented!(), + Dummy => unimplemented!(), + } + } + + fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) + } + + fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) + } + + fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + // Do the initial selection for the obligation. This yields the shallow result we are + // looking for -- that is, what specific impl. + self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // Currently, we use a fulfillment context to completely resolve all nested obligations. + // This is because they can inform the inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) + }) + } + + /// Trait method, which has to be resolved to an impl method. + pub fn trait_method( + &self, + def_id: DefId, + substs: &'tcx Substs<'tcx> + ) -> (DefId, &'tcx Substs<'tcx>) { + let method_item = self.tcx.impl_or_trait_item(def_id); + let trait_id = method_item.container().id(); + let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); + match self.fulfill_obligation(trait_ref) { + traits::VtableImpl(vtable_impl) => { + let impl_did = vtable_impl.impl_def_id; + let mname = self.tcx.item_name(def_id); + // Create a concatenated set of substitutions which includes those from the impl + // and those from the method: + let impl_substs = vtable_impl.substs.with_method_from(substs); + let substs = self.tcx.mk_substs(impl_substs); + let mth = get_impl_method(self.tcx, impl_did, substs, mname); + + (mth.method.def_id, mth.substs) + } + + traits::VtableClosure(vtable_closure) => + (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), + + traits::VtableFnPointer(_fn_ty) => { + let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + unimplemented!() + // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); + + // let method_ty = def_ty(tcx, def_id, substs); + // let fn_ptr_ty = match method_ty.sty { + // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + // _ => unreachable!("expected fn item type, found {}", + // method_ty) + // }; + // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + } + + traits::VtableObject(ref _data) => { + unimplemented!() + // Callee { + // data: Virtual(traits::get_vtable_index_of_object_method( + // tcx, data, def_id)), + // ty: def_ty(tcx, def_id, substs) + // } + } + vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), + } + } } impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { @@ -193,33 +327,36 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } - fn alloc_ret_ptr(&mut self, ty: ty::FnOutput<'tcx>) -> Option { - match ty { - ty::FnConverging(ty) => { - let size = self.type_size(ty); - Some(self.memory.allocate(size)) + #[inline(never)] + #[cold] + fn report(&self, e: &EvalError) { + let stmt = self.frame().stmt; + let block = self.basic_block(); + let span = if stmt < block.statements.len() { + block.statements[stmt].span + } else { + block.terminator().span + }; + let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); + for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() { + // FIXME(solson): Find a way to do this without this Display impl hack. + use rustc::util::ppaux; + use std::fmt; + struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); + impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], + |tcx| tcx.lookup_item_type(self.0).generics) + } } - ty::FnDiverging => None, + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); } + err.emit(); } - fn maybe_report(&self, span: codemap::Span, r: EvalResult) -> EvalResult { + fn maybe_report(&self, r: EvalResult) -> EvalResult { if let Err(ref e) = r { - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() { - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(self.0).generics) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); - } - err.emit(); + self.report(e); } r } @@ -1317,79 +1454,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } } } - - fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { - // Do the initial selection for the obligation. This yields the shallow result we are - // looking for -- that is, what specific impl. - self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // Currently, we use a fulfillment context to completely resolve all nested obligations. - // This is because they can inform the inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) - }) - } - - /// Trait method, which has to be resolved to an impl method. - pub fn trait_method( - &self, - def_id: DefId, - substs: &'tcx Substs<'tcx> - ) -> (DefId, &'tcx Substs<'tcx>) { - let method_item = self.tcx.impl_or_trait_item(def_id); - let trait_id = method_item.container().id(); - let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); - match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(vtable_impl) => { - let impl_did = vtable_impl.impl_def_id; - let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the impl - // and those from the method: - let impl_substs = vtable_impl.substs.with_method_from(substs); - let substs = self.tcx.mk_substs(impl_substs); - let mth = get_impl_method(self.tcx, impl_did, substs, mname); - - (mth.method.def_id, mth.substs) - } - - traits::VtableClosure(vtable_closure) => - (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), - - traits::VtableFnPointer(_fn_ty) => { - let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - unimplemented!() - // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); - - // let method_ty = def_ty(tcx, def_id, substs); - // let fn_ptr_ty = match method_ty.sty { - // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - // _ => unreachable!("expected fn item type, found {}", - // method_ty) - // }; - // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) - } - - traits::VtableObject(ref _data) => { - unimplemented!() - // Callee { - // data: Virtual(traits::get_vtable_index_of_object_method( - // tcx, data, def_id)), - // ty: def_ty(tcx, def_id, substs) - // } - } - vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), - } - } } fn pointee_type(ptr_ty: ty::Ty) -> Option { From f42be6db54660b73f814e6083a87005e799d5666 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 6 Jun 2016 15:22:33 +0200 Subject: [PATCH 0320/1096] move load_mir to the global eval context --- src/interpreter/mod.rs | 44 +++++++++++++++++-------------------- src/interpreter/stepper.rs | 45 +++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5072270f1aa7b..22db69a2afff8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -317,6 +317,26 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), } } + + fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + match self.tcx.map.as_local_node_id(def_id) { + Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), + None => { + let mut mir_cache = self.mir_cache.borrow_mut(); + if let Some(mir) = mir_cache.get(&def_id) { + return CachedMir::Owned(mir.clone()); + } + + let cs = &self.tcx.sess.cstore; + let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { + panic!("no mir for {:?}", def_id); + }); + let cached = Rc::new(mir); + mir_cache.insert(def_id, cached.clone()); + CachedMir::Owned(cached) + } + } + } } impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { @@ -1430,30 +1450,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn mir(&self) -> CachedMir<'mir, 'tcx> { self.frame().mir.clone() } - - fn substs(&self) -> &'tcx Substs<'tcx> { - self.frame().substs - } - - fn load_mir(&self, def_id: DefId) -> CachedMir<'mir, 'tcx> { - match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), - None => { - let mut mir_cache = self.mir_cache.borrow_mut(); - if let Some(mir) = mir_cache.get(&def_id) { - return CachedMir::Owned(mir.clone()); - } - - let cs = &self.tcx.sess.cstore; - let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for {:?}", def_id); - }); - let cached = Rc::new(mir); - mir_cache.insert(def_id, cached.clone()); - CachedMir::Owned(cached) - } - } - } } fn pointee_type(ptr_ty: ty::Ty) -> Option { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 1a2fbe43408a0..6fb287228be88 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -3,6 +3,7 @@ use super::{ CachedMir, TerminatorTarget, ConstantId, + GlobalEvalContext }; use error::EvalResult; use rustc::mir::repr as mir; @@ -102,8 +103,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx assert!(self.fncx.frame().constants.is_empty()); ConstantExtractor { span: stmt.span, - fncx: self.fncx, mir: &self.mir, + gecx: self.fncx.gecx, + frame: self.fncx.stack.last_mut().expect("stack empty"), }.visit_statement(block, stmt); if self.fncx.frame().constants.is_empty() { self.process = Self::statement; @@ -115,10 +117,12 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } let terminator = basic_block.terminator(); + assert!(self.fncx.frame().constants.is_empty()); ConstantExtractor { span: terminator.span, - fncx: self.fncx, mir: &self.mir, + gecx: self.fncx.gecx, + frame: self.fncx.stack.last_mut().expect("stack empty"), }.visit_terminator(block, terminator); if self.fncx.frame().constants.is_empty() { self.process = Self::terminator; @@ -144,36 +148,37 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } } -struct ConstantExtractor<'a: 'c, 'b: 'a + 'mir + 'c, 'c, 'mir: 'c, 'tcx: 'a + 'b + 'c> { +struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { span: Span, - mir: &'c mir::Mir<'tcx>, - fncx: &'c mut FnEvalContext<'a, 'b, 'mir, 'tcx>, + mir: &'a CachedMir<'mir, 'tcx>, + frame: &'a mut Frame<'mir, 'tcx>, + gecx: &'a mut GlobalEvalContext<'b, 'tcx>, } -impl<'a, 'b, 'c, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { +impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { let cid = ConstantId::Static { def_id: def_id, substs: substs, }; - if self.fncx.gecx.statics.contains_key(&cid) { + if self.gecx.statics.contains_key(&cid) { return; } - let mir = self.fncx.load_mir(def_id); - let ptr = self.fncx.alloc_ret_ptr(mir.return_ty).expect("there's no such thing as an unreachable static"); - self.fncx.statics.insert(cid.clone(), ptr); - self.fncx.frame_mut().constants.push((cid, span, ptr, mir)); + let mir = self.gecx.load_mir(def_id); + let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); + self.gecx.statics.insert(cid.clone(), ptr); + self.frame.constants.push((cid, span, ptr, mir)); } } -impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mir, 'tcx> { +impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { self.super_constant(constant); match constant.literal { // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - let item_ty = self.fncx.tcx.lookup_item_type(def_id).subst(self.fncx.tcx, substs); + let item_ty = self.gecx.tcx.lookup_item_type(def_id).subst(self.gecx.tcx, substs); if item_ty.ty.is_fn() { // unimplemented } else { @@ -182,19 +187,19 @@ impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mi }, mir::Literal::Promoted { index } => { let cid = ConstantId::Promoted { - def_id: self.fncx.frame().def_id, - substs: self.fncx.substs(), + def_id: self.frame.def_id, + substs: self.frame.substs, index: index, }; - if self.fncx.statics.contains_key(&cid) { + if self.gecx.statics.contains_key(&cid) { return; } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; - let return_ptr = self.fncx.alloc_ret_ptr(return_ty).expect("there's no such thing as an unreachable static"); + let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs()).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); - self.fncx.statics.insert(cid.clone(), return_ptr); - self.fncx.frame_mut().constants.push((cid, constant.span, return_ptr, mir)); + self.gecx.statics.insert(cid.clone(), return_ptr); + self.frame.constants.push((cid, constant.span, return_ptr, mir)); } } } @@ -202,7 +207,7 @@ impl<'a, 'b, 'c, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'c, 'mi fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { self.super_lvalue(lvalue, context); if let mir::Lvalue::Static(def_id) = *lvalue { - let substs = self.fncx.tcx.mk_substs(subst::Substs::empty()); + let substs = self.gecx.tcx.mk_substs(subst::Substs::empty()); let span = self.span; self.static_item(def_id, substs, span); } From c881cf10d86deca32093eb40319a24a793de7d0b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 6 Jun 2016 15:24:56 +0200 Subject: [PATCH 0321/1096] clippy nits --- src/interpreter/stepper.rs | 2 +- src/memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 6fb287228be88..f733d1d46c838 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -129,7 +129,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Ok(Event::Terminator) } else { self.process = Self::constant; - return Ok(Event::Constant); + Ok(Event::Constant) } } diff --git a/src/memory.rs b/src/memory.rs index 728d5310f8611..ab527a47c0bf5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -88,7 +88,7 @@ impl Memory { alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation"))); + return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size))); // alloc.bytes.truncate(new_size); // alloc.undef_mask.len = new_size; // TODO: potentially remove relocations From 1f27d3f7b3816fde346c5140801c7dd5ced3f823 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 10:17:26 +0200 Subject: [PATCH 0322/1096] don't cache the MIR in the Stepper --- src/interpreter/stepper.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index f733d1d46c838..8807dbcafab6a 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -22,14 +22,12 @@ pub enum Event { pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, - mir: CachedMir<'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { Stepper { - mir: fncx.mir(), fncx: fncx, process: Self::dummy, } @@ -38,7 +36,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn dummy(&mut self) -> EvalResult<()> { Ok(()) } fn statement(&mut self) -> EvalResult<()> { - let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); + let mir = self.fncx.mir(); + let block_data = mir.basic_block_data(self.fncx.frame().next_block); let stmt = &block_data.statements[self.fncx.frame().stmt]; let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); @@ -51,7 +50,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx // after a terminator we go to a new block self.fncx.frame_mut().stmt = 0; let term = { - let block_data = self.mir.basic_block_data(self.fncx.frame().next_block); + let mir = self.fncx.mir(); + let block_data = mir.basic_block_data(self.fncx.frame().next_block); let terminator = block_data.terminator(); let result = self.fncx.eval_terminator(terminator); self.fncx.maybe_report(terminator.span, result)? @@ -61,13 +61,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx TerminatorTarget::Return => { assert!(self.fncx.frame().constants.is_empty()); self.fncx.pop_stack_frame(); - if !self.fncx.stack.is_empty() { - self.mir = self.fncx.mir(); - } - }, - TerminatorTarget::Call => { - self.mir = self.fncx.mir(); }, + TerminatorTarget::Call => {}, } Ok(()) } @@ -77,7 +72,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let def_id = cid.def_id(); let substs = cid.substs(); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - self.mir = self.fncx.mir(); Ok(()) } @@ -97,13 +91,13 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let block = self.fncx.frame().next_block; let stmt = self.fncx.frame().stmt; - let basic_block = self.mir.basic_block_data(block); + let mir = self.fncx.mir(); + let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { assert!(self.fncx.frame().constants.is_empty()); ConstantExtractor { span: stmt.span, - mir: &self.mir, gecx: self.fncx.gecx, frame: self.fncx.stack.last_mut().expect("stack empty"), }.visit_statement(block, stmt); @@ -120,7 +114,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx assert!(self.fncx.frame().constants.is_empty()); ConstantExtractor { span: terminator.span, - mir: &self.mir, gecx: self.fncx.gecx, frame: self.fncx.stack.last_mut().expect("stack empty"), }.visit_terminator(block, terminator); @@ -150,7 +143,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { span: Span, - mir: &'a CachedMir<'mir, 'tcx>, frame: &'a mut Frame<'mir, 'tcx>, gecx: &'a mut GlobalEvalContext<'b, 'tcx>, } @@ -194,7 +186,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> if self.gecx.statics.contains_key(&cid) { return; } - let mir = self.mir.promoted[index].clone(); + let mir = self.frame.mir.promoted[index].clone(); let return_ty = mir.return_ty; let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs()).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); From 6b939bbd798c42b9e992da36f9158b711063a731 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 11:11:08 +0200 Subject: [PATCH 0323/1096] rebase leftovers --- src/interpreter/mod.rs | 88 ++++++++++++-------------------------- src/interpreter/stepper.rs | 7 +-- 2 files changed, 31 insertions(+), 64 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 22db69a2afff8..c1a91fee79761 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -194,6 +194,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { ty::FnDiverging => None, } } + // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { use rustc::middle::const_val::ConstVal::*; @@ -337,6 +338,25 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } } } + + fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { + let substituted = ty.subst(self.tcx, substs); + self.tcx.normalize_associated_type(&substituted) + } + + fn type_size(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout(ty, substs).size(&self.tcx.data_layout).bytes() as usize + } + + fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { + // TODO(solson): Is this inefficient? Needs investigation. + let ty = self.monomorphize(ty, substs); + + self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + // TODO(solson): Report this error properly. + ty.layout(&infcx).unwrap() + }) + } } impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { @@ -1308,49 +1328,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } - // TODO(solson): Try making const_to_primval instead. - fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { - use rustc::middle::const_val::ConstVal::*; - match *const_val { - Float(_f) => unimplemented!(), - Integral(int) => { - // TODO(solson): Check int constant type. - let ptr = self.memory.allocate(8); - self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)?; - Ok(ptr) - } - Str(ref s) => { - let psize = self.memory.pointer_size; - let static_ptr = self.memory.allocate(s.len()); - let ptr = self.memory.allocate(psize * 2); - self.memory.write_bytes(static_ptr, s.as_bytes())?; - self.memory.write_ptr(ptr, static_ptr)?; - self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; - Ok(ptr) - } - ByteStr(ref bs) => { - let psize = self.memory.pointer_size; - let static_ptr = self.memory.allocate(bs.len()); - let ptr = self.memory.allocate(psize); - self.memory.write_bytes(static_ptr, bs)?; - self.memory.write_ptr(ptr, static_ptr)?; - Ok(ptr) - } - Bool(b) => { - let ptr = self.memory.allocate(1); - self.memory.write_bool(ptr, b)?; - Ok(ptr) - } - Char(_c) => unimplemented!(), - Struct(_node_id) => unimplemented!(), - Tuple(_node_id) => unimplemented!(), - Function(_def_id) => unimplemented!(), - Array(_, _) => unimplemented!(), - Repeat(_, _) => unimplemented!(), - Dummy => unimplemented!(), - } - } - fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx)) } @@ -1360,12 +1337,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn monomorphize(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - let substituted = ty.subst(self.tcx, self.substs()); - self.tcx.normalize_associated_type(&substituted) - } - - fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) + self.gecx.monomorphize(ty, self.substs()) } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { @@ -1377,22 +1349,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) - } - fn type_size(&self, ty: Ty<'tcx>) -> usize { - self.type_layout(ty).size(&self.tcx.data_layout).bytes() as usize + self.gecx.type_size(ty, self.substs()) } fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { - // TODO(solson): Is this inefficient? Needs investigation. - let ty = self.monomorphize(ty); - - self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { - // TODO(solson): Report this error properly. - ty.layout(&infcx).unwrap() - }) + self.gecx.type_layout(ty, self.substs()) } pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { @@ -1450,6 +1412,10 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn mir(&self) -> CachedMir<'mir, 'tcx> { self.frame().mir.clone() } + + fn substs(&self) -> &'tcx Substs<'tcx> { + self.frame().substs + } } fn pointee_type(ptr_ty: ty::Ty) -> Option { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 8807dbcafab6a..fffac08fc6f49 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -3,7 +3,8 @@ use super::{ CachedMir, TerminatorTarget, ConstantId, - GlobalEvalContext + GlobalEvalContext, + Frame, }; use error::EvalResult; use rustc::mir::repr as mir; @@ -41,7 +42,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let stmt = &block_data.statements[self.fncx.frame().stmt]; let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); - self.fncx.maybe_report(stmt.span, result)?; + self.fncx.maybe_report(result)?; self.fncx.frame_mut().stmt += 1; Ok(()) } @@ -54,7 +55,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx let block_data = mir.basic_block_data(self.fncx.frame().next_block); let terminator = block_data.terminator(); let result = self.fncx.eval_terminator(terminator); - self.fncx.maybe_report(terminator.span, result)? + self.fncx.maybe_report(result)? }; match term { TerminatorTarget::Block => {}, From 8b25bc8a9a709c516d986193bce7a74ed1861bcc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 11:11:33 +0200 Subject: [PATCH 0324/1096] directly push stackframes for constants when they are encountered --- src/interpreter/mod.rs | 8 ++--- src/interpreter/stepper.rs | 69 ++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c1a91fee79761..a05dfb734bf5b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -14,7 +14,7 @@ use std::rc::Rc; use std::{iter, mem}; use syntax::ast; use syntax::attr; -use syntax::codemap::{self, DUMMY_SP, Span}; +use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; @@ -94,9 +94,6 @@ struct Frame<'a, 'tcx: 'a> { /// The index of the currently evaluated statment stmt: usize, - - // Constants that need to be evaluated before the next statement can be evaluated - constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'a, 'tcx>)>, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -409,7 +406,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { loop { match stepper.step()? { - Constant => trace!("next statement requires the computation of a constant"), + Constant => trace!("computing a constant"), Assignment => trace!("{:?}", stepper.stmt()), Terminator => { trace!("{:?}", stepper.term().kind); @@ -444,7 +441,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { def_id: def_id, substs: substs, stmt: 0, - constants: Vec::new(), }); let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index fffac08fc6f49..7499eb07ae918 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -4,7 +4,6 @@ use super::{ TerminatorTarget, ConstantId, GlobalEvalContext, - Frame, }; use error::EvalResult; use rustc::mir::repr as mir; @@ -13,6 +12,7 @@ use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; +use memory::Pointer; pub enum Event { Constant, @@ -24,6 +24,10 @@ pub enum Event { pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, + + // a cache of the constants to be computed before the next statement/terminator + // this is an optimization, so we don't have to allocate a new vector for every statement + constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, } impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { @@ -31,6 +35,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Stepper { fncx: fncx, process: Self::dummy, + constants: Vec::new(), } } @@ -60,7 +65,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx match term { TerminatorTarget::Block => {}, TerminatorTarget::Return => { - assert!(self.fncx.frame().constants.is_empty()); self.fncx.pop_stack_frame(); }, TerminatorTarget::Call => {}, @@ -68,14 +72,6 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Ok(()) } - fn constant(&mut self) -> EvalResult<()> { - let (cid, span, return_ptr, mir) = self.fncx.frame_mut().constants.pop().expect("state machine broken"); - let def_id = cid.def_id(); - let substs = cid.substs(); - self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); - Ok(()) - } - pub fn step(&mut self) -> EvalResult { (self.process)(self)?; @@ -85,48 +81,60 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx return Ok(Event::Done); } - if !self.fncx.frame().constants.is_empty() { - self.process = Self::constant; - return Ok(Event::Constant); - } - let block = self.fncx.frame().next_block; let stmt = self.fncx.frame().stmt; let mir = self.fncx.mir(); let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { - assert!(self.fncx.frame().constants.is_empty()); + assert!(self.constants.is_empty()); ConstantExtractor { span: stmt.span, + substs: self.fncx.substs(), + def_id: self.fncx.frame().def_id, gecx: self.fncx.gecx, - frame: self.fncx.stack.last_mut().expect("stack empty"), + constants: &mut self.constants, + mir: &mir, }.visit_statement(block, stmt); - if self.fncx.frame().constants.is_empty() { + if self.constants.is_empty() { self.process = Self::statement; return Ok(Event::Assignment); } else { - self.process = Self::constant; + self.process = Self::statement; + self.extract_constants(); return Ok(Event::Constant); } } let terminator = basic_block.terminator(); - assert!(self.fncx.frame().constants.is_empty()); + assert!(self.constants.is_empty()); ConstantExtractor { span: terminator.span, + substs: self.fncx.substs(), + def_id: self.fncx.frame().def_id, gecx: self.fncx.gecx, - frame: self.fncx.stack.last_mut().expect("stack empty"), + constants: &mut self.constants, + mir: &mir, }.visit_terminator(block, terminator); - if self.fncx.frame().constants.is_empty() { + if self.constants.is_empty() { self.process = Self::terminator; Ok(Event::Terminator) } else { - self.process = Self::constant; + self.process = Self::statement; + self.extract_constants(); Ok(Event::Constant) } } + fn extract_constants(&mut self) { + assert!(!self.constants.is_empty()); + for (cid, span, return_ptr, mir) in self.constants.drain(..) { + let def_id = cid.def_id(); + let substs = cid.substs(); + self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); + } + } + /// returns the statement that will be processed next pub fn stmt(&self) -> &mir::Statement { &self.fncx.basic_block().statements[self.fncx.frame().stmt] @@ -144,8 +152,11 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { span: Span, - frame: &'a mut Frame<'mir, 'tcx>, + constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, gecx: &'a mut GlobalEvalContext<'b, 'tcx>, + mir: &'a mir::Mir<'tcx>, + def_id: DefId, + substs: &'tcx subst::Substs<'tcx>, } impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { @@ -160,7 +171,7 @@ impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { let mir = self.gecx.load_mir(def_id); let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); self.gecx.statics.insert(cid.clone(), ptr); - self.frame.constants.push((cid, span, ptr, mir)); + self.constants.push((cid, span, ptr, mir)); } } @@ -180,19 +191,19 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> }, mir::Literal::Promoted { index } => { let cid = ConstantId::Promoted { - def_id: self.frame.def_id, - substs: self.frame.substs, + def_id: self.def_id, + substs: self.substs, index: index, }; if self.gecx.statics.contains_key(&cid) { return; } - let mir = self.frame.mir.promoted[index].clone(); + let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs()).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); self.gecx.statics.insert(cid.clone(), return_ptr); - self.frame.constants.push((cid, constant.span, return_ptr, mir)); + self.constants.push((cid, constant.span, return_ptr, mir)); } } } From 3de30e33f50377d00c7222db1d98398e9a8bb41d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 11:34:56 +0200 Subject: [PATCH 0325/1096] no more function pointers --- src/interpreter/mod.rs | 24 ++++++-------- src/interpreter/stepper.rs | 66 ++++++++++++++------------------------ 2 files changed, 34 insertions(+), 56 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a05dfb734bf5b..896c740d8007a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -400,22 +400,18 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn run(&mut self) -> EvalResult<()> { let mut stepper = stepper::Stepper::new(self); - 'outer: loop { + let mut done = false; + while !done { use self::stepper::Event::*; - trace!("// {:?}", stepper.block()); - - loop { - match stepper.step()? { - Constant => trace!("computing a constant"), - Assignment => trace!("{:?}", stepper.stmt()), - Terminator => { - trace!("{:?}", stepper.term().kind); - continue 'outer; - }, - Done => return Ok(()), - } - } + stepper.step(|event| match event { + Block(b) => trace!("// {:?}", b), + Assignment(a) => trace!("{:?}", a), + Terminator(t) => trace!("{:?}", t.kind), + Done => done = true, + _ => {}, + })?; } + Ok(()) } fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 7499eb07ae918..d6677c8c319be 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -14,16 +14,18 @@ use syntax::codemap::Span; use std::rc::Rc; use memory::Pointer; -pub enum Event { +pub enum Event<'a, 'tcx: 'a> { + Block(mir::BasicBlock), + Return, + Call, Constant, - Assignment, - Terminator, + Assignment(&'a mir::Statement<'tcx>), + Terminator(&'a mir::Terminator<'tcx>), Done, } pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, - process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, // a cache of the constants to be computed before the next statement/terminator // this is an optimization, so we don't have to allocate a new vector for every statement @@ -34,17 +36,15 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { Stepper { fncx: fncx, - process: Self::dummy, constants: Vec::new(), } } - fn dummy(&mut self) -> EvalResult<()> { Ok(()) } - - fn statement(&mut self) -> EvalResult<()> { + fn statement FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { let mir = self.fncx.mir(); let block_data = mir.basic_block_data(self.fncx.frame().next_block); let stmt = &block_data.statements[self.fncx.frame().stmt]; + f(Event::Assignment(stmt)); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); self.fncx.maybe_report(result)?; @@ -52,33 +52,33 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Ok(()) } - fn terminator(&mut self) -> EvalResult<()> { + fn terminator FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { // after a terminator we go to a new block self.fncx.frame_mut().stmt = 0; let term = { let mir = self.fncx.mir(); let block_data = mir.basic_block_data(self.fncx.frame().next_block); let terminator = block_data.terminator(); + f(Event::Terminator(terminator)); let result = self.fncx.eval_terminator(terminator); self.fncx.maybe_report(result)? }; match term { - TerminatorTarget::Block => {}, + TerminatorTarget::Block => f(Event::Block(self.fncx.frame().next_block)), TerminatorTarget::Return => { + f(Event::Return); self.fncx.pop_stack_frame(); }, - TerminatorTarget::Call => {}, + TerminatorTarget::Call => f(Event::Call), } Ok(()) } - pub fn step(&mut self) -> EvalResult { - (self.process)(self)?; - + // returns true as long as there are more things to do + pub fn step FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { if self.fncx.stack.is_empty() { - // fuse the iterator - self.process = Self::dummy; - return Ok(Event::Done); + f(Event::Done); + return Ok(()); } let block = self.fncx.frame().next_block; @@ -97,12 +97,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_statement(block, stmt); if self.constants.is_empty() { - self.process = Self::statement; - return Ok(Event::Assignment); + return self.statement(f); } else { - self.process = Self::statement; - self.extract_constants(); - return Ok(Event::Constant); + return self.extract_constants(f); } } @@ -117,36 +114,21 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_terminator(block, terminator); if self.constants.is_empty() { - self.process = Self::terminator; - Ok(Event::Terminator) + self.terminator(f) } else { - self.process = Self::statement; - self.extract_constants(); - Ok(Event::Constant) + self.extract_constants(f) } } - fn extract_constants(&mut self) { + fn extract_constants FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { assert!(!self.constants.is_empty()); for (cid, span, return_ptr, mir) in self.constants.drain(..) { let def_id = cid.def_id(); let substs = cid.substs(); + f(Event::Constant); self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); } - } - - /// returns the statement that will be processed next - pub fn stmt(&self) -> &mir::Statement { - &self.fncx.basic_block().statements[self.fncx.frame().stmt] - } - - /// returns the terminator of the current block - pub fn term(&self) -> &mir::Terminator { - self.fncx.basic_block().terminator() - } - - pub fn block(&self) -> mir::BasicBlock { - self.fncx.frame().next_block + self.step(f) } } From 3868a62713245880145caca63a3e456c806a5bb9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 11:46:37 +0200 Subject: [PATCH 0326/1096] put `ConstantId`'s common fields into a struct --- src/interpreter/mod.rs | 44 +++++++++++++++++--------------------- src/interpreter/stepper.rs | 14 ++++++------ 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 896c740d8007a..a7e95af6bbf3f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -129,30 +129,18 @@ enum TerminatorTarget { } #[derive(Clone, Debug, Eq, PartialEq, Hash)] -enum ConstantId<'tcx> { - Promoted { def_id: DefId, substs: &'tcx Substs<'tcx>, index: usize }, - Static { def_id: DefId, substs: &'tcx Substs<'tcx> }, +struct ConstantId<'tcx> { + def_id: DefId, + substs: &'tcx Substs<'tcx>, + kind: ConstantKind, } -impl<'tcx> ConstantId<'tcx> { - fn substs(&self) -> &'tcx Substs<'tcx> { - use self::ConstantId::*; - match *self { - Promoted { substs, .. } | - Static { substs, .. } => substs - } - } - - fn def_id(&self) -> DefId { - use self::ConstantId::*; - match *self { - Promoted { def_id, .. } | - Static { def_id, .. } => def_id, - } - } +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +enum ConstantKind { + Promoted(usize), + Static, } - impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { GlobalEvalContext { @@ -1208,15 +1196,19 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { if item_ty.ty.is_fn() { Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) } else { - let cid = ConstantId::Static{ def_id: def_id, substs: substs }; + let cid = ConstantId { + def_id: def_id, + substs: substs, + kind: ConstantKind::Static, + }; Ok(*self.statics.get(&cid).expect("static should have been cached (rvalue)")) } }, Promoted { index } => { - let cid = ConstantId::Promoted { + let cid = ConstantId { def_id: self.frame().def_id, substs: self.substs(), - index: index, + kind: ConstantKind::Promoted(index), }; Ok(*self.statics.get(&cid).expect("a promoted constant hasn't been precomputed")) }, @@ -1236,7 +1228,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Static(def_id) => { let substs = self.tcx.mk_substs(subst::Substs::empty()); - let cid = ConstantId::Static{ def_id: def_id, substs: substs }; + let cid = ConstantId { + def_id: def_id, + substs: substs, + kind: ConstantKind::Static, + }; *self.gecx.statics.get(&cid).expect("static should have been cached (lvalue)") }, diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index d6677c8c319be..c0e9249b29250 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -4,6 +4,7 @@ use super::{ TerminatorTarget, ConstantId, GlobalEvalContext, + ConstantKind, }; use error::EvalResult; use rustc::mir::repr as mir; @@ -123,10 +124,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn extract_constants FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { assert!(!self.constants.is_empty()); for (cid, span, return_ptr, mir) in self.constants.drain(..) { - let def_id = cid.def_id(); - let substs = cid.substs(); f(Event::Constant); - self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr)); + self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); } self.step(f) } @@ -143,9 +142,10 @@ struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { - let cid = ConstantId::Static { + let cid = ConstantId { def_id: def_id, substs: substs, + kind: ConstantKind::Static, }; if self.gecx.statics.contains_key(&cid) { return; @@ -172,17 +172,17 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> } }, mir::Literal::Promoted { index } => { - let cid = ConstantId::Promoted { + let cid = ConstantId { def_id: self.def_id, substs: self.substs, - index: index, + kind: ConstantKind::Promoted(index), }; if self.gecx.statics.contains_key(&cid) { return; } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; - let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs()).expect("there's no such thing as an unreachable static"); + let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); self.gecx.statics.insert(cid.clone(), return_ptr); self.constants.push((cid, constant.span, return_ptr, mir)); From 240f0c0dd64f0091f98d1c6dd543d306d60ee29c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 12:30:25 +0200 Subject: [PATCH 0327/1096] improve fn argument naming --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7e95af6bbf3f..774db299db6f4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -170,8 +170,8 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { Ok(return_ptr) } - fn alloc_ret_ptr(&mut self, ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { - match ty { + fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + match output_ty { ty::FnConverging(ty) => { let size = self.type_size(ty, substs); Some(self.memory.allocate(size)) From 2178961262e464151bb33feeaaae04fb272d4856 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 12:35:15 +0200 Subject: [PATCH 0328/1096] improve the docs of ConstantId --- src/interpreter/mod.rs | 13 ++++++++++--- src/interpreter/stepper.rs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 774db299db6f4..1942727e47694 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -129,8 +129,14 @@ enum TerminatorTarget { } #[derive(Clone, Debug, Eq, PartialEq, Hash)] +/// Uniquely identifies a specific constant or static struct ConstantId<'tcx> { + /// the def id of the constant/static or in case of promoteds, the def id of the function they belong to def_id: DefId, + /// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated + /// constants actually have something useful here. We could special case statics and constants, + /// but that would only require more branching when working with constants, and not bring any + /// real benefits. substs: &'tcx Substs<'tcx>, kind: ConstantKind, } @@ -138,7 +144,8 @@ struct ConstantId<'tcx> { #[derive(Clone, Debug, Eq, PartialEq, Hash)] enum ConstantKind { Promoted(usize), - Static, + /// Statics, constants and associated constants + Global, } impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { @@ -1199,7 +1206,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let cid = ConstantId { def_id: def_id, substs: substs, - kind: ConstantKind::Static, + kind: ConstantKind::Global, }; Ok(*self.statics.get(&cid).expect("static should have been cached (rvalue)")) } @@ -1231,7 +1238,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let cid = ConstantId { def_id: def_id, substs: substs, - kind: ConstantKind::Static, + kind: ConstantKind::Global, }; *self.gecx.statics.get(&cid).expect("static should have been cached (lvalue)") }, diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index c0e9249b29250..f9bf5c2d3b853 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -145,7 +145,7 @@ impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { let cid = ConstantId { def_id: def_id, substs: substs, - kind: ConstantKind::Static, + kind: ConstantKind::Global, }; if self.gecx.statics.contains_key(&cid) { return; From cbbf58bbaaafb9cdd9cb837bc3570a8b04bc734a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 12:47:24 +0200 Subject: [PATCH 0329/1096] the statement/terminator has already been computed, don't do it again --- src/interpreter/stepper.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index f9bf5c2d3b853..60eae047f1bf1 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -41,10 +41,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } } - fn statement FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { - let mir = self.fncx.mir(); - let block_data = mir.basic_block_data(self.fncx.frame().next_block); - let stmt = &block_data.statements[self.fncx.frame().stmt]; + fn statement FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { f(Event::Assignment(stmt)); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); @@ -53,13 +50,10 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Ok(()) } - fn terminator FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { + fn terminator FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block self.fncx.frame_mut().stmt = 0; let term = { - let mir = self.fncx.mir(); - let block_data = mir.basic_block_data(self.fncx.frame().next_block); - let terminator = block_data.terminator(); f(Event::Terminator(terminator)); let result = self.fncx.eval_terminator(terminator); self.fncx.maybe_report(result)? @@ -98,7 +92,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_statement(block, stmt); if self.constants.is_empty() { - return self.statement(f); + return self.statement(f, stmt); } else { return self.extract_constants(f); } @@ -115,7 +109,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_terminator(block, terminator); if self.constants.is_empty() { - self.terminator(f) + self.terminator(f, terminator) } else { self.extract_constants(f) } @@ -133,6 +127,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { span: Span, + // FIXME: directly push the new stackframes instead of doing this intermediate caching constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, gecx: &'a mut GlobalEvalContext<'b, 'tcx>, mir: &'a mir::Mir<'tcx>, From 59d858a0b142a718a771bdcc3c1f096e3ae01d5f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 10:52:45 +0200 Subject: [PATCH 0330/1096] refactor away the closures and `Event` enum --- src/interpreter/mod.rs | 12 +--------- src/interpreter/stepper.rs | 46 ++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1942727e47694..b581e860d7848 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -395,17 +395,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn run(&mut self) -> EvalResult<()> { let mut stepper = stepper::Stepper::new(self); - let mut done = false; - while !done { - use self::stepper::Event::*; - stepper.step(|event| match event { - Block(b) => trace!("// {:?}", b), - Assignment(a) => trace!("{:?}", a), - Terminator(t) => trace!("{:?}", t.kind), - Done => done = true, - _ => {}, - })?; - } + while stepper.step()? {} Ok(()) } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 60eae047f1bf1..50aaba1d73f13 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -15,16 +15,6 @@ use syntax::codemap::Span; use std::rc::Rc; use memory::Pointer; -pub enum Event<'a, 'tcx: 'a> { - Block(mir::BasicBlock), - Return, - Call, - Constant, - Assignment(&'a mir::Statement<'tcx>), - Terminator(&'a mir::Terminator<'tcx>), - Done, -} - pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, @@ -41,8 +31,8 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx } } - fn statement FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { - f(Event::Assignment(stmt)); + fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { + trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; let result = self.fncx.eval_assignment(lvalue, rvalue); self.fncx.maybe_report(result)?; @@ -50,30 +40,28 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx Ok(()) } - fn terminator FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { + fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block self.fncx.frame_mut().stmt = 0; let term = { - f(Event::Terminator(terminator)); + trace!("{:?}", terminator.kind); let result = self.fncx.eval_terminator(terminator); self.fncx.maybe_report(result)? }; match term { - TerminatorTarget::Block => f(Event::Block(self.fncx.frame().next_block)), TerminatorTarget::Return => { - f(Event::Return); self.fncx.pop_stack_frame(); }, - TerminatorTarget::Call => f(Event::Call), + TerminatorTarget::Block | + TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block), } Ok(()) } // returns true as long as there are more things to do - pub fn step FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { + pub fn step(&mut self) -> EvalResult { if self.fncx.stack.is_empty() { - f(Event::Done); - return Ok(()); + return Ok(false); } let block = self.fncx.frame().next_block; @@ -92,10 +80,11 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_statement(block, stmt); if self.constants.is_empty() { - return self.statement(f, stmt); + self.statement(stmt)?; } else { - return self.extract_constants(f); + self.extract_constants()?; } + return Ok(true); } let terminator = basic_block.terminator(); @@ -109,19 +98,22 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx mir: &mir, }.visit_terminator(block, terminator); if self.constants.is_empty() { - self.terminator(f, terminator) + self.terminator(terminator)?; } else { - self.extract_constants(f) + self.extract_constants()?; } + Ok(true) } - fn extract_constants FnMut(Event<'f, 'tcx>)>(&mut self, mut f: F) -> EvalResult<()> { + fn extract_constants(&mut self) -> EvalResult<()> { assert!(!self.constants.is_empty()); for (cid, span, return_ptr, mir) in self.constants.drain(..) { - f(Event::Constant); + trace!("queuing a constant"); self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); } - self.step(f) + // self.step() can't be "done", so it can't return false + assert!(self.step()?); + Ok(()) } } From 225a6a272d6fa9c945e9e55f2070f736844c2781 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 11:16:09 +0200 Subject: [PATCH 0331/1096] we already have the constant's type, no need to recompute from the def_id --- src/interpreter/mod.rs | 5 ++--- src/interpreter/stepper.rs | 9 +++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b581e860d7848..15fa4cdd3b842 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1184,13 +1184,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()), - Constant(mir::Constant { ref literal, .. }) => { + Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal::*; match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { def_id, substs } => { - let item_ty = self.tcx.lookup_item_type(def_id).subst(self.tcx, substs); - if item_ty.ty.is_fn() { + if ty.is_fn() { Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) } else { let cid = ConstantId { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 50aaba1d73f13..587239fdf068d 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -8,7 +8,7 @@ use super::{ }; use error::EvalResult; use rustc::mir::repr as mir; -use rustc::ty::subst::{self, Subst}; +use rustc::ty::subst; use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; @@ -151,9 +151,10 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - let item_ty = self.gecx.tcx.lookup_item_type(def_id).subst(self.gecx.tcx, substs); - if item_ty.ty.is_fn() { - // unimplemented + if constant.ty.is_fn() { + // No need to do anything here, even if function pointers are implemented, + // because the type is the actual function, not the signature of the function. + // Thus we can simply create a zero sized allocation in `evaluate_operand` } else { self.static_item(def_id, substs, constant.span); } From 040a501a68d85179d0fb8c5bf409f8e93ba12aeb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 11:27:02 +0200 Subject: [PATCH 0332/1096] make sure globals that yield function pointers aren't treated like functions --- src/interpreter/mod.rs | 2 +- src/interpreter/stepper.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 15fa4cdd3b842..6c0d58f1c47e7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1189,7 +1189,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match *literal { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { def_id, substs } => { - if ty.is_fn() { + if let ty::TyFnDef(..) = ty.sty { Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) } else { let cid = ConstantId { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 587239fdf068d..db7b129eee601 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -8,7 +8,7 @@ use super::{ }; use error::EvalResult; use rustc::mir::repr as mir; -use rustc::ty::subst; +use rustc::ty::{subst, self}; use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; @@ -151,7 +151,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - if constant.ty.is_fn() { + if let ty::TyFnDef(..) = constant.ty.sty { // No need to do anything here, even if function pointers are implemented, // because the type is the actual function, not the signature of the function. // Thus we can simply create a zero sized allocation in `evaluate_operand` From 05eaa522a5486ba20bb564bf2b37179124d0951c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 11:27:12 +0200 Subject: [PATCH 0333/1096] rename `static_item` to `global_item` --- src/interpreter/stepper.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index db7b129eee601..a113b8f98e813 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -128,7 +128,7 @@ struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { } impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { - fn static_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { + fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { let cid = ConstantId { def_id: def_id, substs: substs, @@ -156,7 +156,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> // because the type is the actual function, not the signature of the function. // Thus we can simply create a zero sized allocation in `evaluate_operand` } else { - self.static_item(def_id, substs, constant.span); + self.global_item(def_id, substs, constant.span); } }, mir::Literal::Promoted { index } => { @@ -183,7 +183,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> if let mir::Lvalue::Static(def_id) = *lvalue { let substs = self.gecx.tcx.mk_substs(subst::Substs::empty()); let span = self.span; - self.static_item(def_id, substs, span); + self.global_item(def_id, substs, span); } } } From 8fec1a7aa776ed0f1373a1531b8665f4e35919f5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 16:01:53 +0200 Subject: [PATCH 0334/1096] merge FnEvalContext into GlobalEvalContext --- src/interpreter/mod.rs | 115 ++++++++++++------------------------- src/interpreter/stepper.rs | 51 ++++++++-------- 2 files changed, 63 insertions(+), 103 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6c0d58f1c47e7..33e7d49bc6dee 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -9,7 +9,7 @@ use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::rc::Rc; use std::{iter, mem}; use syntax::ast; @@ -39,26 +39,9 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// Precomputed statics, constants and promoteds statics: HashMap, Pointer>, -} - -struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> { - gecx: &'a mut GlobalEvalContext<'b, 'tcx>, /// The virtual call stack. - stack: Vec>, -} - -impl<'a, 'b, 'mir, 'tcx> Deref for FnEvalContext<'a, 'b, 'mir, 'tcx> { - type Target = GlobalEvalContext<'b, 'tcx>; - fn deref(&self) -> &Self::Target { - self.gecx - } -} - -impl<'a, 'b, 'mir, 'tcx> DerefMut for FnEvalContext<'a, 'b, 'mir, 'tcx> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.gecx - } + stack: Vec>, } /// A stack frame. @@ -160,20 +143,19 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { .bit_width() .expect("Session::target::uint_type was usize")/8), statics: HashMap::new(), + stack: Vec::new(), } } - fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { + fn call(&mut self, mir: &'a mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { let substs = self.tcx.mk_substs(subst::Substs::empty()); let return_ptr = self.alloc_ret_ptr(mir.return_ty, substs); - let mut nested_fecx = FnEvalContext::new(self); + self.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); + self.frame_mut().return_ptr = return_ptr; - nested_fecx.frame_mut().return_ptr = return_ptr; - - nested_fecx.run()?; + self.run()?; Ok(return_ptr) } @@ -349,15 +331,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { ty.layout(&infcx).unwrap() }) } -} - -impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { - fn new(gecx: &'a mut GlobalEvalContext<'b, 'tcx>) -> Self { - FnEvalContext { - gecx: gecx, - stack: Vec::new(), - } - } #[inline(never)] #[cold] @@ -399,7 +372,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, + fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); @@ -425,7 +398,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { }); let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.memory.allocate(size) }).collect(); @@ -459,7 +432,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self - .type_layout(self.lvalue_ty(discr)) + .type_layout(self.lvalue_ty(discr), self.substs()) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; @@ -512,7 +485,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let ret = return_ptr.unwrap(); self.call_intrinsic(&name, substs, args, ret, size)? } @@ -523,7 +496,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Abi::C => { match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? } ty::FnDiverging => unimplemented!(), @@ -553,7 +526,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let last_arg = args.last().unwrap(); let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty); + let last_layout = self.type_layout(last_ty, self.substs()); match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -638,7 +611,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // Filling drop. // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.memory.drop_fill(ptr, size)?; Ok(()) @@ -646,7 +619,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty); + let adt_layout = self.type_layout(adt_ty, self.substs()); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, .. } => { @@ -699,7 +672,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -713,7 +686,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty, self.substs()); let src = self.memory.read_ptr(args[0])?; let dest = self.memory.read_ptr(args[1])?; let count = self.memory.read_isize(args[2])?; @@ -729,7 +702,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty); + let arg_size = self.type_size(arg_ty, self.substs()); self.memory.drop_fill(args[0], arg_size)?; } @@ -748,7 +721,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -760,7 +733,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty) as isize; + let pointee_size = self.type_size(pointee_ty, self.substs()) as isize; let ptr_arg = args[0]; let offset = self.memory.read_isize(args[1])?; @@ -781,7 +754,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); @@ -790,20 +763,20 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty) as u64; + let size = self.type_size(ty, self.substs()) as u64; self.memory.write_uint(dest, size, dest_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; + let size = self.type_size(ty, self.substs()) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; + let elem_size = self.type_size(elem_ty, self.substs()) as u64; let ptr_size = self.memory.pointer_size as isize; let n = self.memory.read_usize(args[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, dest_size)?; @@ -911,7 +884,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty); + let dest_layout = self.type_layout(dest_ty, self.substs()); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -951,7 +924,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty, self.substs()) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; @@ -1029,7 +1002,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Repeat(ref operand, _) => { let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty, self.substs()), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; @@ -1070,7 +1043,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } Box(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr)?; } @@ -1164,7 +1137,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { - let layout = self.type_layout(ty); + let layout = self.type_layout(ty, self.substs()); use rustc::ty::layout::Layout::*; match *layout { @@ -1229,13 +1202,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - *self.gecx.statics.get(&cid).expect("static should have been cached (lvalue)") + *self.statics.get(&cid).expect("static should have been cached (lvalue)") }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty); + let base_layout = self.type_layout(base_ty, self.substs()); use rustc::mir::repr::ProjectionElem::*; match proj.elem { @@ -1296,7 +1269,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Index(ref operand) => { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty), + ty::TySlice(elem_ty) => self.type_size(elem_ty, self.substs()), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; @@ -1313,19 +1286,15 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx)) + self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx), self.substs()) } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().operand_ty(self.tcx, operand)) - } - - fn monomorphize(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.gecx.monomorphize(ty, self.substs()) + self.monomorphize(self.mir().operand_ty(self.tcx, operand), self.substs()) } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { self.memory.drop_fill(src, size)?; @@ -1333,14 +1302,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn type_size(&self, ty: Ty<'tcx>) -> usize { - self.gecx.type_size(ty, self.substs()) - } - - fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { - self.gecx.type_layout(ty, self.substs()) - } - pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { use syntax::ast::{IntTy, UintTy}; let val = match (self.memory.pointer_size, &ty.sty) { @@ -1380,7 +1341,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(val) } - fn frame(&self) -> &Frame<'mir, 'tcx> { + fn frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } @@ -1389,11 +1350,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { frame.mir.basic_block_data(frame.next_block) } - fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { + fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn mir(&self) -> CachedMir<'mir, 'tcx> { + fn mir(&self) -> CachedMir<'a, 'tcx> { self.frame().mir.clone() } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index a113b8f98e813..836df7a8bf9ef 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -1,5 +1,4 @@ use super::{ - FnEvalContext, CachedMir, TerminatorTarget, ConstantId, @@ -15,18 +14,18 @@ use syntax::codemap::Span; use std::rc::Rc; use memory::Pointer; -pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ - fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, +pub struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ + gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, // a cache of the constants to be computed before the next statement/terminator // this is an optimization, so we don't have to allocate a new vector for every statement - constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, + constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'a, 'tcx>)>, } -impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { - pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { +impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { + pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { Stepper { - fncx: fncx, + gecx: gecx, constants: Vec::new(), } } @@ -34,48 +33,48 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let result = self.fncx.eval_assignment(lvalue, rvalue); - self.fncx.maybe_report(result)?; - self.fncx.frame_mut().stmt += 1; + let result = self.gecx.eval_assignment(lvalue, rvalue); + self.gecx.maybe_report(result)?; + self.gecx.frame_mut().stmt += 1; Ok(()) } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block - self.fncx.frame_mut().stmt = 0; + self.gecx.frame_mut().stmt = 0; let term = { trace!("{:?}", terminator.kind); - let result = self.fncx.eval_terminator(terminator); - self.fncx.maybe_report(result)? + let result = self.gecx.eval_terminator(terminator); + self.gecx.maybe_report(result)? }; match term { TerminatorTarget::Return => { - self.fncx.pop_stack_frame(); + self.gecx.pop_stack_frame(); }, TerminatorTarget::Block | - TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block), + TerminatorTarget::Call => trace!("// {:?}", self.gecx.frame().next_block), } Ok(()) } // returns true as long as there are more things to do pub fn step(&mut self) -> EvalResult { - if self.fncx.stack.is_empty() { + if self.gecx.stack.is_empty() { return Ok(false); } - let block = self.fncx.frame().next_block; - let stmt = self.fncx.frame().stmt; - let mir = self.fncx.mir(); + let block = self.gecx.frame().next_block; + let stmt = self.gecx.frame().stmt; + let mir = self.gecx.mir(); let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { assert!(self.constants.is_empty()); ConstantExtractor { span: stmt.span, - substs: self.fncx.substs(), - def_id: self.fncx.frame().def_id, - gecx: self.fncx.gecx, + substs: self.gecx.substs(), + def_id: self.gecx.frame().def_id, + gecx: self.gecx, constants: &mut self.constants, mir: &mir, }.visit_statement(block, stmt); @@ -91,9 +90,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx assert!(self.constants.is_empty()); ConstantExtractor { span: terminator.span, - substs: self.fncx.substs(), - def_id: self.fncx.frame().def_id, - gecx: self.fncx.gecx, + substs: self.gecx.substs(), + def_id: self.gecx.frame().def_id, + gecx: self.gecx, constants: &mut self.constants, mir: &mir, }.visit_terminator(block, terminator); @@ -109,7 +108,7 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx assert!(!self.constants.is_empty()); for (cid, span, return_ptr, mir) in self.constants.drain(..) { trace!("queuing a constant"); - self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); + self.gecx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); } // self.step() can't be "done", so it can't return false assert!(self.step()?); From ba9e25b2eb932e87d02aedefae867f5f768a66cc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 16:08:34 +0200 Subject: [PATCH 0335/1096] No more terminators --- src/interpreter/mod.rs | 37 +++++++++---------------------------- src/interpreter/stepper.rs | 17 +++++------------ 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 33e7d49bc6dee..1e65bc0aeaec5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -99,18 +99,6 @@ enum CachedMir<'mir, 'tcx: 'mir> { Owned(Rc>) } -/// Represents the action to be taken in the main loop as a result of executing a terminator. -enum TerminatorTarget { - /// Make a local jump to the next block - Block, - - /// Start executing from the new current frame. (For function calls.) - Call, - - /// Stop executing the current frame and resume the previous frame. - Return, -} - #[derive(Clone, Debug, Eq, PartialEq, Hash)] /// Uniquely identifies a specific constant or static struct ConstantId<'tcx> { @@ -412,21 +400,19 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) - -> EvalResult { + -> EvalResult<()> { use rustc::mir::repr::TerminatorKind::*; - let target = match terminator.kind { - Return => TerminatorTarget::Return, + match terminator.kind { + Return => self.pop_stack_frame(), Goto { target } => { self.frame_mut().next_block = target; - TerminatorTarget::Block }, If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; self.frame_mut().next_block = if cond_val { then_target } else { else_target }; - TerminatorTarget::Block } SwitchInt { ref discr, ref values, ref targets, .. } => { @@ -450,7 +436,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } self.frame_mut().next_block = target_block; - TerminatorTarget::Block } Switch { ref discr, ref targets, adt_def } => { @@ -463,7 +448,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { match matching { Some(i) => { self.frame_mut().next_block = targets[i]; - TerminatorTarget::Block }, None => return Err(EvalError::InvalidDiscriminant), } @@ -549,8 +533,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { let dest = self.frame().locals[i]; self.move_(src, dest, src_ty)?; } - - TerminatorTarget::Call } abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), @@ -566,13 +548,12 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { let ty = self.lvalue_ty(value); self.drop(ptr, ty)?; self.frame_mut().next_block = target; - TerminatorTarget::Block } Resume => unimplemented!(), - }; + } - Ok(target) + Ok(()) } fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { @@ -662,7 +643,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize - ) -> EvalResult { + ) -> EvalResult<()> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); @@ -799,7 +780,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. - Ok(TerminatorTarget::Call) + Ok(()) } fn call_c_abi( @@ -808,7 +789,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize, - ) -> EvalResult { + ) -> EvalResult<()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { @@ -861,7 +842,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. - Ok(TerminatorTarget::Call) + Ok(()) } fn assign_fields>( diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 836df7a8bf9ef..38490bbc26b2d 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -1,6 +1,5 @@ use super::{ CachedMir, - TerminatorTarget, ConstantId, GlobalEvalContext, ConstantKind, @@ -42,17 +41,11 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block self.gecx.frame_mut().stmt = 0; - let term = { - trace!("{:?}", terminator.kind); - let result = self.gecx.eval_terminator(terminator); - self.gecx.maybe_report(result)? - }; - match term { - TerminatorTarget::Return => { - self.gecx.pop_stack_frame(); - }, - TerminatorTarget::Block | - TerminatorTarget::Call => trace!("// {:?}", self.gecx.frame().next_block), + trace!("{:?}", terminator.kind); + let result = self.gecx.eval_terminator(terminator); + self.gecx.maybe_report(result)?; + if !self.gecx.stack.is_empty() { + trace!("// {:?}", self.gecx.frame().next_block); } Ok(()) } From fc935c10f86ec7414f6b7c9466c6a3b8cc4625ef Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 16:13:42 +0200 Subject: [PATCH 0336/1096] print errors in one central location --- src/interpreter/mod.rs | 14 ++------------ src/interpreter/stepper.rs | 6 ++---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1e65bc0aeaec5..8ac86e3ad1349 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -322,7 +322,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { #[inline(never)] #[cold] - fn report(&self, e: &EvalError) { + fn report(&self, e: EvalError) { let stmt = self.frame().stmt; let block = self.basic_block(); let span = if stmt < block.statements.len() { @@ -347,13 +347,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { err.emit(); } - fn maybe_report(&self, r: EvalResult) -> EvalResult { - if let Err(ref e) = r { - self.report(e); - } - r - } - fn run(&mut self) -> EvalResult<()> { let mut stepper = stepper::Stepper::new(self); while stepper.step()? {} @@ -1435,10 +1428,7 @@ pub fn interpret_start_points<'a, 'tcx>( gecx.memory.dump(return_ptr.alloc_id); }, Ok(None) => warn!("diverging function returned"), - Err(_e) => { - // TODO(solson): Detect whether the error was already reported or not. - // tcx.sess.err(&e.to_string()); - } + Err(e) => gecx.report(e), } } } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 38490bbc26b2d..c73a46c844926 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -32,8 +32,7 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let result = self.gecx.eval_assignment(lvalue, rvalue); - self.gecx.maybe_report(result)?; + self.gecx.eval_assignment(lvalue, rvalue)?; self.gecx.frame_mut().stmt += 1; Ok(()) } @@ -42,8 +41,7 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { // after a terminator we go to a new block self.gecx.frame_mut().stmt = 0; trace!("{:?}", terminator.kind); - let result = self.gecx.eval_terminator(terminator); - self.gecx.maybe_report(result)?; + self.gecx.eval_terminator(terminator)?; if !self.gecx.stack.is_empty() { trace!("// {:?}", self.gecx.frame().next_block); } From 2dbd82d29667480600846ec62bb0782056330c0e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 16:49:40 +0200 Subject: [PATCH 0337/1096] inline the `call` method into `interpret_start_points` --- src/interpreter/mod.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8ac86e3ad1349..e70fe5e93f334 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -135,18 +135,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } } - fn call(&mut self, mir: &'a mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { - let substs = self.tcx.mk_substs(subst::Substs::empty()); - let return_ptr = self.alloc_ret_ptr(mir.return_ty, substs); - - self.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - - self.frame_mut().return_ptr = return_ptr; - - self.run()?; - Ok(return_ptr) - } - fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { @@ -1423,12 +1411,18 @@ pub fn interpret_start_points<'a, 'tcx>( debug!("Interpreting: {}", item.name); let mut gecx = GlobalEvalContext::new(tcx, mir_map); - match gecx.call(mir, tcx.map.local_def_id(id)) { - Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) { - gecx.memory.dump(return_ptr.alloc_id); + let substs = tcx.mk_substs(subst::Substs::empty()); + let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs); + + gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); + + match (gecx.run(), return_ptr) { + (Ok(()), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { + gecx.memory.dump(ptr.alloc_id); }, - Ok(None) => warn!("diverging function returned"), - Err(e) => gecx.report(e), + (Ok(()), None) => warn!("diverging function returned"), + // FIXME: diverging functions can end up here in some future miri + (Err(e), _) => gecx.report(e), } } } From 336206cec24b9e4bc38445f490ee4d32c9c815a1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 17:23:58 +0200 Subject: [PATCH 0338/1096] the `type_size` method's `substs` argument allows computing the locals before pushing the stack frame --- src/interpreter/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e70fe5e93f334..a7a57a7960170 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -353,11 +353,16 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { ::log_settings::settings().indentation += 1; + let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + let size = self.type_size(ty, substs); + self.memory.allocate(size) + }).collect(); + self.stack.push(Frame { mir: mir.clone(), next_block: mir::START_BLOCK, return_ptr: return_ptr, - locals: Vec::new(), + locals: locals, var_offset: num_args, temp_offset: num_args + num_vars, span: span, @@ -365,13 +370,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { substs: substs, stmt: 0, }); - - let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty, self.substs()); - self.memory.allocate(size) - }).collect(); - - self.frame_mut().locals = locals; } fn pop_stack_frame(&mut self) { From 8c3a066d8d6fec4720c171130816c97ed209ce95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 17:24:42 +0200 Subject: [PATCH 0339/1096] get rid of the constants cache in the stepper this is possible due to the removal of `FnEvalContext` --- src/interpreter/stepper.rs | 51 ++++++++++++++------------------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index c73a46c844926..47ee9874ad668 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -11,21 +11,15 @@ use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -use memory::Pointer; pub struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, - - // a cache of the constants to be computed before the next statement/terminator - // this is an optimization, so we don't have to allocate a new vector for every statement - constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'a, 'tcx>)>, } impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { Stepper { gecx: gecx, - constants: Vec::new(), } } @@ -60,64 +54,57 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { - assert!(self.constants.is_empty()); + let current_stack = self.gecx.stack.len(); ConstantExtractor { span: stmt.span, substs: self.gecx.substs(), def_id: self.gecx.frame().def_id, gecx: self.gecx, - constants: &mut self.constants, mir: &mir, }.visit_statement(block, stmt); - if self.constants.is_empty() { + if current_stack == self.gecx.stack.len() { self.statement(stmt)?; } else { - self.extract_constants()?; + // ConstantExtractor added some new frames for statics/constants/promoteds + // self.step() can't be "done", so it can't return false + assert!(self.step()?); } return Ok(true); } let terminator = basic_block.terminator(); - assert!(self.constants.is_empty()); + let current_stack = self.gecx.stack.len(); ConstantExtractor { span: terminator.span, substs: self.gecx.substs(), def_id: self.gecx.frame().def_id, gecx: self.gecx, - constants: &mut self.constants, mir: &mir, }.visit_terminator(block, terminator); - if self.constants.is_empty() { + if current_stack == self.gecx.stack.len() { self.terminator(terminator)?; } else { - self.extract_constants()?; + // ConstantExtractor added some new frames for statics/constants/promoteds + // self.step() can't be "done", so it can't return false + assert!(self.step()?); } Ok(true) } - - fn extract_constants(&mut self) -> EvalResult<()> { - assert!(!self.constants.is_empty()); - for (cid, span, return_ptr, mir) in self.constants.drain(..) { - trace!("queuing a constant"); - self.gecx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); - } - // self.step() can't be "done", so it can't return false - assert!(self.step()?); - Ok(()) - } } -struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { +// WARNING: make sure that any methods implemented on this type don't ever access gecx.stack +// this includes any method that might access the stack +// basically don't call anything other than `load_mir`, `alloc_ret_ptr`, `push_stack_frame` +// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons +struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, - // FIXME: directly push the new stackframes instead of doing this intermediate caching - constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, gecx: &'a mut GlobalEvalContext<'b, 'tcx>, mir: &'a mir::Mir<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, } -impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { +impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { let cid = ConstantId { def_id: def_id, @@ -130,11 +117,11 @@ impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { let mir = self.gecx.load_mir(def_id); let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); self.gecx.statics.insert(cid.clone(), ptr); - self.constants.push((cid, span, ptr, mir)); + self.gecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)); } } -impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { self.super_constant(constant); match constant.literal { @@ -163,7 +150,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); self.gecx.statics.insert(cid.clone(), return_ptr); - self.constants.push((cid, constant.span, return_ptr, mir)); + self.gecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr)); } } } From 3b804942fdcc8858644218a570e034062021c7a4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 12:34:15 +0200 Subject: [PATCH 0340/1096] simplify the stepper interface --- src/interpreter/mod.rs | 26 ++++++++++++++------------ src/interpreter/stepper.rs | 10 +++++++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7a57a7960170..2673f56090326 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -24,7 +24,7 @@ use std::collections::HashMap; mod stepper; -struct GlobalEvalContext<'a, 'tcx: 'a> { +pub struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -335,12 +335,6 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { err.emit(); } - fn run(&mut self) -> EvalResult<()> { - let mut stepper = stepper::Stepper::new(self); - while stepper.step()? {} - Ok(()) - } - fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { @@ -1414,14 +1408,22 @@ pub fn interpret_start_points<'a, 'tcx>( gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); - match (gecx.run(), return_ptr) { - (Ok(()), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { + loop { match (stepper::step(&mut gecx), return_ptr) { + (Ok(true), _) => {}, + (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { gecx.memory.dump(ptr.alloc_id); + break; + }, + (Ok(false), None) => { + warn!("diverging function returned"); + break; }, - (Ok(()), None) => warn!("diverging function returned"), // FIXME: diverging functions can end up here in some future miri - (Err(e), _) => gecx.report(e), - } + (Err(e), _) => { + gecx.report(e); + break; + }, + } } } } } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 47ee9874ad668..10145bdce2718 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -12,12 +12,16 @@ use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -pub struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ +struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, } +pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult { + Stepper::new(gecx).step() +} + impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { - pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { + fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { Stepper { gecx: gecx, } @@ -43,7 +47,7 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { } // returns true as long as there are more things to do - pub fn step(&mut self) -> EvalResult { + fn step(&mut self) -> EvalResult { if self.gecx.stack.is_empty() { return Ok(false); } From b3c1713b89cacc7c86e7cf028f2206b309a6a152 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 13:01:51 +0200 Subject: [PATCH 0341/1096] expose a minimal API and use it in the binary --- src/bin/miri.rs | 85 +++++++++++++++++++++++++++- src/interpreter/mod.rs | 111 +++++++++---------------------------- src/interpreter/stepper.rs | 10 +--- src/lib.rs | 16 +++++- 4 files changed, 127 insertions(+), 95 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 34a8c1b2be879..cec244a5c745a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -7,11 +7,21 @@ extern crate rustc; extern crate rustc_driver; extern crate env_logger; extern crate log_settings; -extern crate log; +extern crate syntax; +#[macro_use] extern crate log; -use miri::interpreter; +use miri::{ + GlobalEvalContext, + CachedMir, + step, + EvalError, + Frame, +}; use rustc::session::Session; use rustc_driver::{driver, CompilerCalls}; +use rustc::ty::{TyCtxt, subst}; +use rustc::mir::mir_map::MirMap; +use rustc::hir::def_id::DefId; struct MiriCompilerCalls; @@ -25,13 +35,82 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); - interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); }); control } } + + +fn interpret_start_points<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir_map: &MirMap<'tcx>, +) { + let initial_indentation = ::log_settings::settings().indentation; + for (&id, mir) in &mir_map.map { + for attr in tcx.map.attrs(id) { + use syntax::attr::AttrMetaMethods; + if attr.check_name("miri_run") { + let item = tcx.map.expect_item(id); + + ::log_settings::settings().indentation = initial_indentation; + + debug!("Interpreting: {}", item.name); + + let mut gecx = GlobalEvalContext::new(tcx, mir_map); + let substs = tcx.mk_substs(subst::Substs::empty()); + let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs); + + gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); + + loop { match (step(&mut gecx), return_ptr) { + (Ok(true), _) => {}, + (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { + gecx.memory().dump(ptr.alloc_id); + break; + }, + (Ok(false), None) => { + warn!("diverging function returned"); + break; + }, + // FIXME: diverging functions can end up here in some future miri + (Err(e), _) => { + report(tcx, &gecx, e); + break; + }, + } } + } + } + } +} + +fn report(tcx: TyCtxt, gecx: &GlobalEvalContext, e: EvalError) { + let frame = gecx.stack().last().expect("stackframe was empty"); + let block = frame.mir.basic_block_data(frame.next_block); + let span = if frame.stmt < block.statements.len() { + block.statements[frame.stmt].span + } else { + block.terminator().span + }; + let mut err = tcx.sess.struct_span_err(span, &e.to_string()); + for &Frame { def_id, substs, span, .. } in gecx.stack().iter().rev() { + // FIXME(solson): Find a way to do this without this Display impl hack. + use rustc::util::ppaux; + use std::fmt; + struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); + impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], + |tcx| tcx.lookup_item_type(self.0).generics) + } + } + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + } + err.emit(); +} + #[miri_run] fn main() { init_logger(); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2673f56090326..3b08a0ff62dec 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -24,6 +24,10 @@ use std::collections::HashMap; mod stepper; +pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult { + stepper::Stepper::new(gecx).step() +} + pub struct GlobalEvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -45,38 +49,38 @@ pub struct GlobalEvalContext<'a, 'tcx: 'a> { } /// A stack frame. -struct Frame<'a, 'tcx: 'a> { +pub struct Frame<'a, 'tcx: 'a> { /// The def_id of the current function - def_id: DefId, + pub def_id: DefId, /// The span of the call site - span: codemap::Span, + pub span: codemap::Span, /// type substitutions for the current function invocation - substs: &'tcx Substs<'tcx>, + pub substs: &'tcx Substs<'tcx>, /// The MIR for the function called on this frame. - mir: CachedMir<'a, 'tcx>, + pub mir: CachedMir<'a, 'tcx>, /// The block that is currently executed (or will be executed after the above call stacks return) - next_block: mir::BasicBlock, + pub next_block: mir::BasicBlock, /// A pointer for writing the return value of the current call if it's not a diverging call. - return_ptr: Option, + pub return_ptr: Option, /// The list of locals for the current function, stored in order as /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` /// and the temporaries at `self.temp_offset`. - locals: Vec, + pub locals: Vec, /// The offset of the first variable in `self.locals`. - var_offset: usize, + pub var_offset: usize, /// The offset of the first temporary in `self.locals`. - temp_offset: usize, + pub temp_offset: usize, /// The index of the currently evaluated statment - stmt: usize, + pub stmt: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -94,7 +98,7 @@ enum LvalueExtra { } #[derive(Clone)] -enum CachedMir<'mir, 'tcx: 'mir> { +pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), Owned(Rc>) } @@ -120,7 +124,7 @@ enum ConstantKind { } impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { - fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { GlobalEvalContext { tcx: tcx, mir_map: mir_map, @@ -135,7 +139,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } } - fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { let size = self.type_size(ty, substs); @@ -145,6 +149,14 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } } + pub fn memory(&self) -> &Memory { + &self.memory + } + + pub fn stack(&self) -> &[Frame] { + &self.stack + } + // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { use rustc::middle::const_val::ConstVal::*; @@ -308,34 +320,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { }) } - #[inline(never)] - #[cold] - fn report(&self, e: EvalError) { - let stmt = self.frame().stmt; - let block = self.basic_block(); - let span = if stmt < block.statements.len() { - block.statements[stmt].span - } else { - block.terminator().span - }; - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() { - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(self.0).generics) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); - } - err.emit(); - } - - fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, + pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); @@ -1387,48 +1372,6 @@ pub fn get_impl_method<'a, 'tcx>( } } -pub fn interpret_start_points<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir_map: &MirMap<'tcx>, -) { - let initial_indentation = ::log_settings::settings().indentation; - for (&id, mir) in &mir_map.map { - for attr in tcx.map.attrs(id) { - use syntax::attr::AttrMetaMethods; - if attr.check_name("miri_run") { - let item = tcx.map.expect_item(id); - - ::log_settings::settings().indentation = initial_indentation; - - debug!("Interpreting: {}", item.name); - - let mut gecx = GlobalEvalContext::new(tcx, mir_map); - let substs = tcx.mk_substs(subst::Substs::empty()); - let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs); - - gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); - - loop { match (stepper::step(&mut gecx), return_ptr) { - (Ok(true), _) => {}, - (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { - gecx.memory.dump(ptr.alloc_id); - break; - }, - (Ok(false), None) => { - warn!("diverging function returned"); - break; - }, - // FIXME: diverging functions can end up here in some future miri - (Err(e), _) => { - gecx.report(e); - break; - }, - } } - } - } - } -} - // TODO(solson): Upstream these methods into rustc::ty::layout. trait IntegerExt { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 10145bdce2718..942c913ccf984 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -12,16 +12,12 @@ use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ +pub(super) struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, } -pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult { - Stepper::new(gecx).step() -} - impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { - fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { + pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { Stepper { gecx: gecx, } @@ -47,7 +43,7 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { } // returns true as long as there are more things to do - fn step(&mut self) -> EvalResult { + pub(super) fn step(&mut self) -> EvalResult { if self.gecx.stack.is_empty() { return Ok(false); } diff --git a/src/lib.rs b/src/lib.rs index 4e06a3ce38d56..c5d74993dcc69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,20 @@ extern crate log_settings; extern crate byteorder; mod error; -pub mod interpreter; +mod interpreter; mod memory; mod primval; + +pub use error::{ + EvalError, + EvalResult, +}; + +pub use interpreter::{ + GlobalEvalContext, + step, + Frame, + CachedMir, +}; + +pub use memory::Memory; From 6af821f20205e449ab022d04e669c8063b6b9cb4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 16:20:17 +0200 Subject: [PATCH 0342/1096] rename GlobalEvalContext to EvalContext --- src/bin/miri.rs | 6 +++--- src/interpreter/mod.rs | 8 ++++---- src/interpreter/stepper.rs | 8 ++++---- src/lib.rs | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index cec244a5c745a..f4e7ea0d7f06a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -11,7 +11,7 @@ extern crate syntax; #[macro_use] extern crate log; use miri::{ - GlobalEvalContext, + EvalContext, CachedMir, step, EvalError, @@ -59,7 +59,7 @@ fn interpret_start_points<'a, 'tcx>( debug!("Interpreting: {}", item.name); - let mut gecx = GlobalEvalContext::new(tcx, mir_map); + let mut gecx = EvalContext::new(tcx, mir_map); let substs = tcx.mk_substs(subst::Substs::empty()); let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs); @@ -86,7 +86,7 @@ fn interpret_start_points<'a, 'tcx>( } } -fn report(tcx: TyCtxt, gecx: &GlobalEvalContext, e: EvalError) { +fn report(tcx: TyCtxt, gecx: &EvalContext, e: EvalError) { let frame = gecx.stack().last().expect("stackframe was empty"); let block = frame.mir.basic_block_data(frame.next_block); let span = if frame.stmt < block.statements.len() { diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3b08a0ff62dec..9fcb2da36b5db 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -24,11 +24,11 @@ use std::collections::HashMap; mod stepper; -pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult { +pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut EvalContext<'a, 'tcx>) -> EvalResult { stepper::Stepper::new(gecx).step() } -pub struct GlobalEvalContext<'a, 'tcx: 'a> { +pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -123,9 +123,9 @@ enum ConstantKind { Global, } -impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { +impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { - GlobalEvalContext { + EvalContext { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 942c913ccf984..7e1b328a4eeb5 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -1,7 +1,7 @@ use super::{ CachedMir, ConstantId, - GlobalEvalContext, + EvalContext, ConstantKind, }; use error::EvalResult; @@ -13,11 +13,11 @@ use syntax::codemap::Span; use std::rc::Rc; pub(super) struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ - gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, + gecx: &'fncx mut EvalContext<'a, 'tcx>, } impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { - pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { + pub(super) fn new(gecx: &'fncx mut EvalContext<'a, 'tcx>) -> Self { Stepper { gecx: gecx, } @@ -98,7 +98,7 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { // The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, - gecx: &'a mut GlobalEvalContext<'b, 'tcx>, + gecx: &'a mut EvalContext<'b, 'tcx>, mir: &'a mir::Mir<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, diff --git a/src/lib.rs b/src/lib.rs index c5d74993dcc69..c3369878f3554 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ pub use error::{ }; pub use interpreter::{ - GlobalEvalContext, + EvalContext, step, Frame, CachedMir, From 4fa328ef5f69989078fa39f24e87d2c8368c5a80 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 16:20:25 +0200 Subject: [PATCH 0343/1096] remove unused method --- src/interpreter/mod.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9fcb2da36b5db..93140f58a9992 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1284,11 +1284,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack.last().expect("no call frames exist") } - fn basic_block(&self) -> &mir::BasicBlockData<'tcx> { - let frame = self.frame(); - frame.mir.basic_block_data(frame.next_block) - } - fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } From 9c8f84caf774f5cb9e60a3c4bc14f6b514503c4d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 16:32:39 +0200 Subject: [PATCH 0344/1096] style nit --- src/bin/miri.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f4e7ea0d7f06a..2948eb029eaa3 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -65,22 +65,24 @@ fn interpret_start_points<'a, 'tcx>( gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); - loop { match (step(&mut gecx), return_ptr) { - (Ok(true), _) => {}, - (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { - gecx.memory().dump(ptr.alloc_id); - break; - }, - (Ok(false), None) => { - warn!("diverging function returned"); - break; - }, - // FIXME: diverging functions can end up here in some future miri - (Err(e), _) => { - report(tcx, &gecx, e); - break; - }, - } } + loop { + match (step(&mut gecx), return_ptr) { + (Ok(true), _) => {}, + (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { + gecx.memory().dump(ptr.alloc_id); + break; + }, + (Ok(false), None) => { + warn!("diverging function returned"); + break; + }, + // FIXME: diverging functions can end up here in some future miri + (Err(e), _) => { + report(tcx, &gecx, e); + break; + }, + } + } } } } From cea2a8ae9eeadd02b3da38d819f95741449dd4e0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Jun 2016 16:56:04 +0200 Subject: [PATCH 0345/1096] adjust lifetimes and bindings to the GlobalEvalContext -> EvalContext rename --- src/bin/miri.rs | 18 +++++----- src/interpreter/mod.rs | 4 +-- src/interpreter/stepper.rs | 74 +++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 2948eb029eaa3..82702ae437880 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -59,17 +59,17 @@ fn interpret_start_points<'a, 'tcx>( debug!("Interpreting: {}", item.name); - let mut gecx = EvalContext::new(tcx, mir_map); + let mut ecx = EvalContext::new(tcx, mir_map); let substs = tcx.mk_substs(subst::Substs::empty()); - let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs); + let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs); - gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); + ecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); loop { - match (step(&mut gecx), return_ptr) { + match (step(&mut ecx), return_ptr) { (Ok(true), _) => {}, (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { - gecx.memory().dump(ptr.alloc_id); + ecx.memory().dump(ptr.alloc_id); break; }, (Ok(false), None) => { @@ -78,7 +78,7 @@ fn interpret_start_points<'a, 'tcx>( }, // FIXME: diverging functions can end up here in some future miri (Err(e), _) => { - report(tcx, &gecx, e); + report(tcx, &ecx, e); break; }, } @@ -88,8 +88,8 @@ fn interpret_start_points<'a, 'tcx>( } } -fn report(tcx: TyCtxt, gecx: &EvalContext, e: EvalError) { - let frame = gecx.stack().last().expect("stackframe was empty"); +fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { + let frame = ecx.stack().last().expect("stackframe was empty"); let block = frame.mir.basic_block_data(frame.next_block); let span = if frame.stmt < block.statements.len() { block.statements[frame.stmt].span @@ -97,7 +97,7 @@ fn report(tcx: TyCtxt, gecx: &EvalContext, e: EvalError) { block.terminator().span }; let mut err = tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { def_id, substs, span, .. } in gecx.stack().iter().rev() { + for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { // FIXME(solson): Find a way to do this without this Display impl hack. use rustc::util::ppaux; use std::fmt; diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 93140f58a9992..6831ab212fb35 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -24,8 +24,8 @@ use std::collections::HashMap; mod stepper; -pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut EvalContext<'a, 'tcx>) -> EvalResult { - stepper::Stepper::new(gecx).step() +pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult { + stepper::Stepper::new(ecx).step() } pub struct EvalContext<'a, 'tcx: 'a> { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 7e1b328a4eeb5..3e4816834a82d 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -12,57 +12,57 @@ use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -pub(super) struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ - gecx: &'fncx mut EvalContext<'a, 'tcx>, +pub(super) struct Stepper<'ecx, 'a: 'ecx, 'tcx: 'a>{ + ecx: &'ecx mut EvalContext<'a, 'tcx>, } -impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { - pub(super) fn new(gecx: &'fncx mut EvalContext<'a, 'tcx>) -> Self { +impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { + pub(super) fn new(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> Self { Stepper { - gecx: gecx, + ecx: ecx, } } fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - self.gecx.eval_assignment(lvalue, rvalue)?; - self.gecx.frame_mut().stmt += 1; + self.ecx.eval_assignment(lvalue, rvalue)?; + self.ecx.frame_mut().stmt += 1; Ok(()) } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block - self.gecx.frame_mut().stmt = 0; + self.ecx.frame_mut().stmt = 0; trace!("{:?}", terminator.kind); - self.gecx.eval_terminator(terminator)?; - if !self.gecx.stack.is_empty() { - trace!("// {:?}", self.gecx.frame().next_block); + self.ecx.eval_terminator(terminator)?; + if !self.ecx.stack.is_empty() { + trace!("// {:?}", self.ecx.frame().next_block); } Ok(()) } // returns true as long as there are more things to do pub(super) fn step(&mut self) -> EvalResult { - if self.gecx.stack.is_empty() { + if self.ecx.stack.is_empty() { return Ok(false); } - let block = self.gecx.frame().next_block; - let stmt = self.gecx.frame().stmt; - let mir = self.gecx.mir(); + let block = self.ecx.frame().next_block; + let stmt = self.ecx.frame().stmt; + let mir = self.ecx.mir(); let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { - let current_stack = self.gecx.stack.len(); + let current_stack = self.ecx.stack.len(); ConstantExtractor { span: stmt.span, - substs: self.gecx.substs(), - def_id: self.gecx.frame().def_id, - gecx: self.gecx, + substs: self.ecx.substs(), + def_id: self.ecx.frame().def_id, + ecx: self.ecx, mir: &mir, }.visit_statement(block, stmt); - if current_stack == self.gecx.stack.len() { + if current_stack == self.ecx.stack.len() { self.statement(stmt)?; } else { // ConstantExtractor added some new frames for statics/constants/promoteds @@ -73,15 +73,15 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { } let terminator = basic_block.terminator(); - let current_stack = self.gecx.stack.len(); + let current_stack = self.ecx.stack.len(); ConstantExtractor { span: terminator.span, - substs: self.gecx.substs(), - def_id: self.gecx.frame().def_id, - gecx: self.gecx, + substs: self.ecx.substs(), + def_id: self.ecx.frame().def_id, + ecx: self.ecx, mir: &mir, }.visit_terminator(block, terminator); - if current_stack == self.gecx.stack.len() { + if current_stack == self.ecx.stack.len() { self.terminator(terminator)?; } else { // ConstantExtractor added some new frames for statics/constants/promoteds @@ -92,13 +92,13 @@ impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { } } -// WARNING: make sure that any methods implemented on this type don't ever access gecx.stack +// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack // this includes any method that might access the stack // basically don't call anything other than `load_mir`, `alloc_ret_ptr`, `push_stack_frame` // The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, - gecx: &'a mut EvalContext<'b, 'tcx>, + ecx: &'a mut EvalContext<'b, 'tcx>, mir: &'a mir::Mir<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, @@ -111,13 +111,13 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - if self.gecx.statics.contains_key(&cid) { + if self.ecx.statics.contains_key(&cid) { return; } - let mir = self.gecx.load_mir(def_id); - let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); - self.gecx.statics.insert(cid.clone(), ptr); - self.gecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)); + let mir = self.ecx.load_mir(def_id); + let ptr = self.ecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); + self.ecx.statics.insert(cid.clone(), ptr); + self.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)); } } @@ -142,15 +142,15 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { substs: self.substs, kind: ConstantKind::Promoted(index), }; - if self.gecx.statics.contains_key(&cid) { + if self.ecx.statics.contains_key(&cid) { return; } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; - let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); + let return_ptr = self.ecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); - self.gecx.statics.insert(cid.clone(), return_ptr); - self.gecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr)); + self.ecx.statics.insert(cid.clone(), return_ptr); + self.ecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr)); } } } @@ -158,7 +158,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { self.super_lvalue(lvalue, context); if let mir::Lvalue::Static(def_id) = *lvalue { - let substs = self.gecx.tcx.mk_substs(subst::Substs::empty()); + let substs = self.ecx.tcx.mk_substs(subst::Substs::empty()); let span = self.span; self.global_item(def_id, substs, span); } From 9780729104a257958a734e86501bee45a10c627e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Jun 2016 10:45:06 +0200 Subject: [PATCH 0346/1096] we already have the constant's type, no need to recompute from the def_id --- src/interpreter/stepper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 3e4816834a82d..1da8c903b79ea 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -129,7 +129,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = constant.ty.sty { - // No need to do anything here, even if function pointers are implemented, + // No need to do anything here, // because the type is the actual function, not the signature of the function. // Thus we can simply create a zero sized allocation in `evaluate_operand` } else { From 67211218f0e3451cdc3fa979c0989ee47a0db048 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 13:31:24 +0200 Subject: [PATCH 0347/1096] display the full path to the function if no MIR is found --- src/interpreter/mod.rs | 3 ++- src/lib.rs | 1 + tests/run-fail/inception.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6831ab212fb35..bbc2d2b5c492d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -282,6 +282,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + use rustc_trans::back::symbol_names::def_id_to_string; match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), None => { @@ -292,7 +293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for {:?}", def_id); + panic!("no mir for `{}`", def_id_to_string(self.tcx, def_id)); }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); diff --git a/src/lib.rs b/src/lib.rs index c3369878f3554..90979bd777ce8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ // From rustc. #[macro_use] extern crate rustc; extern crate rustc_mir; +extern crate rustc_trans; extern crate syntax; #[macro_use] extern crate log; extern crate log_settings; diff --git a/tests/run-fail/inception.rs b/tests/run-fail/inception.rs index f0fb4113f1f76..a98f71502f9f3 100644 --- a/tests/run-fail/inception.rs +++ b/tests/run-fail/inception.rs @@ -1,4 +1,4 @@ -// error-pattern:no mir for DefId +// error-pattern:no mir for `env_logger/ use std::env; use std::process::{Command, Output}; From 384623daa7ca04352fc57cb2f31790961ac27e21 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Jun 2016 13:43:34 +0200 Subject: [PATCH 0348/1096] function pointers --- src/error.rs | 3 + src/interpreter/mod.rs | 192 ++++++++++++++++------------ src/memory.rs | 41 +++++- tests/compile-fail/unimplemented.rs | 2 +- tests/run-pass/function_pointers.rs | 17 +++ tests/run-pass/zst.rs | 17 +++ 6 files changed, 184 insertions(+), 88 deletions(-) create mode 100644 tests/run-pass/function_pointers.rs create mode 100644 tests/run-pass/zst.rs diff --git a/src/error.rs b/src/error.rs index 7d252658ba2d6..a4ae8cbdda25b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,6 +6,7 @@ use memory::Pointer; #[derive(Clone, Debug)] pub enum EvalError { DanglingPointerDeref, + InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, PointerOutOfBounds { @@ -28,6 +29,8 @@ impl Error for EvalError { match *self { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", + EvalError::InvalidFunctionPointer => + "tried to use a pointer as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bbc2d2b5c492d..943cf33a97a3a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -6,7 +6,7 @@ use rustc::traits::{self, ProjectionMode}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; use std::ops::Deref; @@ -14,7 +14,7 @@ use std::rc::Rc; use std::{iter, mem}; use syntax::ast; use syntax::attr; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; @@ -39,7 +39,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { mir_cache: RefCell>>>, /// The virtual memory system. - memory: Memory, + memory: Memory<'tcx>, /// Precomputed statics, constants and promoteds statics: HashMap, Pointer>, @@ -421,81 +421,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { + ty::TyFnPtr(bare_fn_ty) => { + let ptr = self.eval_operand(func)?; + assert_eq!(ptr.offset, 0); + let fn_ptr = self.memory.read_ptr(ptr)?; + let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?; + self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, terminator.span)? + }, ty::TyFnDef(def_id, substs, fn_ty) => { - use syntax::abi::Abi; - match fn_ty.abi { - Abi::RustIntrinsic => { - let name = self.tcx.item_name(def_id).as_str(); - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); - let ret = return_ptr.unwrap(); - self.call_intrinsic(&name, substs, args, ret, size)? - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::C => { - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::Rust | Abi::RustCall => { - // TODO(solson): Adjust the first argument when calling a Fn or - // FnMut closure via FnOnce::call_once. - - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { - self.trait_method(def_id, substs) - } else { - (def_id, substs) - }; - - let mut arg_srcs = Vec::new(); - for arg in args { - let src = self.eval_operand(arg)?; - let src_ty = self.operand_ty(arg); - arg_srcs.push((src, src_ty)); - } - - if fn_ty.abi == Abi::RustCall && !args.is_empty() { - arg_srcs.pop(); - let last_arg = args.last().unwrap(); - let last = self.eval_operand(last_arg)?; - let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty, self.substs()); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); - } - } - ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - - let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, terminator.span, mir, resolved_substs, return_ptr); - - for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { - let dest = self.frame().locals[i]; - self.move_(src, dest, src_ty)?; - } - } - - abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), - } + self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, terminator.span)? } _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), @@ -515,6 +449,93 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + pub fn eval_fn_call( + &mut self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + fn_ty: &'tcx BareFnTy, + return_ptr: Option, + args: &[mir::Operand<'tcx>], + span: Span, + ) -> EvalResult<()> { + use syntax::abi::Abi; + match fn_ty.abi { + Abi::RustIntrinsic => { + let name = self.tcx.item_name(def_id).as_str(); + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty, self.substs()); + let ret = return_ptr.unwrap(); + self.call_intrinsic(&name, substs, args, ret, size) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::C => { + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty, self.substs()); + self.call_c_abi(def_id, args, return_ptr.unwrap(), size) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::Rust | Abi::RustCall => { + // TODO(solson): Adjust the first argument when calling a Fn or + // FnMut closure via FnOnce::call_once. + + // Only trait methods can have a Self parameter. + let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { + self.trait_method(def_id, substs) + } else { + (def_id, substs) + }; + + let mut arg_srcs = Vec::new(); + for arg in args { + let src = self.eval_operand(arg)?; + let src_ty = self.operand_ty(arg); + arg_srcs.push((src, src_ty)); + } + + if fn_ty.abi == Abi::RustCall && !args.is_empty() { + arg_srcs.pop(); + let last_arg = args.last().unwrap(); + let last = self.eval_operand(last_arg)?; + let last_ty = self.operand_ty(last_arg); + let last_layout = self.type_layout(last_ty, self.substs()); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + arg_srcs.push((src, ty)); + } + } + ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + + let mir = self.load_mir(resolved_def_id); + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr); + + for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { + let dest = self.frame().locals[i]; + self.move_(src, dest, src_ty)?; + } + + Ok(()) + } + + abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + } + } + fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); @@ -989,12 +1010,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Cast(kind, ref operand, dest_ty) => { - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); - use rustc::mir::repr::CastKind::*; match kind { Unsize => { + let src = self.eval_operand(operand)?; + let src_ty = self.operand_ty(operand); self.move_(src, dest, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -1010,11 +1030,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Misc => { + let src = self.eval_operand(operand)?; // FIXME(solson): Wrong for almost everything. let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; self.memory.copy(src, dest, size)?; } + ReifyFnPointer => match self.operand_ty(operand).sty { + ty::TyFnDef(def_id, substs, _) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs); + self.memory.write_ptr(dest, fn_ptr)?; + }, + ref other => panic!("reify fn pointer on {:?}", other), + }, + _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), } } @@ -1103,7 +1132,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value { ref value } => Ok(self.const_to_ptr(value)?), Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { - Err(EvalError::Unimplemented("unimplemented: mentions of function items".to_string())) + // function items are zero sized + Ok(self.memory.allocate(0)) } else { let cid = ConstantId { def_id: def_id, diff --git a/src/memory.rs b/src/memory.rs index ab527a47c0bf5..d9999821e1b03 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,6 +3,9 @@ use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, mem, ptr}; +use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; + use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -42,22 +45,37 @@ impl Pointer { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory { +pub struct Memory<'tcx> { alloc_map: HashMap, + functions: HashMap)>, next_id: AllocId, pub pointer_size: usize, } -impl Memory { +impl<'tcx> Memory<'tcx> { // FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.) pub fn new(pointer_size: usize) -> Self { Memory { alloc_map: HashMap::new(), + functions: HashMap::new(), next_id: AllocId(0), pointer_size: pointer_size, } } + // FIXME: never create two pointers to the same def_id + substs combination + // maybe re-use the statics cache of the gecx? + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { + let id = self.next_id; + debug!("creating fn ptr: {}", id); + self.next_id.0 += 1; + self.functions.insert(id, (def_id, substs)); + Pointer { + alloc_id: id, + offset: 0, + } + } + pub fn allocate(&mut self, size: usize) -> Pointer { let alloc = Allocation { bytes: vec![0; size], @@ -125,6 +143,11 @@ impl Memory { self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref) } + pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> { + debug!("reading fn ptr: {}", id); + self.functions.get(&id).map(|&did| did).ok_or(EvalError::InvalidFunctionPointer) + } + /// Print an allocation and all allocations it points to, recursively. pub fn dump(&self, id: AllocId) { let mut allocs_seen = HashSet::new(); @@ -137,12 +160,18 @@ impl Memory { print!("{}", prefix); let mut relocations = vec![]; - let alloc = match self.alloc_map.get(&id) { - Some(a) => a, - None => { + let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { + (Some(a), None) => a, + (None, Some(_)) => { + // FIXME: print function name + println!("function pointer"); + continue; + }, + (None, None) => { println!("(deallocated)"); continue; - } + }, + (Some(_), Some(_)) => unreachable!(), }; for i in 0..alloc.bytes.len() { diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 754d3d9ee7e6d..7752650ade887 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute)] #![allow(dead_code, unused_attributes)] -//error-pattern:unimplemented: mentions of function items +//error-pattern:begin_panic_fmt #[miri_run] diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs new file mode 100644 index 0000000000000..55a6f9fbeac46 --- /dev/null +++ b/tests/run-pass/function_pointers.rs @@ -0,0 +1,17 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +fn f() -> i32 { + 42 +} + +fn return_fn_ptr() -> fn() -> i32 { + f +} + +#[miri_run] +fn call_fn_ptr() -> i32 { + return_fn_ptr()() +} + +fn main() {} diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs new file mode 100644 index 0000000000000..d4f1d4023ba7f --- /dev/null +++ b/tests/run-pass/zst.rs @@ -0,0 +1,17 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +struct A; + +#[miri_run] +fn zst_ret() -> A { + A +} + +#[miri_run] +fn use_zst() -> A { + let a = A; + a +} + +fn main() {} From 781c3a6660669ac989b8f86858c541180b424434 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 11 Jun 2016 12:38:28 -0600 Subject: [PATCH 0349/1096] Update for changes in rustc nightly. --- Cargo.lock | 10 +++++----- src/bin/miri.rs | 8 ++++---- src/interpreter/mod.rs | 36 +++++++++++++++++++++++++++--------- src/interpreter/stepper.rs | 6 +++--- src/lib.rs | 1 + 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5518b1238a27..240a2144807da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -55,7 +55,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -76,7 +76,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -102,7 +102,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 82702ae437880..123428bcad191 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -90,11 +90,11 @@ fn interpret_start_points<'a, 'tcx>( fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { let frame = ecx.stack().last().expect("stackframe was empty"); - let block = frame.mir.basic_block_data(frame.next_block); + let block = &frame.mir.basic_blocks()[frame.next_block]; let span = if frame.stmt < block.statements.len() { - block.statements[frame.stmt].span + block.statements[frame.stmt].source_info.span } else { - block.terminator().span + block.terminator().source_info.span }; let mut err = tcx.sess.struct_span_err(span, &e.to_string()); for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { @@ -105,7 +105,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(self.0).generics) + |tcx| Some(tcx.lookup_item_type(self.0).generics)) } } err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6831ab212fb35..b3de02e55b025 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -8,6 +8,7 @@ use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::DefIdMap; +use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; @@ -118,7 +119,7 @@ struct ConstantId<'tcx> { #[derive(Clone, Debug, Eq, PartialEq, Hash)] enum ConstantKind { - Promoted(usize), + Promoted(mir::Promoted), /// Statics, constants and associated constants Global, } @@ -485,7 +486,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, terminator.span, mir, resolved_substs, return_ptr); + self.push_stack_frame( + def_id, terminator.source_info.span, mir, resolved_substs, + return_ptr + ); for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; @@ -501,14 +505,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - Drop { ref value, target, .. } => { - let ptr = self.eval_lvalue(value)?.to_ptr(); - let ty = self.lvalue_ty(value); + Drop { ref location, target, .. } => { + let ptr = self.eval_lvalue(location)?.to_ptr(); + let ty = self.lvalue_ty(location); self.drop(ptr, ty)?; self.frame_mut().next_block = target; } + Assert { ref cond, expected, ref msg, target, cleanup } => { + let actual_ptr = self.eval_operand(cond)?; + let actual = self.memory.read_bool(actual_ptr)?; + if actual == expected { + self.frame_mut().next_block = target; + } else { + panic!("unimplemented: jump to {:?} and print {:?}", cleanup, msg); + } + } + + DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), + Unreachable => unimplemented!(), } Ok(()) @@ -845,6 +861,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(dest, val)?; } + CheckedBinaryOp(..) => unimplemented!(), + UnaryOp(un_op, ref operand) => { let ptr = self.eval_operand(operand)?; let ty = self.operand_ty(operand); @@ -1018,7 +1036,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - Slice { .. } => unimplemented!(), InlineAsm { .. } => unimplemented!(), } @@ -1130,9 +1147,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = match *lvalue { ReturnPointer => self.frame().return_ptr .expect("ReturnPointer used in a function with no return value"), - Arg(i) => self.frame().locals[i as usize], - Var(i) => self.frame().locals[self.frame().var_offset + i as usize], - Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize], + Arg(i) => self.frame().locals[i.index()], + Var(i) => self.frame().locals[self.frame().var_offset + i.index()], + Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], Static(def_id) => { let substs = self.tcx.mk_substs(subst::Substs::empty()); @@ -1217,6 +1234,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ConstantIndex { .. } => unimplemented!(), + Subslice { .. } => unimplemented!(), } } }; diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 3e4816834a82d..cba76eaf5a3ce 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -51,12 +51,12 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { let block = self.ecx.frame().next_block; let stmt = self.ecx.frame().stmt; let mir = self.ecx.mir(); - let basic_block = mir.basic_block_data(block); + let basic_block = &mir.basic_blocks()[block]; if let Some(ref stmt) = basic_block.statements.get(stmt) { let current_stack = self.ecx.stack.len(); ConstantExtractor { - span: stmt.span, + span: stmt.source_info.span, substs: self.ecx.substs(), def_id: self.ecx.frame().def_id, ecx: self.ecx, @@ -75,7 +75,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { let terminator = basic_block.terminator(); let current_stack = self.ecx.stack.len(); ConstantExtractor { - span: terminator.span, + span: terminator.source_info.span, substs: self.ecx.substs(), def_id: self.ecx.frame().def_id, ecx: self.ecx, diff --git a/src/lib.rs b/src/lib.rs index c3369878f3554..9d2203d115c10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ // From rustc. #[macro_use] extern crate rustc; +extern crate rustc_data_structures; extern crate rustc_mir; extern crate syntax; #[macro_use] extern crate log; From 947e9a5c31fe8884e570849f42df6e9390e2649d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 11 Jun 2016 12:38:50 -0600 Subject: [PATCH 0350/1096] Fix infinite loop when debug trace is disabled. --- src/bin/miri.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 123428bcad191..b42c40031fd30 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -66,21 +66,22 @@ fn interpret_start_points<'a, 'tcx>( ecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); loop { - match (step(&mut ecx), return_ptr) { - (Ok(true), _) => {}, - (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { - ecx.memory().dump(ptr.alloc_id); + match step(&mut ecx) { + Ok(true) => {} + Ok(false) => { + match return_ptr { + Some(ptr) => if log_enabled!(::log::LogLevel::Debug) { + ecx.memory().dump(ptr.alloc_id); + }, + None => warn!("diverging function returned"), + } break; - }, - (Ok(false), None) => { - warn!("diverging function returned"); - break; - }, + } // FIXME: diverging functions can end up here in some future miri - (Err(e), _) => { + Err(e) => { report(tcx, &ecx, e); break; - }, + } } } } From 71188ea2dfd025a9cbb2476d06881c9f70ce0ec8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 11 Jun 2016 13:10:25 -0600 Subject: [PATCH 0351/1096] Remove inception test for now. --- src/bin/miri.rs | 5 +--- tests/compiletest.rs | 1 - tests/run-fail/inception.rs | 50 ------------------------------------- 3 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 tests/run-fail/inception.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index b42c40031fd30..f14eb39439ef6 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,5 +1,4 @@ -#![feature(rustc_private, custom_attribute)] -#![allow(unused_attributes)] +#![feature(rustc_private)] extern crate getopts; extern crate miri; @@ -114,14 +113,12 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { err.emit(); } -#[miri_run] fn main() { init_logger(); let args: Vec = std::env::args().collect(); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); } -#[miri_run] fn init_logger() { const NSPACES: usize = 40; let format = |record: &log::LogRecord| { diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 184e282413242..2fc1e615cff19 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,5 +27,4 @@ fn run_mode(mode: &'static str) { fn compile_test() { run_mode("compile-fail"); run_mode("run-pass"); - run_mode("run-fail"); } diff --git a/tests/run-fail/inception.rs b/tests/run-fail/inception.rs deleted file mode 100644 index f0fb4113f1f76..0000000000000 --- a/tests/run-fail/inception.rs +++ /dev/null @@ -1,50 +0,0 @@ -// error-pattern:no mir for DefId - -use std::env; -use std::process::{Command, Output}; - -fn run_miri(file: &str, sysroot: &str) -> Output { - let path = env::current_dir().unwrap(); - let libpath = path.join("target").join("debug"); - let libpath = libpath.to_str().unwrap(); - let libpath2 = path.join("target").join("debug").join("deps"); - let libpath2 = libpath2.to_str().unwrap(); - let mut args = vec![ - "run".to_string(), "--".to_string(), - "--sysroot".to_string(), sysroot.to_string(), - "-L".to_string(), libpath.to_string(), - "-L".to_string(), libpath2.to_string(), - file.to_string() - ]; - for file in std::fs::read_dir("target/debug/deps").unwrap() { - let file = file.unwrap(); - if file.file_type().unwrap().is_file() { - let path = file.path(); - if let Some(ext) = path.extension() { - if ext == "rlib" { - let name = path.file_stem().unwrap().to_str().unwrap(); - if let Some(dash) = name.rfind('-') { - if name.starts_with("lib") { - args.push("--extern".to_string()); - args.push(format!("{}={}", &name[3..dash], path.to_str().unwrap())); - } - } - } - } - } - } - Command::new("cargo") - .args(&args) - .output() - .unwrap_or_else(|e| panic!("failed to execute process: {}", e)) -} - -fn main() { - let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - let test_run = run_miri("src/bin/miri.rs", &sysroot); - - if test_run.status.code().unwrap_or(-1) != 0 { - println!("{}", String::from_utf8(test_run.stdout).unwrap()); - panic!("{}", String::from_utf8(test_run.stderr).unwrap()); - } -} From 1c58b7c2ed7a1c8f145c9410cb6a4d0e861b220a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 11 Jun 2016 13:10:42 -0600 Subject: [PATCH 0352/1096] Add hacky stub version of CheckedBinaryOp. --- src/interpreter/mod.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b3de02e55b025..37113f3873197 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -861,7 +861,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(dest, val)?; } - CheckedBinaryOp(..) => unimplemented!(), + // FIXME(solson): Factor this out with BinaryOp. + CheckedBinaryOp(bin_op, ref left, ref right) => { + let left_ptr = self.eval_operand(left)?; + let left_ty = self.operand_ty(left); + let left_val = self.read_primval(left_ptr, left_ty)?; + + let right_ptr = self.eval_operand(right)?; + let right_ty = self.operand_ty(right); + let right_val = self.read_primval(right_ptr, right_ty)?; + + let val = primval::binary_op(bin_op, left_val, right_val)?; + self.memory.write_primval(dest, val)?; + + // FIXME(solson): Find the result type size properly. Perhaps refactor out + // Projection calculations so we can do the equivalent of `dest.1` here. + let s = self.type_size(left_ty, self.substs()); + self.memory.write_bool(dest.offset(s as isize), false)?; + } UnaryOp(un_op, ref operand) => { let ptr = self.eval_operand(operand)?; From b1c62195607ba0b4862d1fcbece5385be73d48f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 16:05:49 -0600 Subject: [PATCH 0353/1096] Unset RUST_NEW_ERROR_FORMAT in compiletest. --- tests/compiletest.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 2fc1e615cff19..b64b091bf6ff0 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -19,6 +19,10 @@ fn run_mode(mode: &'static str) { config.mode = cfg_mode; config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target = target.to_owned(); + + // Disable rustc's new error fomatting. It breaks these tests. + std::env::remove_var("RUST_NEW_ERROR_FORMAT"); + compiletest::run_tests(&config); } } From c149595ebbc7393a19b976097f68ae7e9a943c97 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 20:42:08 -0600 Subject: [PATCH 0354/1096] Fix bug in relocation ranges. --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index ab527a47c0bf5..be47e9c9da72a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -357,7 +357,7 @@ impl Memory { -> EvalResult> { let start = ptr.offset.saturating_sub(self.pointer_size - 1); - let end = start + size; + let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } From 23b504c2d14937fc1879e68667615b66c18eb506 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 20:50:17 -0600 Subject: [PATCH 0355/1096] Implement reallocating to a smaller size. --- src/memory.rs | 22 ++++++++++++---------- tests/run-pass/heap.rs | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index be47e9c9da72a..6ef742f600d3e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -81,17 +81,18 @@ impl Memory { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - let alloc = self.get_mut(ptr.alloc_id)?; - let size = alloc.bytes.len(); + let size = self.get_mut(ptr.alloc_id)?.bytes.len(); + if new_size > size { let amount = new_size - size; + let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size))); - // alloc.bytes.truncate(new_size); - // alloc.undef_mask.len = new_size; - // TODO: potentially remove relocations + self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; + let alloc = self.get_mut(ptr.alloc_id)?; + alloc.bytes.truncate(new_size); + alloc.undef_mask.truncate(new_size); } Ok(()) @@ -506,11 +507,12 @@ impl UndefMask { self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } -} -// fn uniform_block(state: bool) -> Block { -// if state { !0 } else { 0 } -// } + fn truncate(&mut self, length: usize) { + self.len = length; + self.blocks.truncate(self.len / BLOCK_SIZE + 1); + } +} fn bit_index(bits: usize) -> (usize, usize) { (bits / BLOCK_SIZE, bits % BLOCK_SIZE) diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index f5aad1601c77d..a7175969efac5 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,6 +11,26 @@ fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } +#[miri_run] +fn allocate_reallocate() { + let mut s = String::new(); + + // 6 byte heap alloc (__rust_allocate) + s.push_str("foobar"); + assert_eq!(s.len(), 6); + assert_eq!(s.capacity(), 6); + + // heap size doubled to 12 (__rust_reallocate) + s.push_str("baz"); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 12); + + // heap size reduced to 9 (__rust_reallocate) + s.shrink_to_fit(); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 9); +} + #[miri_run] fn main() { assert_eq!(*make_box(), (1, 2)); From 25a3be9c7eea77d544f32abb26c059dfb9bb816f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 21:00:23 -0600 Subject: [PATCH 0356/1096] Handle Misc casts slightly more sanely. Still insanely, though. --- src/interpreter/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 37113f3873197..39dc20bb855ef 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1045,8 +1045,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { // FIXME(solson): Wrong for almost everything. - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - self.memory.copy(src, dest, size)?; + let dest_size = self.type_size(dest_ty, self.substs()); + let src_size = self.type_size(src_ty, self.substs()); + if dest_size == src_size { + warn!("performing fishy cast from {:?} to {:?}", src_ty, dest_ty); + self.memory.copy(src, dest, dest_size)?; + } else { + return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); + } } _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), From cf247239e388e60cf907c1bc42af72bdbf2e60e8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 21:16:25 -0600 Subject: [PATCH 0357/1096] Simplify the common case of type_size and type_layout. --- src/interpreter/mod.rs | 74 ++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 39dc20bb855ef..bb3d15f9e182f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); Some(self.memory.allocate(size)) } ty::FnDiverging => None, @@ -307,11 +307,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout(ty, substs).size(&self.tcx.data_layout).bytes() as usize + fn type_size(&self, ty: Ty<'tcx>) -> usize { + self.type_size_with_substs(ty, self.substs()) } - fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize + } + + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { + self.type_layout_with_substs(ty, self.substs()) + } + + fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); @@ -334,7 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation += 1; let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); self.memory.allocate(size) }).collect(); @@ -377,7 +385,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self - .type_layout(self.lvalue_ty(discr), self.substs()) + .type_layout(self.lvalue_ty(discr)) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; @@ -428,7 +436,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ret = return_ptr.unwrap(); self.call_intrinsic(&name, substs, args, ret, size)? } @@ -439,7 +447,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? } ty::FnDiverging => unimplemented!(), @@ -469,7 +477,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let last_arg = args.last().unwrap(); let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty, self.substs()); + let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -566,7 +574,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Filling drop. // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.drop_fill(ptr, size)?; Ok(()) @@ -574,7 +582,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty, self.substs()); + let adt_layout = self.type_layout(adt_ty); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, .. } => { @@ -627,7 +635,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -641,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty, self.substs()); + let elem_size = self.type_size(elem_ty); let src = self.memory.read_ptr(args[0])?; let dest = self.memory.read_ptr(args[1])?; let count = self.memory.read_isize(args[2])?; @@ -657,7 +665,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty, self.substs()); + let arg_size = self.type_size(arg_ty); self.memory.drop_fill(args[0], arg_size)?; } @@ -676,7 +684,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -688,7 +696,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty, self.substs()) as isize; + let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args[0]; let offset = self.memory.read_isize(args[1])?; @@ -709,7 +717,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); @@ -718,20 +726,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty, self.substs()) as u64; + let elem_size = self.type_size(elem_ty) as u64; let ptr_size = self.memory.pointer_size as isize; let n = self.memory.read_usize(args[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, dest_size)?; @@ -810,7 +818,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_int(dest, result, dest_size)?; } - _ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))), + _ => { + return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); + } } // Since we pushed no stack frame, the main loop will act @@ -839,7 +849,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty, self.substs()); + let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -876,7 +886,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Find the result type size properly. Perhaps refactor out // Projection calculations so we can do the equivalent of `dest.1` here. - let s = self.type_size(left_ty, self.substs()); + let s = self.type_size(left_ty); self.memory.write_bool(dest.offset(s as isize), false)?; } @@ -898,7 +908,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty, self.substs()) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; @@ -976,7 +986,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Repeat(ref operand, _) => { let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty, self.substs()), n), + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; @@ -1017,7 +1027,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Box(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr)?; } @@ -1045,8 +1055,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { // FIXME(solson): Wrong for almost everything. - let dest_size = self.type_size(dest_ty, self.substs()); - let src_size = self.type_size(src_ty, self.substs()); + let dest_size = self.type_size(dest_ty); + let src_size = self.type_size(src_ty); if dest_size == src_size { warn!("performing fishy cast from {:?} to {:?}", src_ty, dest_ty); self.memory.copy(src, dest, dest_size)?; @@ -1116,7 +1126,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { - let layout = self.type_layout(ty, self.substs()); + let layout = self.type_layout(ty); use rustc::ty::layout::Layout::*; match *layout { @@ -1187,7 +1197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty, self.substs()); + let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { @@ -1248,7 +1258,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Index(ref operand) => { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty, self.substs()), + ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; @@ -1274,7 +1284,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { self.memory.drop_fill(src, size)?; From f06610f34cbe7be5ef8a818f2ffdd50874b248a9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 21:36:02 -0600 Subject: [PATCH 0358/1096] Add a casting hack to make more tests pass. --- src/interpreter/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bb3d15f9e182f..f77ddae6aaa6e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1055,10 +1055,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { // FIXME(solson): Wrong for almost everything. + warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); let dest_size = self.type_size(dest_ty); let src_size = self.type_size(src_ty); - if dest_size == src_size { - warn!("performing fishy cast from {:?} to {:?}", src_ty, dest_ty); + + // Hack to support fat pointer -> thin pointer casts to keep tests for + // other things passing for now. + let is_fat_ptr_cast = pointee_type(src_ty).map(|ty| { + !self.type_is_sized(ty) + }).unwrap_or(false); + + if dest_size == src_size || is_fat_ptr_cast { self.memory.copy(src, dest, dest_size)?; } else { return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); From 03745482a209714f1e71cce6b4ff1d2e450ac491 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 21:38:33 -0600 Subject: [PATCH 0359/1096] Disable a test that breaks on 32-bit. --- tests/run-pass/sums.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 9b7ddb43b7c78..120c196abe975 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -55,7 +55,8 @@ fn two_nones() -> (Option, Option) { (None, None) } -#[miri_run] +// FIXME(solson): Casts inside PartialEq fails on 32-bit. +#[cfg_attr(target_pointer_width = "64", miri_run)] fn main() { assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); From 82daebc5ce112553c6acb20ccf553fa3b16bb567 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 22:00:53 -0600 Subject: [PATCH 0360/1096] Get the sysroot in a better way (stolen from clippy). --- tests/compiletest.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index b64b091bf6ff0..435ad0f51a271 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,26 +3,32 @@ extern crate compiletest_rs as compiletest; use std::path::PathBuf; fn run_mode(mode: &'static str) { + // Disable rustc's new error fomatting. It breaks these tests. + std::env::remove_var("RUST_NEW_ERROR_FORMAT"); + + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + let sysroot = match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + }; + let sysroot_flag = format!("--sysroot {}", sysroot); + // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; for &target in targets { let mut config = compiletest::default_config(); + config.host_rustcflags = Some(sysroot_flag.clone()); + config.mode = mode.parse().ok().expect("Invalid mode"); + config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); config.rustc_path = "target/debug/miri".into(); - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target); - let path = format!("--sysroot {}", path); - config.target_rustcflags = Some(path.clone()); - config.host_rustcflags = Some(path); - let cfg_mode = mode.parse().ok().expect("Invalid mode"); - - config.mode = cfg_mode; config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target = target.to_owned(); - - // Disable rustc's new error fomatting. It breaks these tests. - std::env::remove_var("RUST_NEW_ERROR_FORMAT"); - + config.target_rustcflags = Some(sysroot_flag.clone()); compiletest::run_tests(&config); } } From 2f2219becbb48e3ae65a3176e353b8e1112ba2e9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 12 Jun 2016 22:26:54 -0600 Subject: [PATCH 0361/1096] Simplify compiletest. --- tests/compiletest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 435ad0f51a271..76b5b5f6e9638 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -23,7 +23,7 @@ fn run_mode(mode: &'static str) { for &target in targets { let mut config = compiletest::default_config(); config.host_rustcflags = Some(sysroot_flag.clone()); - config.mode = mode.parse().ok().expect("Invalid mode"); + config.mode = mode.parse().expect("Invalid mode"); config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); config.rustc_path = "target/debug/miri".into(); config.src_base = PathBuf::from(format!("tests/{}", mode)); From 3aa585e421a1c60626b339079b1aa420c1e3ab1d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 11:24:01 +0200 Subject: [PATCH 0362/1096] Merge remote-tracking branch 'origin/master' into function_pointers2 --- src/interpreter/mod.rs | 88 +++++++++++++++++++++++++++--------------- src/memory.rs | 24 ++++++------ tests/compiletest.rs | 26 +++++++++---- tests/run-pass/heap.rs | 20 ++++++++++ tests/run-pass/sums.rs | 3 +- 5 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 251ebb768e0d6..1b0b416e0e12c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -143,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); Some(self.memory.allocate(size)) } ty::FnDiverging => None, @@ -308,11 +308,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout(ty, substs).size(&self.tcx.data_layout).bytes() as usize + fn type_size(&self, ty: Ty<'tcx>) -> usize { + self.type_size_with_substs(ty, self.substs()) } - fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize + } + + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { + self.type_layout_with_substs(ty, self.substs()) + } + + fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); @@ -335,7 +343,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation += 1; let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty, substs); + let size = self.type_size_with_substs(ty, substs); self.memory.allocate(size) }).collect(); @@ -378,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self - .type_layout(self.lvalue_ty(discr), self.substs()) + .type_layout(self.lvalue_ty(discr)) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; @@ -479,7 +487,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ret = return_ptr.unwrap(); self.call_intrinsic(&name, substs, args, ret, size) } @@ -490,7 +498,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.call_c_abi(def_id, args, return_ptr.unwrap(), size) } ty::FnDiverging => unimplemented!(), @@ -520,7 +528,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let last_arg = args.last().unwrap(); let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty, self.substs()); + let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -587,7 +595,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Filling drop. // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.drop_fill(ptr, size)?; Ok(()) @@ -595,7 +603,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty, self.substs()); + let adt_layout = self.type_layout(adt_ty); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, .. } => { @@ -648,7 +656,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -662,7 +670,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty, self.substs()); + let elem_size = self.type_size(elem_ty); let src = self.memory.read_ptr(args[0])?; let dest = self.memory.read_ptr(args[1])?; let count = self.memory.read_isize(args[2])?; @@ -678,7 +686,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty, self.substs()); + let arg_size = self.type_size(arg_ty); self.memory.drop_fill(args[0], arg_size)?; } @@ -697,7 +705,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -709,7 +717,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty, self.substs()) as isize; + let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args[0]; let offset = self.memory.read_isize(args[1])?; @@ -730,7 +738,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); @@ -739,20 +747,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { - let size = self.type_size(ty, self.substs()) as u64; + let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty, self.substs()) as u64; + let elem_size = self.type_size(elem_ty) as u64; let ptr_size = self.memory.pointer_size as isize; let n = self.memory.read_usize(args[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, dest_size)?; @@ -831,7 +839,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_int(dest, result, dest_size)?; } - _ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))), + _ => { + return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); + } } // Since we pushed no stack frame, the main loop will act @@ -860,7 +870,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty, self.substs()); + let dest_layout = self.type_layout(dest_ty); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -897,7 +907,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): Find the result type size properly. Perhaps refactor out // Projection calculations so we can do the equivalent of `dest.1` here. - let s = self.type_size(left_ty, self.substs()); + let s = self.type_size(left_ty); self.memory.write_bool(dest.offset(s as isize), false)?; } @@ -919,7 +929,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty, self.substs()) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; @@ -997,7 +1007,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Repeat(ref operand, _) => { let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty, self.substs()), n), + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; @@ -1038,7 +1048,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Box(ty) => { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr)?; } @@ -1065,9 +1075,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { let src = self.eval_operand(operand)?; + let src_ty = self.operand_ty(operand); // FIXME(solson): Wrong for almost everything. - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - self.memory.copy(src, dest, size)?; + warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); + let dest_size = self.type_size(dest_ty); + let src_size = self.type_size(src_ty); + + // Hack to support fat pointer -> thin pointer casts to keep tests for + // other things passing for now. + let is_fat_ptr_cast = pointee_type(src_ty).map(|ty| { + !self.type_is_sized(ty) + }).unwrap_or(false); + + if dest_size == src_size || is_fat_ptr_cast { + self.memory.copy(src, dest, dest_size)?; + } else { + return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); + } } ReifyFnPointer => match self.operand_ty(operand).sty { @@ -1139,7 +1163,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { - let layout = self.type_layout(ty, self.substs()); + let layout = self.type_layout(ty); use rustc::ty::layout::Layout::*; match *layout { @@ -1211,7 +1235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty, self.substs()); + let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { @@ -1272,7 +1296,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Index(ref operand) => { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty, self.substs()), + ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; @@ -1298,7 +1322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { - let size = self.type_size(ty, self.substs()); + let size = self.type_size(ty); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { self.memory.drop_fill(src, size)?; diff --git a/src/memory.rs b/src/memory.rs index d9999821e1b03..ad6c60ef517c6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -99,17 +99,18 @@ impl<'tcx> Memory<'tcx> { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - let alloc = self.get_mut(ptr.alloc_id)?; - let size = alloc.bytes.len(); + let size = self.get_mut(ptr.alloc_id)?.bytes.len(); + if new_size > size { let amount = new_size - size; + let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size))); - // alloc.bytes.truncate(new_size); - // alloc.undef_mask.len = new_size; - // TODO: potentially remove relocations + self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; + let alloc = self.get_mut(ptr.alloc_id)?; + alloc.bytes.truncate(new_size); + alloc.undef_mask.truncate(new_size); } Ok(()) @@ -386,7 +387,7 @@ impl<'tcx> Memory<'tcx> { -> EvalResult> { let start = ptr.offset.saturating_sub(self.pointer_size - 1); - let end = start + size; + let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } @@ -535,11 +536,12 @@ impl UndefMask { self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } -} -// fn uniform_block(state: bool) -> Block { -// if state { !0 } else { 0 } -// } + fn truncate(&mut self, length: usize) { + self.len = length; + self.blocks.truncate(self.len / BLOCK_SIZE + 1); + } +} fn bit_index(bits: usize) -> (usize, usize) { (bits / BLOCK_SIZE, bits % BLOCK_SIZE) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 2fc1e615cff19..76b5b5f6e9638 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,22 +3,32 @@ extern crate compiletest_rs as compiletest; use std::path::PathBuf; fn run_mode(mode: &'static str) { + // Disable rustc's new error fomatting. It breaks these tests. + std::env::remove_var("RUST_NEW_ERROR_FORMAT"); + + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + let sysroot = match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + }; + let sysroot_flag = format!("--sysroot {}", sysroot); + // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; for &target in targets { let mut config = compiletest::default_config(); + config.host_rustcflags = Some(sysroot_flag.clone()); + config.mode = mode.parse().expect("Invalid mode"); + config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); config.rustc_path = "target/debug/miri".into(); - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target); - let path = format!("--sysroot {}", path); - config.target_rustcflags = Some(path.clone()); - config.host_rustcflags = Some(path); - let cfg_mode = mode.parse().ok().expect("Invalid mode"); - - config.mode = cfg_mode; config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target = target.to_owned(); + config.target_rustcflags = Some(sysroot_flag.clone()); compiletest::run_tests(&config); } } diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index f5aad1601c77d..a7175969efac5 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,6 +11,26 @@ fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } +#[miri_run] +fn allocate_reallocate() { + let mut s = String::new(); + + // 6 byte heap alloc (__rust_allocate) + s.push_str("foobar"); + assert_eq!(s.len(), 6); + assert_eq!(s.capacity(), 6); + + // heap size doubled to 12 (__rust_reallocate) + s.push_str("baz"); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 12); + + // heap size reduced to 9 (__rust_reallocate) + s.shrink_to_fit(); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 9); +} + #[miri_run] fn main() { assert_eq!(*make_box(), (1, 2)); diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 9b7ddb43b7c78..120c196abe975 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -55,7 +55,8 @@ fn two_nones() -> (Option, Option) { (None, None) } -#[miri_run] +// FIXME(solson): Casts inside PartialEq fails on 32-bit. +#[cfg_attr(target_pointer_width = "64", miri_run)] fn main() { assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); From 4d090fa6932853b0035745a51fbd4bd88f2ee9b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 11:39:15 +0200 Subject: [PATCH 0363/1096] report better errors when using a fn ptr as memory and vice versa --- src/error.rs | 6 ++++++ src/memory.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index a4ae8cbdda25b..49fd8564be639 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,8 @@ pub enum EvalError { ReadUndefBytes, InvalidBoolOp(mir::BinOp), Unimplemented(String), + DerefFunctionPointer, + ExecuteMemory, } pub type EvalResult = Result; @@ -48,6 +50,10 @@ impl Error for EvalError { EvalError::InvalidBoolOp(_) => "invalid boolean operation", EvalError::Unimplemented(ref msg) => msg, + EvalError::DerefFunctionPointer => + "tried to dereference a function pointer", + EvalError::ExecuteMemory => + "tried to treat a memory pointer as a function pointer", } } diff --git a/src/memory.rs b/src/memory.rs index ad6c60ef517c6..2bc5ff184de08 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -46,7 +46,10 @@ impl Pointer { //////////////////////////////////////////////////////////////////////////////// pub struct Memory<'tcx> { + /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, + /// Function "allocations". They exist solely so pointers have something to point to, and + /// we can figure out what they point to. functions: HashMap)>, next_id: AllocId, pub pointer_size: usize, @@ -137,16 +140,34 @@ impl<'tcx> Memory<'tcx> { //////////////////////////////////////////////////////////////////////////////// pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { - self.alloc_map.get(&id).ok_or(EvalError::DanglingPointerDeref) + match self.alloc_map.get(&id) { + Some(alloc) => Ok(alloc), + None => match self.functions.get(&id) { + Some(_) => Err(EvalError::DerefFunctionPointer), + None => Err(EvalError::DanglingPointerDeref), + } + } } pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { - self.alloc_map.get_mut(&id).ok_or(EvalError::DanglingPointerDeref) + match self.alloc_map.get_mut(&id) { + Some(alloc) => Ok(alloc), + None => match self.functions.get(&id) { + Some(_) => Err(EvalError::DerefFunctionPointer), + None => Err(EvalError::DanglingPointerDeref), + } + } } pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> { debug!("reading fn ptr: {}", id); - self.functions.get(&id).map(|&did| did).ok_or(EvalError::InvalidFunctionPointer) + match self.functions.get(&id) { + Some(&fn_id) => Ok(fn_id), + None => match self.alloc_map.get(&id) { + Some(_) => Err(EvalError::ExecuteMemory), + None => Err(EvalError::InvalidFunctionPointer), + } + } } /// Print an allocation and all allocations it points to, recursively. From 9565d48203e0c6f6b5c2fcdb575fa46f81deac78 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 12:29:01 +0200 Subject: [PATCH 0364/1096] add tests for fn pointers --- tests/compile-fail/deref_fn_ptr.rs | 13 +++++++++++++ tests/compile-fail/execute_memory.rs | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/compile-fail/deref_fn_ptr.rs create mode 100644 tests/compile-fail/execute_memory.rs diff --git a/tests/compile-fail/deref_fn_ptr.rs b/tests/compile-fail/deref_fn_ptr.rs new file mode 100644 index 0000000000000..52c7c2b8f9d55 --- /dev/null +++ b/tests/compile-fail/deref_fn_ptr.rs @@ -0,0 +1,13 @@ +#![feature(custom_attribute)] +#![allow(dead_code, unused_attributes)] + +fn f() {} + +#[miri_run] +fn deref_fn_ptr() -> i32 { + unsafe { + *std::mem::transmute::(f) //~ ERROR: tried to dereference a function pointer + } +} + +fn main() {} diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs new file mode 100644 index 0000000000000..4e06fd8db8ded --- /dev/null +++ b/tests/compile-fail/execute_memory.rs @@ -0,0 +1,14 @@ +#![feature(custom_attribute, box_syntax)] +#![allow(dead_code, unused_attributes)] + +#[miri_run] +fn deref_fn_ptr() { + //FIXME: this span is wrong + let x = box 42; //~ ERROR: tried to treat a memory pointer as a function pointer + unsafe { + let f = std::mem::transmute::, fn()>(x); + f() + } +} + +fn main() {} From fe9b4550061b7593a5324ed2cbcc8e86bf6d986b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 15:32:08 +0200 Subject: [PATCH 0365/1096] comment nit --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 2bc5ff184de08..35ee99eab9702 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -67,7 +67,7 @@ impl<'tcx> Memory<'tcx> { } // FIXME: never create two pointers to the same def_id + substs combination - // maybe re-use the statics cache of the gecx? + // maybe re-use the statics cache of the EvalContext? pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { let id = self.next_id; debug!("creating fn ptr: {}", id); From 55fd060cd885023f918f4e5c5f910523fa7dfd52 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Jun 2016 14:27:05 +0200 Subject: [PATCH 0366/1096] don't use `#[miri_run]` anymore, but execute the `main` function --- benches/fibonacci_helper.rs | 4 - benches/fibonacci_helper_iterative.rs | 4 - benches/smoke_helper.rs | 4 - src/bin/miri.rs | 73 ++++++++----------- tests/compile-fail/dangling_pointer_deref.rs | 8 ++ tests/compile-fail/deref_fn_ptr.rs | 13 +--- tests/compile-fail/errors.rs | 62 ---------------- tests/compile-fail/execute_memory.rs | 8 +- tests/compile-fail/invalid_bool.rs | 4 + tests/compile-fail/null_pointer_deref.rs | 4 + tests/compile-fail/out_of_bounds_read.rs | 5 ++ ..._of_relocation_makes_the_rest_undefined.rs | 11 +++ ...o_different_allocations_are_unorderable.rs | 7 ++ tests/compile-fail/undefined_byte_read.rs | 6 ++ tests/compile-fail/unimplemented.rs | 9 +-- tests/compile-fail/wild_pointer_deref.rs | 5 ++ tests/compiletest.rs | 2 +- tests/run-pass/arrays.rs | 13 +--- tests/run-pass/bools.rs | 8 -- tests/run-pass/bug.rs | 8 +- tests/run-pass/c_enums.rs | 7 -- tests/run-pass/calls.rs | 10 +-- tests/run-pass/function_pointers.rs | 8 +- tests/run-pass/heap.rs | 8 +- tests/run-pass/intrinsics.rs | 4 - tests/run-pass/loops.rs | 7 -- tests/run-pass/pointers.rs | 11 --- tests/run-pass/std.rs | 11 +-- tests/run-pass/sums.rs | 13 +--- tests/run-pass/vecs.rs | 11 +-- 30 files changed, 107 insertions(+), 241 deletions(-) create mode 100644 tests/compile-fail/dangling_pointer_deref.rs delete mode 100644 tests/compile-fail/errors.rs create mode 100644 tests/compile-fail/invalid_bool.rs create mode 100644 tests/compile-fail/null_pointer_deref.rs create mode 100644 tests/compile-fail/out_of_bounds_read.rs create mode 100644 tests/compile-fail/overwriting_part_of_relocation_makes_the_rest_undefined.rs create mode 100644 tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs create mode 100644 tests/compile-fail/undefined_byte_read.rs create mode 100644 tests/compile-fail/wild_pointer_deref.rs diff --git a/benches/fibonacci_helper.rs b/benches/fibonacci_helper.rs index cddfff9c2c92d..004000e70ea73 100644 --- a/benches/fibonacci_helper.rs +++ b/benches/fibonacci_helper.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute)] -#![allow(unused_attributes)] - -#[miri_run] #[inline(never)] pub fn main() { assert_eq!(fib(10), 55); diff --git a/benches/fibonacci_helper_iterative.rs b/benches/fibonacci_helper_iterative.rs index 486d8c2e8a868..59283be4820f7 100644 --- a/benches/fibonacci_helper_iterative.rs +++ b/benches/fibonacci_helper_iterative.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute)] -#![allow(unused_attributes)] - -#[miri_run] #[inline(never)] pub fn main() { assert_eq!(fib(10), 55); diff --git a/benches/smoke_helper.rs b/benches/smoke_helper.rs index e8691f244c02f..ef05b044cddde 100644 --- a/benches/smoke_helper.rs +++ b/benches/smoke_helper.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute)] -#![allow(unused_attributes)] - -#[miri_run] #[inline(never)] pub fn main() { } diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f14eb39439ef6..f1e9714ff39d1 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -20,7 +20,10 @@ use rustc::session::Session; use rustc_driver::{driver, CompilerCalls}; use rustc::ty::{TyCtxt, subst}; use rustc::mir::mir_map::MirMap; +use rustc::mir::repr::Mir; use rustc::hir::def_id::DefId; +use rustc::hir::{map, ItemFn, Item}; +use syntax::codemap::Span; struct MiriCompilerCalls; @@ -34,58 +37,46 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); - interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + + let tcx = state.tcx.unwrap(); + let mir_map = state.mir_map.unwrap(); + let (span, mir, def_id) = get_main(tcx, mir_map); + println!("found `main` function at: {:?}", span); + + let mut ecx = EvalContext::new(tcx, mir_map); + let substs = tcx.mk_substs(subst::Substs::empty()); + let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging"); + + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)); + + loop { + match step(&mut ecx) { + Ok(true) => {} + Ok(false) => break, + // FIXME: diverging functions can end up here in some future miri + Err(e) => { + report(tcx, &ecx, e); + break; + } + } + } }); control } } - - -fn interpret_start_points<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir_map: &MirMap<'tcx>, -) { - let initial_indentation = ::log_settings::settings().indentation; +fn get_main<'a, 'b, 'tcx: 'b>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'b MirMap<'tcx>) -> (Span, &'b Mir<'tcx>, DefId) { for (&id, mir) in &mir_map.map { - for attr in tcx.map.attrs(id) { - use syntax::attr::AttrMetaMethods; - if attr.check_name("miri_run") { - let item = tcx.map.expect_item(id); - - ::log_settings::settings().indentation = initial_indentation; - - debug!("Interpreting: {}", item.name); - - let mut ecx = EvalContext::new(tcx, mir_map); - let substs = tcx.mk_substs(subst::Substs::empty()); - let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs); - - ecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); - - loop { - match step(&mut ecx) { - Ok(true) => {} - Ok(false) => { - match return_ptr { - Some(ptr) => if log_enabled!(::log::LogLevel::Debug) { - ecx.memory().dump(ptr.alloc_id); - }, - None => warn!("diverging function returned"), - } - break; - } - // FIXME: diverging functions can end up here in some future miri - Err(e) => { - report(tcx, &ecx, e); - break; - } - } + if let map::Node::NodeItem(&Item { name, span, ref node, .. }) = tcx.map.get(id) { + if let ItemFn(..) = *node { + if name.as_str() == "main" { + return (span, mir, tcx.map.local_def_id(id)); } } } } + panic!("no main function found"); } fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { diff --git a/tests/compile-fail/dangling_pointer_deref.rs b/tests/compile-fail/dangling_pointer_deref.rs new file mode 100644 index 0000000000000..0ede7c96f0047 --- /dev/null +++ b/tests/compile-fail/dangling_pointer_deref.rs @@ -0,0 +1,8 @@ +fn main() { + let p = { + let b = Box::new(42); + &*b as *const i32 + }; + let x = unsafe { *p }; //~ ERROR: dangling pointer was dereferenced + panic!("this should never print: {}", x); +} diff --git a/tests/compile-fail/deref_fn_ptr.rs b/tests/compile-fail/deref_fn_ptr.rs index 52c7c2b8f9d55..c1eaf7eaa61d2 100644 --- a/tests/compile-fail/deref_fn_ptr.rs +++ b/tests/compile-fail/deref_fn_ptr.rs @@ -1,13 +1,8 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - fn f() {} -#[miri_run] -fn deref_fn_ptr() -> i32 { - unsafe { +fn main() { + let x: i32 = unsafe { *std::mem::transmute::(f) //~ ERROR: tried to dereference a function pointer - } + }; + panic!("this should never print: {}", x); } - -fn main() {} diff --git a/tests/compile-fail/errors.rs b/tests/compile-fail/errors.rs deleted file mode 100644 index 0cadd76cccf34..0000000000000 --- a/tests/compile-fail/errors.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] -fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 { - let mut p = &42; - unsafe { - let ptr: *mut _ = &mut p; - *(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause - // "attempted to interpret some raw bytes as a pointer address" instead of - // "attempted to read undefined bytes" - } - *p //~ ERROR: attempted to read undefined bytes -} - -#[miri_run] -fn pointers_to_different_allocations_are_unorderable() -> bool { - let x: *const u8 = &1; - let y: *const u8 = &2; - x < y //~ ERROR: attempted to do math or a comparison on pointers into different allocations -} - -#[miri_run] -fn invalid_bool() -> u8 { - let b = unsafe { std::mem::transmute::(2) }; - if b { 1 } else { 2 } //~ ERROR: invalid boolean value read -} - -#[miri_run] -fn undefined_byte_read() -> u8 { - let v: Vec = Vec::with_capacity(10); - let undef = unsafe { *v.get_unchecked(5) }; - undef + 1 //~ ERROR: attempted to read undefined bytes -} - -#[miri_run] -fn out_of_bounds_read() -> u8 { - let v: Vec = vec![1, 2]; - unsafe { *v.get_unchecked(5) } //~ ERROR: memory access of 5..6 outside bounds of allocation 11 which has size 2 -} - -#[miri_run] -fn dangling_pointer_deref() -> i32 { - let p = { - let b = Box::new(42); - &*b as *const i32 - }; - unsafe { *p } //~ ERROR: dangling pointer was dereferenced -} - -#[miri_run] -fn wild_pointer_deref() -> i32 { - let p = 42 as *const i32; - unsafe { *p } //~ ERROR: attempted to interpret some raw bytes as a pointer address -} - -#[miri_run] -fn null_pointer_deref() -> i32 { - unsafe { *std::ptr::null() } //~ ERROR: attempted to interpret some raw bytes as a pointer address -} - -fn main() {} diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs index 4e06fd8db8ded..413c6602114ba 100644 --- a/tests/compile-fail/execute_memory.rs +++ b/tests/compile-fail/execute_memory.rs @@ -1,8 +1,6 @@ -#![feature(custom_attribute, box_syntax)] -#![allow(dead_code, unused_attributes)] +#![feature(box_syntax)] -#[miri_run] -fn deref_fn_ptr() { +fn main() { //FIXME: this span is wrong let x = box 42; //~ ERROR: tried to treat a memory pointer as a function pointer unsafe { @@ -10,5 +8,3 @@ fn deref_fn_ptr() { f() } } - -fn main() {} diff --git a/tests/compile-fail/invalid_bool.rs b/tests/compile-fail/invalid_bool.rs new file mode 100644 index 0000000000000..9de2630797ece --- /dev/null +++ b/tests/compile-fail/invalid_bool.rs @@ -0,0 +1,4 @@ +fn main() { + let b = unsafe { std::mem::transmute::(2) }; + if b { unreachable!() } else { unreachable!() } //~ ERROR: invalid boolean value read +} diff --git a/tests/compile-fail/null_pointer_deref.rs b/tests/compile-fail/null_pointer_deref.rs new file mode 100644 index 0000000000000..3d1afe9215618 --- /dev/null +++ b/tests/compile-fail/null_pointer_deref.rs @@ -0,0 +1,4 @@ +fn main() { + let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: attempted to interpret some raw bytes as a pointer address + panic!("this should never print: {}", x); +} diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs new file mode 100644 index 0000000000000..3e9e87cdc6c9e --- /dev/null +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -0,0 +1,5 @@ +fn main() { + let v: Vec = vec![1, 2]; + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation 29 which has size 2 + panic!("this should never print: {}", x); +} diff --git a/tests/compile-fail/overwriting_part_of_relocation_makes_the_rest_undefined.rs b/tests/compile-fail/overwriting_part_of_relocation_makes_the_rest_undefined.rs new file mode 100644 index 0000000000000..50f51d0ba9cad --- /dev/null +++ b/tests/compile-fail/overwriting_part_of_relocation_makes_the_rest_undefined.rs @@ -0,0 +1,11 @@ +fn main() { + let mut p = &42; + unsafe { + let ptr: *mut _ = &mut p; + *(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause + // "attempted to interpret some raw bytes as a pointer address" instead of + // "attempted to read undefined bytes" + } + let x = *p; //~ ERROR: attempted to read undefined bytes + panic!("this should never print: {}", x); +} diff --git a/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs b/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs new file mode 100644 index 0000000000000..be478c8213246 --- /dev/null +++ b/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs @@ -0,0 +1,7 @@ +fn main() { + let x: *const u8 = &1; + let y: *const u8 = &2; + if x < y { //~ ERROR: attempted to do math or a comparison on pointers into different allocations + unreachable!() + } +} diff --git a/tests/compile-fail/undefined_byte_read.rs b/tests/compile-fail/undefined_byte_read.rs new file mode 100644 index 0000000000000..f8b6f7f4aec13 --- /dev/null +++ b/tests/compile-fail/undefined_byte_read.rs @@ -0,0 +1,6 @@ +fn main() { + let v: Vec = Vec::with_capacity(10); + let undef = unsafe { *v.get_unchecked(5) }; + let x = undef + 1; //~ ERROR: attempted to read undefined bytes + panic!("this should never print: {}", x); +} diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs index 7752650ade887..9611631a47a3b 100644 --- a/tests/compile-fail/unimplemented.rs +++ b/tests/compile-fail/unimplemented.rs @@ -1,12 +1,5 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - //error-pattern:begin_panic_fmt - -#[miri_run] -fn failed_assertions() { +fn main() { assert_eq!(5, 6); } - -fn main() {} diff --git a/tests/compile-fail/wild_pointer_deref.rs b/tests/compile-fail/wild_pointer_deref.rs new file mode 100644 index 0000000000000..1f472489b4fca --- /dev/null +++ b/tests/compile-fail/wild_pointer_deref.rs @@ -0,0 +1,5 @@ +fn main() { + let p = 42 as *const i32; + let x = unsafe { *p }; //~ ERROR: attempted to interpret some raw bytes as a pointer address + panic!("this should never print: {}", x); +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 76b5b5f6e9638..0619c5a765617 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -15,7 +15,7 @@ fn run_mode(mode: &'static str) { .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") .to_owned(), }; - let sysroot_flag = format!("--sysroot {}", sysroot); + let sysroot_flag = format!("--sysroot {} -Dwarnings", sysroot); // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; diff --git a/tests/run-pass/arrays.rs b/tests/run-pass/arrays.rs index db4f999a8b043..469dde3091eb2 100644 --- a/tests/run-pass/arrays.rs +++ b/tests/run-pass/arrays.rs @@ -1,50 +1,38 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn empty_array() -> [u16; 0] { [] } -#[miri_run] fn mini_array() -> [u16; 1] { [42] } -#[miri_run] fn big_array() -> [u16; 5] { [5, 4, 3, 2, 1] } -#[miri_run] fn array_array() -> [[u8; 2]; 3] { [[5, 4], [3, 2], [1, 0]] } -#[miri_run] fn index_unsafe() -> i32 { let a = [0, 10, 20, 30]; unsafe { *a.get_unchecked(2) } } -#[miri_run] fn index() -> i32 { let a = [0, 10, 20, 30]; a[2] } -#[miri_run] fn array_repeat() -> [u8; 8] { [42; 8] } -#[miri_run] fn slice_index() -> u8 { let arr: &[_] = &[101, 102, 103, 104, 105, 106]; arr[5] } -#[miri_run] fn main() { assert_eq!(empty_array(), []); assert_eq!(index_unsafe(), 20); @@ -53,4 +41,5 @@ fn main() { assert_eq!(big_array(), [5, 4, 3, 2, 1]); assert_eq!(array_array(), [[5, 4], [3, 2], [1, 0]]); assert_eq!(array_repeat(), [42; 8]); + assert_eq!(mini_array(), [42]); } diff --git a/tests/run-pass/bools.rs b/tests/run-pass/bools.rs index 953670fef9b27..103d7eac27cde 100644 --- a/tests/run-pass/bools.rs +++ b/tests/run-pass/bools.rs @@ -1,24 +1,17 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn boolean() -> bool { true } -#[miri_run] fn if_false() -> i64 { let c = false; if c { 1 } else { 0 } } -#[miri_run] fn if_true() -> i64 { let c = true; if c { 1 } else { 0 } } -#[miri_run] fn match_bool() -> i16 { let b = true; match b { @@ -27,7 +20,6 @@ fn match_bool() -> i16 { } } -#[miri_run] fn main() { assert!(boolean()); assert_eq!(if_false(), 0); diff --git a/tests/run-pass/bug.rs b/tests/run-pass/bug.rs index 3006da2c163de..a68f727322e29 100644 --- a/tests/run-pass/bug.rs +++ b/tests/run-pass/bug.rs @@ -1,14 +1,8 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - static mut X: usize = 5; -#[miri_run] -fn static_mut() { +fn main() { unsafe { X = 6; assert_eq!(X, 6); } } - -fn main() {} diff --git a/tests/run-pass/c_enums.rs b/tests/run-pass/c_enums.rs index 60790fef439fc..11897b73eb2ad 100644 --- a/tests/run-pass/c_enums.rs +++ b/tests/run-pass/c_enums.rs @@ -1,6 +1,3 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - enum Foo { Bar = 42, Baz, @@ -13,17 +10,14 @@ enum Signed { Quux = 100, } -#[miri_run] fn foo() -> [u8; 3] { [Foo::Bar as u8, Foo::Baz as u8, Foo::Quux as u8] } -#[miri_run] fn signed() -> [i8; 3] { [Signed::Bar as i8, Signed::Baz as i8, Signed::Quux as i8] } -#[miri_run] fn unsafe_match() -> bool { match unsafe { std::mem::transmute::(43) } { Foo::Baz => true, @@ -31,7 +25,6 @@ fn unsafe_match() -> bool { } } -#[miri_run] fn main() { assert_eq!(foo(), [42, 43, 100]); assert_eq!(signed(), [-42, -41, 100]); diff --git a/tests/run-pass/calls.rs b/tests/run-pass/calls.rs index ba68fb44a6c39..c4ba4a9b701ff 100644 --- a/tests/run-pass/calls.rs +++ b/tests/run-pass/calls.rs @@ -1,7 +1,5 @@ -#![feature(custom_attribute, const_fn)] -#![allow(dead_code, unused_attributes)] +#![feature(const_fn)] -#[miri_run] fn call() -> i32 { fn increment(x: i32) -> i32 { x + 1 @@ -9,7 +7,6 @@ fn call() -> i32 { increment(1) } -#[miri_run] fn factorial_recursive() -> i64 { fn fact(n: i64) -> i64 { if n == 0 { @@ -21,31 +18,28 @@ fn factorial_recursive() -> i64 { fact(10) } -#[miri_run] fn call_generic() -> (i16, bool) { fn id(t: T) -> T { t } (id(42), id(true)) } // Test calling a very simple function from the standard library. -#[miri_run] fn cross_crate_fn_call() -> i64 { if 1i32.is_positive() { 1 } else { 0 } } const fn foo(i: i64) -> i64 { *&i + 1 } -#[miri_run] fn const_fn_call() -> i64 { let x = 5 + foo(5); assert_eq!(x, 11); x } -#[miri_run] fn main() { assert_eq!(call(), 2); assert_eq!(factorial_recursive(), 3628800); assert_eq!(call_generic(), (42, true)); assert_eq!(cross_crate_fn_call(), 1); + assert_eq!(const_fn_call(), 11); } diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index 55a6f9fbeac46..8361a58ea4302 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -1,6 +1,3 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - fn f() -> i32 { 42 } @@ -9,9 +6,10 @@ fn return_fn_ptr() -> fn() -> i32 { f } -#[miri_run] fn call_fn_ptr() -> i32 { return_fn_ptr()() } -fn main() {} +fn main() { + assert_eq!(call_fn_ptr(), 42); +} diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index a7175969efac5..b533f91646988 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -1,17 +1,13 @@ -#![feature(custom_attribute, box_syntax)] -#![allow(dead_code, unused_attributes)] +#![feature(box_syntax)] -#[miri_run] fn make_box() -> Box<(i16, i16)> { Box::new((1, 2)) } -#[miri_run] fn make_box_syntax() -> Box<(i16, i16)> { box (1, 2) } -#[miri_run] fn allocate_reallocate() { let mut s = String::new(); @@ -31,8 +27,8 @@ fn allocate_reallocate() { assert_eq!(s.capacity(), 9); } -#[miri_run] fn main() { assert_eq!(*make_box(), (1, 2)); assert_eq!(*make_box_syntax(), (1, 2)); + allocate_reallocate(); } diff --git a/tests/run-pass/intrinsics.rs b/tests/run-pass/intrinsics.rs index ef7fa0d986135..3152737a601ca 100755 --- a/tests/run-pass/intrinsics.rs +++ b/tests/run-pass/intrinsics.rs @@ -1,9 +1,5 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - use std::mem::{size_of, size_of_val}; -#[miri_run] fn main() { assert_eq!(size_of::>(), 8); assert_eq!(size_of_val(&()), 0); diff --git a/tests/run-pass/loops.rs b/tests/run-pass/loops.rs index 57a2f7c4357be..222287cbe09ad 100644 --- a/tests/run-pass/loops.rs +++ b/tests/run-pass/loops.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn factorial_loop() -> i64 { let mut product = 1; let mut i = 1; @@ -14,7 +10,6 @@ fn factorial_loop() -> i64 { product } -#[miri_run] fn index_for_loop() -> usize { let mut sum = 0; let a = [0, 10, 20, 30]; @@ -24,7 +19,6 @@ fn index_for_loop() -> usize { sum } -#[miri_run] fn for_loop() -> usize { let mut sum = 0; let a = [0, 10, 20, 30]; @@ -34,7 +28,6 @@ fn for_loop() -> usize { sum } -#[miri_run] fn main() { assert_eq!(factorial_loop(), 3628800); assert_eq!(index_for_loop(), 60); diff --git a/tests/run-pass/pointers.rs b/tests/run-pass/pointers.rs index 9f28b982b4eee..2ef7eb0102f19 100644 --- a/tests/run-pass/pointers.rs +++ b/tests/run-pass/pointers.rs @@ -1,25 +1,18 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn one_line_ref() -> i16 { *&1 } -#[miri_run] fn basic_ref() -> i16 { let x = &1; *x } -#[miri_run] fn basic_ref_mut() -> i16 { let x = &mut 1; *x += 2; *x } -#[miri_run] fn basic_ref_mut_var() -> i16 { let mut a = 1; { @@ -29,7 +22,6 @@ fn basic_ref_mut_var() -> i16 { a } -#[miri_run] fn tuple_ref_mut() -> (i8, i8) { let mut t = (10, 20); { @@ -39,7 +31,6 @@ fn tuple_ref_mut() -> (i8, i8) { t } -#[miri_run] fn match_ref_mut() -> i8 { let mut t = (20, 22); { @@ -52,13 +43,11 @@ fn match_ref_mut() -> i8 { t.0 } -#[miri_run] fn dangling_pointer() -> *const i32 { let b = Box::new(42); &*b as *const i32 } -#[miri_run] fn main() { assert_eq!(one_line_ref(), 1); assert_eq!(basic_ref(), 1); diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index 1ec3c5e0bb98a..2e65550b07bf8 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,11 +1,7 @@ -#![feature(custom_attribute, box_syntax)] -#![allow(dead_code, unused_attributes)] - -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::rc::Rc; use std::sync::Arc; -#[miri_run] fn rc_cell() -> Rc> { let r = Rc::new(Cell::new(42)); let x = r.get(); @@ -15,7 +11,6 @@ fn rc_cell() -> Rc> { // TODO(solson): also requires destructors to run for the second borrow to work // TODO(solson): needs StructWrappedNullablePointer support -// #[miri_run] // fn rc_refcell() -> i32 { // let r = Rc::new(RefCell::new(42)); // *r.borrow_mut() += 10; @@ -23,19 +18,17 @@ fn rc_cell() -> Rc> { // x // } -#[miri_run] fn arc() -> Arc { let a = Arc::new(42); a } -#[miri_run] fn true_assert() { assert_eq!(1, 1); } -#[miri_run] fn main() { assert_eq!(*arc(), 42); assert_eq!(rc_cell().get(), 84); + true_assert(); } diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index 120c196abe975..adf4e8f987d32 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -4,7 +4,6 @@ #[derive(Debug, PartialEq)] enum Unit { Unit(()) } // Force non-C-enum representation. -#[miri_run] fn return_unit() -> Unit { Unit::Unit(()) } @@ -12,27 +11,22 @@ fn return_unit() -> Unit { #[derive(Debug, PartialEq)] enum MyBool { False(()), True(()) } // Force non-C-enum representation. -#[miri_run] fn return_true() -> MyBool { MyBool::True(()) } -#[miri_run] fn return_false() -> MyBool { MyBool::False(()) } -#[miri_run] fn return_none() -> Option { None } -#[miri_run] fn return_some() -> Option { Some(42) } -#[miri_run] fn match_opt_none() -> i8 { let x = None; match x { @@ -41,7 +35,6 @@ fn match_opt_none() -> i8 { } } -#[miri_run] fn match_opt_some() -> i8 { let x = Some(13); match x { @@ -50,13 +43,12 @@ fn match_opt_some() -> i8 { } } -#[miri_run] fn two_nones() -> (Option, Option) { (None, None) } // FIXME(solson): Casts inside PartialEq fails on 32-bit. -#[cfg_attr(target_pointer_width = "64", miri_run)] +#[cfg(target_pointer_width = "64")] fn main() { assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); @@ -67,3 +59,6 @@ fn main() { assert_eq!(return_true(), MyBool::True(())); assert_eq!(return_unit(), Unit::Unit(())); } + +#[cfg(not(target_pointer_width = "64"))] +fn main() {} diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index 2a4cc6128843b..b3a88014e6f9a 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn make_vec() -> Vec { let mut v = Vec::with_capacity(4); v.push(1); @@ -9,17 +5,14 @@ fn make_vec() -> Vec { v } -#[miri_run] fn make_vec_macro() -> Vec { vec![1, 2] } -#[miri_run] fn make_vec_macro_repeat() -> Vec { vec![42; 5] } -#[miri_run] fn vec_into_iter() -> u8 { vec![1, 2, 3, 4] .into_iter() @@ -27,7 +20,6 @@ fn vec_into_iter() -> u8 { .fold(0, |x, y| x + y) } -#[miri_run] fn vec_reallocate() -> Vec { let mut v = vec![1, 2]; v.push(3); @@ -36,9 +28,10 @@ fn vec_reallocate() -> Vec { v } -#[miri_run] fn main() { assert_eq!(vec_reallocate().len(), 5); assert_eq!(vec_into_iter(), 30); assert_eq!(make_vec().capacity(), 4); + assert_eq!(make_vec_macro(), [1, 2]); + assert_eq!(make_vec_macro_repeat(), [42; 5]); } From c36dcff0056a4a48651b778ed272aba80f6edd93 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Jun 2016 10:34:54 +0200 Subject: [PATCH 0367/1096] forbid calling functions through pointers of a different type --- src/error.rs | 14 +++-- src/interpreter/mod.rs | 49 +++++++++--------- src/interpreter/stepper.rs | 6 +-- src/memory.rs | 86 ++++++++++++++++++------------- src/primval.rs | 6 +-- tests/compile-fail/cast_fn_ptr.rs | 9 ++++ 6 files changed, 100 insertions(+), 70 deletions(-) create mode 100644 tests/compile-fail/cast_fn_ptr.rs diff --git a/src/error.rs b/src/error.rs index 49fd8564be639..35a978f2d2ea0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,12 @@ use std::error::Error; use std::fmt; use rustc::mir::repr as mir; +use rustc::ty::BareFnTy; use memory::Pointer; #[derive(Clone, Debug)] -pub enum EvalError { +pub enum EvalError<'tcx> { + FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), DanglingPointerDeref, InvalidFunctionPointer, InvalidBool, @@ -24,11 +26,13 @@ pub enum EvalError { ExecuteMemory, } -pub type EvalResult = Result; +pub type EvalResult<'tcx, T> = Result>; -impl Error for EvalError { +impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { match *self { + EvalError::FunctionPointerTyMismatch(..) => + "tried to call a function through a function pointer of a different type", EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => @@ -60,13 +64,15 @@ impl Error for EvalError { fn cause(&self) -> Option<&Error> { None } } -impl fmt::Display for EvalError { +impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { EvalError::PointerOutOfBounds { ptr, size, allocation_size } => { write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}", ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) }, + EvalError::FunctionPointerTyMismatch(expected, got) => + write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1b0b416e0e12c..4acefb91130a4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -18,14 +18,14 @@ use syntax::attr; use syntax::codemap::{self, DUMMY_SP, Span}; use error::{EvalError, EvalResult}; -use memory::{Memory, Pointer}; +use memory::{Memory, Pointer, FunctionDefinition}; use primval::{self, PrimVal}; use std::collections::HashMap; mod stepper; -pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult { +pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, bool> { stepper::Stepper::new(ecx).step() } @@ -159,7 +159,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // TODO(solson): Try making const_to_primval instead. - fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { + fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { use rustc::middle::const_val::ConstVal::*; match *const_val { Float(_f) => unimplemented!(), @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) - -> EvalResult<()> { + -> EvalResult<'tcx, ()> { use rustc::mir::repr::TerminatorKind::*; match terminator.kind { Return => self.pop_stack_frame(), @@ -434,7 +434,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.eval_operand(func)?; assert_eq!(ptr.offset, 0); let fn_ptr = self.memory.read_ptr(ptr)?; - let (def_id, substs) = self.memory.get_fn(fn_ptr.alloc_id)?; + let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; + if fn_ty != bare_fn_ty { + return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); + } self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, terminator.source_info.span)? }, @@ -480,7 +483,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_ptr: Option, args: &[mir::Operand<'tcx>], span: Span, - ) -> EvalResult<()> { + ) -> EvalResult<'tcx, ()> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { @@ -559,7 +562,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { + fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); return Ok(()); @@ -601,7 +604,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { + fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty); @@ -629,7 +632,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult { + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { let not_null = match self.memory.read_usize(ptr) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, @@ -646,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize - ) -> EvalResult<()> { + ) -> EvalResult<'tcx, ()> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); @@ -792,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize, - ) -> EvalResult<()> { + ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { @@ -855,7 +858,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Pointer, offsets: I, operands: &[mir::Operand<'tcx>], - ) -> EvalResult<()> { + ) -> EvalResult<'tcx, ()> { for (offset, operand) in offsets.into_iter().zip(operands) { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); @@ -866,7 +869,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) - -> EvalResult<()> + -> EvalResult<'tcx, ()> { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); @@ -1095,8 +1098,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ReifyFnPointer => match self.operand_ty(operand).sty { - ty::TyFnDef(def_id, substs, _) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs); + ty::TyFnDef(def_id, substs, fn_ty) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); self.memory.write_ptr(dest, fn_ptr)?; }, ref other => panic!("reify fn pointer on {:?}", other), @@ -1112,7 +1115,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult { + fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -1133,7 +1136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.field_path_offset(inner_ty, path) } - fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult { + fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, Size> { let mut offset = Size::from_bytes(0); // Skip the initial 0 intended for LLVM GEP. @@ -1146,7 +1149,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(offset) } - fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult> { + fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match ty.sty { ty::TyStruct(adt_def, substs) => { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) @@ -1162,7 +1165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { + fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { let layout = self.type_layout(ty); use rustc::ty::layout::Layout::*; @@ -1179,7 +1182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult { + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()), @@ -1213,7 +1216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { ReturnPointer => self.frame().return_ptr @@ -1321,7 +1324,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(self.mir().operand_ty(self.tcx, operand), self.substs()) } - fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { + fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { @@ -1330,7 +1333,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { + pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy}; let val = match (self.memory.pointer_size, &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 8603054d124c2..3671194384114 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -23,7 +23,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { } } - fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { + fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; self.ecx.eval_assignment(lvalue, rvalue)?; @@ -31,7 +31,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { Ok(()) } - fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { + fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { // after a terminator we go to a new block self.ecx.frame_mut().stmt = 0; trace!("{:?}", terminator.kind); @@ -43,7 +43,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { } // returns true as long as there are more things to do - pub(super) fn step(&mut self) -> EvalResult { + pub(super) fn step(&mut self) -> EvalResult<'tcx, bool> { if self.ecx.stack.is_empty() { return Ok(false); } diff --git a/src/memory.rs b/src/memory.rs index 35ee99eab9702..e20d92207dbf2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,6 +4,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, mem, ptr}; use rustc::hir::def_id::DefId; +use rustc::ty::BareFnTy; use rustc::ty::subst::Substs; use error::{EvalError, EvalResult}; @@ -41,6 +42,13 @@ impl Pointer { } } +#[derive(Debug, Copy, Clone)] +pub struct FunctionDefinition<'tcx> { + pub def_id: DefId, + pub substs: &'tcx Substs<'tcx>, + pub fn_ty: &'tcx BareFnTy<'tcx>, +} + //////////////////////////////////////////////////////////////////////////////// // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// @@ -50,7 +58,7 @@ pub struct Memory<'tcx> { alloc_map: HashMap, /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. - functions: HashMap)>, + functions: HashMap>, next_id: AllocId, pub pointer_size: usize, } @@ -68,11 +76,15 @@ impl<'tcx> Memory<'tcx> { // FIXME: never create two pointers to the same def_id + substs combination // maybe re-use the statics cache of the EvalContext? - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; - self.functions.insert(id, (def_id, substs)); + self.functions.insert(id, FunctionDefinition { + def_id: def_id, + substs: substs, + fn_ty: fn_ty, + }); Pointer { alloc_id: id, offset: 0, @@ -96,7 +108,7 @@ impl<'tcx> Memory<'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> { + pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, ()> { if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); @@ -120,7 +132,7 @@ impl<'tcx> Memory<'tcx> { } // TODO(solson): See comment on `reallocate`. - pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> { + pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx, ()> { if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); @@ -139,7 +151,7 @@ impl<'tcx> Memory<'tcx> { // Allocation accessors //////////////////////////////////////////////////////////////////////////////// - pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> { + pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { match self.alloc_map.get(&id) { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { @@ -149,7 +161,7 @@ impl<'tcx> Memory<'tcx> { } } - pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> { + pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { match self.alloc_map.get_mut(&id) { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { @@ -159,7 +171,7 @@ impl<'tcx> Memory<'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<(DefId, &'tcx Substs<'tcx>)> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { Some(&fn_id) => Ok(fn_id), @@ -229,7 +241,7 @@ impl<'tcx> Memory<'tcx> { // Byte accessors //////////////////////////////////////////////////////////////////////////////// - fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -241,7 +253,7 @@ impl<'tcx> Memory<'tcx> { Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { + fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -253,7 +265,7 @@ impl<'tcx> Memory<'tcx> { Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -261,7 +273,7 @@ impl<'tcx> Memory<'tcx> { self.get_bytes_unchecked(ptr, size) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) @@ -271,7 +283,7 @@ impl<'tcx> Memory<'tcx> { // Reading and writing //////////////////////////////////////////////////////////////////////////////// - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); @@ -294,27 +306,27 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> { + pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { self.get_bytes(ptr, size) } - pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<()> { + pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { let bytes = self.get_bytes_mut(ptr, src.len())?; bytes.clone_from_slice(src); Ok(()) } - pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<()> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> { let bytes = self.get_bytes_mut(ptr, count)?; for b in bytes { *b = val; } Ok(()) } - pub fn drop_fill(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + pub fn drop_fill(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { self.write_repeat(ptr, mem::POST_DROP_U8, size) } - pub fn read_ptr(&self, ptr: Pointer) -> EvalResult { + pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { let size = self.pointer_size; self.check_defined(ptr, size)?; let offset = self.get_bytes_unchecked(ptr, size)? @@ -326,7 +338,7 @@ impl<'tcx> Memory<'tcx> { } } - pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<()> { + pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> { { let size = self.pointer_size; let mut bytes = self.get_bytes_mut(dest, size)?; @@ -336,7 +348,7 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> { + pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { let pointer_size = self.pointer_size; match val { PrimVal::Bool(b) => self.write_bool(ptr, b), @@ -353,7 +365,7 @@ impl<'tcx> Memory<'tcx> { } } - pub fn read_bool(&self, ptr: Pointer) -> EvalResult { + pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { let bytes = self.get_bytes(ptr, 1)?; match bytes[0] { 0 => Ok(false), @@ -362,40 +374,40 @@ impl<'tcx> Memory<'tcx> { } } - pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<()> { + pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) } - pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult { + pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { self.get_bytes(ptr, size).map(|mut b| b.read_int::(size).unwrap()) } - pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<()> { + pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::(n, size).unwrap()) } - pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult { + pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { self.get_bytes(ptr, size).map(|mut b| b.read_uint::(size).unwrap()) } - pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<()> { + pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::(n, size).unwrap()) } - pub fn read_isize(&self, ptr: Pointer) -> EvalResult { + pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { self.read_int(ptr, self.pointer_size) } - pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<()> { + pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx, ()> { let size = self.pointer_size; self.write_int(ptr, n, size) } - pub fn read_usize(&self, ptr: Pointer) -> EvalResult { + pub fn read_usize(&self, ptr: Pointer) -> EvalResult<'tcx, u64> { self.read_uint(ptr, self.pointer_size) } - pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<()> { + pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { let size = self.pointer_size; self.write_uint(ptr, n, size) } @@ -405,14 +417,14 @@ impl<'tcx> Memory<'tcx> { //////////////////////////////////////////////////////////////////////////////// fn relocations(&self, ptr: Pointer, size: usize) - -> EvalResult> + -> EvalResult<'tcx, btree_map::Range> { let start = ptr.offset.saturating_sub(self.pointer_size - 1); let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } - fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<()> { + fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { return Ok(()); } @@ -436,7 +448,7 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { let overlapping_start = self.relocations(ptr, 0)?.count(); let overlapping_end = self.relocations(ptr.offset(size as isize), 0)?.count(); if overlapping_start + overlapping_end != 0 { @@ -445,7 +457,7 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { let relocations: Vec<_> = self.relocations(src, size)? .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. @@ -461,7 +473,7 @@ impl<'tcx> Memory<'tcx> { //////////////////////////////////////////////////////////////////////////////// // FIXME(solson): This is a very naive, slow version. - fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> { + fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. let mut v = Vec::with_capacity(size); for i in 0..size { @@ -474,7 +486,7 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<()> { + fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); @@ -483,7 +495,7 @@ impl<'tcx> Memory<'tcx> { } pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) - -> EvalResult<()> + -> EvalResult<'tcx, ()> { let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); diff --git a/src/primval.rs b/src/primval.rs index 0b1658739d9d6..5e1cdac45a1fb 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -13,7 +13,7 @@ pub enum PrimVal { IntegerPtr(u64), } -pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult { +pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::BinOp::*; use self::PrimVal::*; @@ -43,7 +43,7 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul }) } - fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult { + fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::BinOp::*; match bin_op { Eq => Ok(Bool(false)), @@ -108,7 +108,7 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul Ok(val) } -pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> EvalResult { +pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::UnOp::*; use self::PrimVal::*; match (un_op, val) { diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs new file mode 100644 index 0000000000000..db87e9e422bd8 --- /dev/null +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -0,0 +1,9 @@ +fn main() { //~ ERROR tried to call a function of type + fn f() {} + + let g = unsafe { + std::mem::transmute::(f) + }; + + g(42) +} From 1bd00e8cb4667afe5cec047f460e27e3e6664629 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Jun 2016 11:52:45 +0200 Subject: [PATCH 0368/1096] run `start` and `main` language item if provided --- src/bin/miri.rs | 22 ++++------------------ tests/run-pass/main_fn.rs | 5 +++++ tests/run-pass/start_fn.rs | 6 ++++++ 3 files changed, 15 insertions(+), 18 deletions(-) create mode 100644 tests/run-pass/main_fn.rs create mode 100644 tests/run-pass/start_fn.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f1e9714ff39d1..cdd6ee027d97f 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -19,11 +19,7 @@ use miri::{ use rustc::session::Session; use rustc_driver::{driver, CompilerCalls}; use rustc::ty::{TyCtxt, subst}; -use rustc::mir::mir_map::MirMap; -use rustc::mir::repr::Mir; use rustc::hir::def_id::DefId; -use rustc::hir::{map, ItemFn, Item}; -use syntax::codemap::Span; struct MiriCompilerCalls; @@ -40,9 +36,12 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let tcx = state.tcx.unwrap(); let mir_map = state.mir_map.unwrap(); - let (span, mir, def_id) = get_main(tcx, mir_map); + + let (node_id, span) = state.session.entry_fn.borrow().expect("no main or start function found"); println!("found `main` function at: {:?}", span); + let mir = mir_map.map.get(&node_id).expect("no mir for main function"); + let def_id = tcx.map.local_def_id(node_id); let mut ecx = EvalContext::new(tcx, mir_map); let substs = tcx.mk_substs(subst::Substs::empty()); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging"); @@ -66,19 +65,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } -fn get_main<'a, 'b, 'tcx: 'b>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'b MirMap<'tcx>) -> (Span, &'b Mir<'tcx>, DefId) { - for (&id, mir) in &mir_map.map { - if let map::Node::NodeItem(&Item { name, span, ref node, .. }) = tcx.map.get(id) { - if let ItemFn(..) = *node { - if name.as_str() == "main" { - return (span, mir, tcx.map.local_def_id(id)); - } - } - } - } - panic!("no main function found"); -} - fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { let frame = ecx.stack().last().expect("stackframe was empty"); let block = &frame.mir.basic_blocks()[frame.next_block]; diff --git a/tests/run-pass/main_fn.rs b/tests/run-pass/main_fn.rs new file mode 100644 index 0000000000000..91d183ee6af70 --- /dev/null +++ b/tests/run-pass/main_fn.rs @@ -0,0 +1,5 @@ +#![feature(main)] + +#[main] +fn foo() { +} diff --git a/tests/run-pass/start_fn.rs b/tests/run-pass/start_fn.rs new file mode 100644 index 0000000000000..600f6e49f68cb --- /dev/null +++ b/tests/run-pass/start_fn.rs @@ -0,0 +1,6 @@ +#![feature(start)] + +#[start] +fn foo(_nargs: isize, _args: *const *const u8) -> isize { + return 0; +} From 8abd2931196beb4bea3133d32643c6634a13fa07 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Jun 2016 11:54:28 +0200 Subject: [PATCH 0369/1096] sysroot_flag is now used for more flags --- tests/compiletest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 0619c5a765617..77cfc57db293d 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -15,20 +15,20 @@ fn run_mode(mode: &'static str) { .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") .to_owned(), }; - let sysroot_flag = format!("--sysroot {} -Dwarnings", sysroot); + let flags = format!("--sysroot {} -Dwarnings", sysroot); // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; for &target in targets { let mut config = compiletest::default_config(); - config.host_rustcflags = Some(sysroot_flag.clone()); + config.host_rustcflags = Some(flags.clone()); config.mode = mode.parse().expect("Invalid mode"); config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); config.rustc_path = "target/debug/miri".into(); config.src_base = PathBuf::from(format!("tests/{}", mode)); config.target = target.to_owned(); - config.target_rustcflags = Some(sysroot_flag.clone()); + config.target_rustcflags = Some(flags.clone()); compiletest::run_tests(&config); } } From 269f70007f28cf63aada3a6547f968ea68cb7040 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 14 Jun 2016 19:30:59 -0600 Subject: [PATCH 0370/1096] Get the sysroot (like compiletest) in Miri itself. --- src/bin/miri.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f14eb39439ef6..a29e44cf96066 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -113,12 +113,6 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { err.emit(); } -fn main() { - init_logger(); - let args: Vec = std::env::args().collect(); - rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); -} - fn init_logger() { const NSPACES: usize = 40; let format = |record: &log::LogRecord| { @@ -142,3 +136,28 @@ fn init_logger() { builder.init().unwrap(); } + +fn find_sysroot() -> String { + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + } +} + +fn main() { + init_logger(); + let mut args: Vec = std::env::args().collect(); + + let sysroot_flag = String::from("--sysroot"); + if !args.contains(&sysroot_flag) { + args.push(sysroot_flag); + args.push(find_sysroot()); + } + + rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); +} From 16f778ad207650c0c9f4e049953f5dda2c48ec12 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 14 Jun 2016 20:13:59 -0600 Subject: [PATCH 0371/1096] Rename next_block to block and reorganize Frame fields. --- src/bin/miri.rs | 2 +- src/interpreter/mod.rs | 51 ++++++++++++++++++++++++-------------- src/interpreter/stepper.rs | 4 +-- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index a29e44cf96066..2d6cf22d58958 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -90,7 +90,7 @@ fn interpret_start_points<'a, 'tcx>( fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { let frame = ecx.stack().last().expect("stackframe was empty"); - let block = &frame.mir.basic_blocks()[frame.next_block]; + let block = &frame.mir.basic_blocks()[frame.block]; let span = if frame.stmt < block.statements.len() { block.statements[frame.stmt].source_info.span } else { diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1b0b416e0e12c..8b69fc6c2483c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -42,7 +42,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The virtual memory system. memory: Memory<'tcx>, - /// Precomputed statics, constants and promoteds + /// Precomputed statics, constants and promoteds. statics: HashMap, Pointer>, /// The virtual call stack. @@ -51,20 +51,25 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// A stack frame. pub struct Frame<'a, 'tcx: 'a> { - /// The def_id of the current function - pub def_id: DefId, + //////////////////////////////////////////////////////////////////////////////// + // Function and callsite information + //////////////////////////////////////////////////////////////////////////////// - /// The span of the call site - pub span: codemap::Span, + /// The MIR for the function called on this frame. + pub mir: CachedMir<'a, 'tcx>, + + /// The def_id of the current function. + pub def_id: DefId, - /// type substitutions for the current function invocation + /// type substitutions for the current function invocation. pub substs: &'tcx Substs<'tcx>, - /// The MIR for the function called on this frame. - pub mir: CachedMir<'a, 'tcx>, + /// The span of the call site. + pub span: codemap::Span, - /// The block that is currently executed (or will be executed after the above call stacks return) - pub next_block: mir::BasicBlock, + //////////////////////////////////////////////////////////////////////////////// + // Return pointer and local allocations + //////////////////////////////////////////////////////////////////////////////// /// A pointer for writing the return value of the current call if it's not a diverging call. pub return_ptr: Option, @@ -80,7 +85,15 @@ pub struct Frame<'a, 'tcx: 'a> { /// The offset of the first temporary in `self.locals`. pub temp_offset: usize, - /// The index of the currently evaluated statment + //////////////////////////////////////////////////////////////////////////////// + // Current position within the function + //////////////////////////////////////////////////////////////////////////////// + + /// The block that is currently executed (or will be executed after the above call stacks + /// return). + pub block: mir::BasicBlock, + + /// The index of the currently evaluated statment. pub stmt: usize, } @@ -349,7 +362,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack.push(Frame { mir: mir.clone(), - next_block: mir::START_BLOCK, + block: mir::START_BLOCK, return_ptr: return_ptr, locals: locals, var_offset: num_args, @@ -374,13 +387,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Return => self.pop_stack_frame(), Goto { target } => { - self.frame_mut().next_block = target; + self.frame_mut().block = target; }, If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; - self.frame_mut().next_block = if cond_val { then_target } else { else_target }; + self.frame_mut().block = if cond_val { then_target } else { else_target }; } SwitchInt { ref discr, ref values, ref targets, .. } => { @@ -403,7 +416,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.frame_mut().next_block = target_block; + self.frame_mut().block = target_block; } Switch { ref discr, ref targets, adt_def } => { @@ -415,7 +428,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match matching { Some(i) => { - self.frame_mut().next_block = targets[i]; + self.frame_mut().block = targets[i]; }, None => return Err(EvalError::InvalidDiscriminant), } @@ -424,7 +437,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Call { ref func, ref args, ref destination, .. } => { let mut return_ptr = None; if let Some((ref lv, target)) = *destination { - self.frame_mut().next_block = target; + self.frame_mut().block = target; return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); } @@ -451,14 +464,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.eval_lvalue(location)?.to_ptr(); let ty = self.lvalue_ty(location); self.drop(ptr, ty)?; - self.frame_mut().next_block = target; + self.frame_mut().block = target; } Assert { ref cond, expected, ref msg, target, cleanup } => { let actual_ptr = self.eval_operand(cond)?; let actual = self.memory.read_bool(actual_ptr)?; if actual == expected { - self.frame_mut().next_block = target; + self.frame_mut().block = target; } else { panic!("unimplemented: jump to {:?} and print {:?}", cleanup, msg); } diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index 8603054d124c2..e92ddd1faa2df 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -37,7 +37,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { trace!("{:?}", terminator.kind); self.ecx.eval_terminator(terminator)?; if !self.ecx.stack.is_empty() { - trace!("// {:?}", self.ecx.frame().next_block); + trace!("// {:?}", self.ecx.frame().block); } Ok(()) } @@ -48,7 +48,7 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { return Ok(false); } - let block = self.ecx.frame().next_block; + let block = self.ecx.frame().block; let stmt = self.ecx.frame().stmt; let mir = self.ecx.mir(); let basic_block = &mir.basic_blocks()[block]; From a55ac1fea8fe4ca8d96d359a8f5b9538e5e9c351 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Jun 2016 12:55:04 +0200 Subject: [PATCH 0372/1096] pass arguments to `start` --- src/bin/miri.rs | 11 +++++++++++ src/interpreter/mod.rs | 6 +++++- tests/compiletest.rs | 3 +++ tests/run-pass/start_fn.rs | 7 +++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index cdd6ee027d97f..8cc52e39ca69e 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -48,6 +48,17 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)); + if mir.arg_decls.len() == 2 { + // start function + let ptr_size = ecx.memory().pointer_size; + let nargs = ecx.memory_mut().allocate(ptr_size); + ecx.memory_mut().write_usize(nargs, 0).unwrap(); + let args = ecx.memory_mut().allocate(ptr_size); + ecx.memory_mut().write_usize(args, 0).unwrap(); + ecx.frame_mut().locals[0] = nargs; + ecx.frame_mut().locals[1] = args; + } + loop { match step(&mut ecx) { Ok(true) => {} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1b0b416e0e12c..89a3b2f593eda 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -154,6 +154,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.memory } + pub fn memory_mut(&mut self) -> &mut Memory<'tcx> { + &mut self.memory + } + pub fn stack(&self) -> &[Frame] { &self.stack } @@ -1373,7 +1377,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack.last().expect("no call frames exist") } - fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { + pub fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 77cfc57db293d..7ce9636fc05f0 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -21,6 +21,9 @@ fn run_mode(mode: &'static str) { let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; for &target in targets { + use std::io::Write; + let stderr = std::io::stderr(); + write!(stderr.lock(), "running tests for target {}", target).unwrap(); let mut config = compiletest::default_config(); config.host_rustcflags = Some(flags.clone()); config.mode = mode.parse().expect("Invalid mode"); diff --git a/tests/run-pass/start_fn.rs b/tests/run-pass/start_fn.rs index 600f6e49f68cb..8b884e22fedf7 100644 --- a/tests/run-pass/start_fn.rs +++ b/tests/run-pass/start_fn.rs @@ -1,6 +1,9 @@ #![feature(start)] #[start] -fn foo(_nargs: isize, _args: *const *const u8) -> isize { - return 0; +fn foo(nargs: isize, args: *const *const u8) -> isize { + if nargs > 0 { + assert!(unsafe{*args} as usize != 0); + } + 0 } From d82a79220b8533dc553cddf3c3a90d2bed6a7271 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Jun 2016 13:00:51 +0200 Subject: [PATCH 0373/1096] use the logging framework instead of println! --- src/bin/miri.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8cc52e39ca69e..8ec691dbbbc27 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -38,7 +38,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mir_map = state.mir_map.unwrap(); let (node_id, span) = state.session.entry_fn.borrow().expect("no main or start function found"); - println!("found `main` function at: {:?}", span); + debug!("found `main` function at: {:?}", span); let mir = mir_map.map.get(&node_id).expect("no mir for main function"); let def_id = tcx.map.local_def_id(node_id); From 82dc95c3ad79697acd68df09684e9d22b731ad63 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Jun 2016 13:18:35 +0200 Subject: [PATCH 0374/1096] create a miri-pass test that allows us to run miri for arbitrary targets --- .travis.yml | 7 ++-- Cargo.lock | 10 ++++-- Cargo.toml | 2 +- src/bin/miri.rs | 4 ++- tests/compiletest.rs | 78 ++++++++++++++++++++++++++++++-------------- 5 files changed, 70 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index d56ca04817e13..8f88f6636504a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,10 @@ before_script: pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: -- | - travis-cargo build && - env RUST_SYSROOT=$HOME/rust travis-cargo test +- set -e +- travis-cargo build +- RUST_SYSROOT=$HOME/rust +- travis-cargo test notifications: email: on_success: never diff --git a/Cargo.lock b/Cargo.lock index 240a2144807da..d0b785a061fbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,10 +24,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -96,6 +97,11 @@ name = "regex-syntax" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc-serialize" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "thread-id" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index 5a8211230fa86..fea568e244d43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ log = "0.3.6" log_settings = "0.1.1" [dev-dependencies] -compiletest_rs = "0.1.1" +compiletest_rs = "0.2" diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8ec691dbbbc27..ac5bf495f4874 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -17,7 +17,7 @@ use miri::{ Frame, }; use rustc::session::Session; -use rustc_driver::{driver, CompilerCalls}; +use rustc_driver::{driver, CompilerCalls, Compilation}; use rustc::ty::{TyCtxt, subst}; use rustc::hir::def_id::DefId; @@ -31,6 +31,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { ) -> driver::CompileController<'a> { let mut control = driver::CompileController::basic(); + control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); @@ -70,6 +71,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } } + state.session.abort_if_errors(); }); control diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 7ce9636fc05f0..01cc8ccdb0056 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,43 +1,73 @@ extern crate compiletest_rs as compiletest; -use std::path::PathBuf; +use std::path::{PathBuf, Path}; +use std::io::Write; -fn run_mode(mode: &'static str) { +fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) { // Disable rustc's new error fomatting. It breaks these tests. std::env::remove_var("RUST_NEW_ERROR_FORMAT"); - - // Taken from https://github.com/Manishearth/rust-clippy/pull/911. - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - let sysroot = match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => option_env!("RUST_SYSROOT") - .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") - .to_owned(), - }; let flags = format!("--sysroot {} -Dwarnings", sysroot); - - // FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that - let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]; - - for &target in targets { - use std::io::Write; - let stderr = std::io::stderr(); - write!(stderr.lock(), "running tests for target {}", target).unwrap(); + for_all_targets(sysroot, |target| { let mut config = compiletest::default_config(); config.host_rustcflags = Some(flags.clone()); config.mode = mode.parse().expect("Invalid mode"); - config.run_lib_path = format!("{}/lib/rustlib/{}/lib", sysroot, target); + config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib"); config.rustc_path = "target/debug/miri".into(); - config.src_base = PathBuf::from(format!("tests/{}", mode)); + config.src_base = PathBuf::from(format!("tests/{}", dir)); config.target = target.to_owned(); config.target_rustcflags = Some(flags.clone()); compiletest::run_tests(&config); + }); +} + +fn for_all_targets(sysroot: &str, f: F) { + for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { + let target = target.unwrap(); + if !target.metadata().unwrap().is_dir() { + continue; + } + let target = target.path().iter().rev().next().unwrap().to_str().unwrap().to_owned(); + if target == "etc" { + continue; + } + let stderr = std::io::stderr(); + writeln!(stderr.lock(), "running tests for target {}", target).unwrap(); + f(target); } } #[test] fn compile_test() { - run_mode("compile-fail"); - run_mode("run-pass"); + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + let sysroot = match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + }; + run_mode("compile-fail", "compile-fail", &sysroot); + for_all_targets(&sysroot, |target| { + for file in std::fs::read_dir("tests/run-pass").unwrap() { + let file = file.unwrap(); + if !file.metadata().unwrap().is_file() { + continue; + } + let file = file.path(); + let stderr = std::io::stderr(); + writeln!(stderr.lock(), "test [miri-pass] {}", file.to_str().unwrap()).unwrap(); + let mut cmd = std::process::Command::new("target/debug/miri"); + cmd.arg(file); + cmd.arg(format!("--sysroot={}", sysroot)); + cmd.arg("-Dwarnings"); + cmd.arg(format!("-target={}", target)); + let libs = Path::new(&sysroot).join("lib"); + let sysroot = libs.join("rustlib").join(&target).join("lib"); + let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); + cmd.env(compiletest::procsrv::dylib_env_var(), paths); + } + let stderr = std::io::stderr(); + writeln!(stderr.lock(), "").unwrap(); + }) } From 506f2deaf9e718a947f0b4107036bce22b843721 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:30:47 +0200 Subject: [PATCH 0375/1096] actually execute miri-pass tests --- tests/compiletest.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 01cc8ccdb0056..8c319977d8d83 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -56,16 +56,25 @@ fn compile_test() { } let file = file.path(); let stderr = std::io::stderr(); - writeln!(stderr.lock(), "test [miri-pass] {}", file.to_str().unwrap()).unwrap(); + write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap(); let mut cmd = std::process::Command::new("target/debug/miri"); cmd.arg(file); cmd.arg(format!("--sysroot={}", sysroot)); cmd.arg("-Dwarnings"); - cmd.arg(format!("-target={}", target)); + cmd.arg(format!("--target={}", target)); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); cmd.env(compiletest::procsrv::dylib_env_var(), paths); + match cmd.output() { + Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), + Ok(output) => { + writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap(); + writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); + } + Err(e) => writeln!(stderr.lock(), "FAILED: {}", e).unwrap(), + } } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); From 9cceef06630b9346831b4f85f6b7a6077a119dad Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:34:05 +0200 Subject: [PATCH 0376/1096] simplify target name extraction --- tests/compiletest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 8c319977d8d83..bdb7acf56b045 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -26,7 +26,7 @@ fn for_all_targets(sysroot: &str, f: F) { if !target.metadata().unwrap().is_dir() { continue; } - let target = target.path().iter().rev().next().unwrap().to_str().unwrap().to_owned(); + let target = target.file_name().into_string().unwrap(); if target == "etc" { continue; } From 06d1780b852479d90b8b813bfc27bcf6431f999d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:42:04 +0200 Subject: [PATCH 0377/1096] fix travis --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f88f6636504a..df0da0f433996 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,7 @@ before_script: script: - set -e - travis-cargo build -- RUST_SYSROOT=$HOME/rust -- travis-cargo test +- env RUST_SYSROOT=$HOME/rust travis-cargo test notifications: email: on_success: never From 6a5f7378c3d4145f56e757ca76aff4bb4fa4b5f0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:44:59 +0200 Subject: [PATCH 0378/1096] travis didn't fail when compiling miri on nightly --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index df0da0f433996..177a8ac72d7cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: rust rust: - nightly -matrix: - allow_failures: - - rust: nightly before_script: - | pip install 'travis-cargo<0.2' --user && From af36ec959ad8382be7d4a7dae6c56ef2af2bb9f6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:46:43 +0200 Subject: [PATCH 0379/1096] undo all travis script changes --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 177a8ac72d7cd..cd9f665d1b3fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ before_script: pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: -- set -e -- travis-cargo build -- env RUST_SYSROOT=$HOME/rust travis-cargo test +- | + travis-cargo build && + env RUST_SYSROOT=$HOME/rust travis-cargo test notifications: email: on_success: never From b6fca7355c4acf59d177eb2029ba4a3834d078ba Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:50:23 +0200 Subject: [PATCH 0380/1096] error out if a run-pass test fails --- tests/compiletest.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index bdb7acf56b045..0cf945059c530 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -38,6 +38,7 @@ fn for_all_targets(sysroot: &str, f: F) { #[test] fn compile_test() { + let mut failed = false; // Taken from https://github.com/Manishearth/rust-clippy/pull/911. let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); @@ -69,14 +70,21 @@ fn compile_test() { match cmd.output() { Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), Ok(output) => { + failed = true; writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); } - Err(e) => writeln!(stderr.lock(), "FAILED: {}", e).unwrap(), + Err(e) => { + failed = true; + writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); + }, } } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); }) + if failed { + panic!("some tests failed"); + } } From 453a22a1e014dca717df5a4c355ab9c8d255604f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:52:23 +0200 Subject: [PATCH 0381/1096] forward `RUST_SYSROOT` to miri test calls --- tests/compiletest.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 0cf945059c530..fd86f40637b2f 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -63,6 +63,7 @@ fn compile_test() { cmd.arg(format!("--sysroot={}", sysroot)); cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); + cmd.env("RUST_SYSROOT", sysroot); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); From 2ed6f1c90a1b890e8caae2707f32ea9d0c4b07fc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 10:54:10 +0200 Subject: [PATCH 0382/1096] caught by travis --- tests/compiletest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index fd86f40637b2f..7a7de52992f38 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -20,7 +20,7 @@ fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) { }); } -fn for_all_targets(sysroot: &str, f: F) { +fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { let target = target.unwrap(); if !target.metadata().unwrap().is_dir() { @@ -63,7 +63,7 @@ fn compile_test() { cmd.arg(format!("--sysroot={}", sysroot)); cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); - cmd.env("RUST_SYSROOT", sysroot); + cmd.env("RUST_SYSROOT", &sysroot); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); @@ -84,7 +84,7 @@ fn compile_test() { } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); - }) + }); if failed { panic!("some tests failed"); } From f01be9199719ece8f643347627d8b549b375009d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 11:00:46 +0200 Subject: [PATCH 0383/1096] miri needs to be *built* with RUST_SYSROOT, not *run* --- .travis.yml | 2 +- tests/compiletest.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd9f665d1b3fb..e76a2f86cf93d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_script: export PATH=$HOME/.local/bin:$PATH script: - | - travis-cargo build && + env RUST_SYSROOT=$HOME/rust travis-cargo build && env RUST_SYSROOT=$HOME/rust travis-cargo test notifications: email: diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 7a7de52992f38..d5ea91f015178 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -63,7 +63,6 @@ fn compile_test() { cmd.arg(format!("--sysroot={}", sysroot)); cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); - cmd.env("RUST_SYSROOT", &sysroot); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); From 60f2bb9c70b89eccf432864e9bd3b448b1363086 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 16 Jun 2016 11:05:10 +0200 Subject: [PATCH 0384/1096] miri knows about `--sysroot` --- tests/compiletest.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index d5ea91f015178..66b94ad88da5a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -60,7 +60,6 @@ fn compile_test() { write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap(); let mut cmd = std::process::Command::new("target/debug/miri"); cmd.arg(file); - cmd.arg(format!("--sysroot={}", sysroot)); cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); let libs = Path::new(&sysroot).join("lib"); From 58b4fac1ceca3d75e59091b09e71a8914d6bd7e2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 13:09:20 +0200 Subject: [PATCH 0385/1096] implement overflowing ops --- src/error.rs | 21 ++++++++++++++ src/interpreter/mod.rs | 47 ++++++++++++++++++++++-------- src/lib.rs | 1 + src/primval.rs | 66 +++++++++++++++++++++++++++++++++++++----- tests/compiletest.rs | 6 ++-- 5 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/error.rs b/src/error.rs index 35a978f2d2ea0..885f5af6321cc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,9 @@ use std::fmt; use rustc::mir::repr as mir; use rustc::ty::BareFnTy; use memory::Pointer; +use rustc_const_math::ConstMathErr; +use syntax::codemap::Span; +use primval::PrimVal; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { @@ -24,6 +27,10 @@ pub enum EvalError<'tcx> { Unimplemented(String), DerefFunctionPointer, ExecuteMemory, + ArrayIndexOutOfBounds(Span, u64, u64), + Math(Span, ConstMathErr), + InvalidBitShiftRhs(PrimVal), + Overflow(PrimVal, PrimVal, mir::BinOp, PrimVal), } pub type EvalResult<'tcx, T> = Result>; @@ -58,6 +65,14 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to dereference a function pointer", EvalError::ExecuteMemory => "tried to treat a memory pointer as a function pointer", + EvalError::ArrayIndexOutOfBounds(..) => + "array index out of bounds", + EvalError::Math(..) => + "mathematical operation failed", + EvalError::InvalidBitShiftRhs(..) => + "bit shift rhs negative or not an int", + EvalError::Overflow(..) => + "mathematical operation overflowed", } } @@ -73,6 +88,12 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { }, EvalError::FunctionPointerTyMismatch(expected, got) => write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), + EvalError::ArrayIndexOutOfBounds(span, len, index) => + write!(f, "array index {} out of bounds {} at {:?}", index, len, span), + EvalError::Math(span, ref err) => + write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + EvalError::Overflow(l, r, op, val) => + write!(f, "mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 213f813529f5f..6d367a3be2ef7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -474,15 +474,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.frame_mut().block = target; } - Assert { ref cond, expected, ref msg, target, cleanup } => { - let actual_ptr = self.eval_operand(cond)?; - let actual = self.memory.read_bool(actual_ptr)?; - if actual == expected { + Assert { ref cond, expected, ref msg, target, .. } => { + let cond_ptr = self.eval_operand(cond)?; + if expected == self.memory.read_bool(cond_ptr)? { self.frame_mut().block = target; } else { - panic!("unimplemented: jump to {:?} and print {:?}", cleanup, msg); + return match *msg { + mir::AssertMessage::BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("can't eval len"); + let len = self.memory.read_usize(len).expect("can't read len"); + let index = self.eval_operand(index).expect("can't eval index"); + let index = self.memory.read_usize(index).expect("can't read index"); + Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) + }, + mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())), + } } - } + }, DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), @@ -922,13 +930,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let right_ty = self.operand_ty(right); let right_val = self.read_primval(right_ptr, right_ty)?; - let val = primval::binary_op(bin_op, left_val, right_val)?; - self.memory.write_primval(dest, val)?; + use rustc::ty::layout::Layout::*; + let tup_layout = match *dest_layout { + Univariant { ref variant, .. } => variant, + _ => panic!("checked bin op returns something other than a tuple"), + }; - // FIXME(solson): Find the result type size properly. Perhaps refactor out - // Projection calculations so we can do the equivalent of `dest.1` here. - let s = self.type_size(left_ty); - self.memory.write_bool(dest.offset(s as isize), false)?; + match primval::binary_op(bin_op, left_val, right_val) { + Ok(val) => { + let offset = tup_layout.field_offset(0).bytes() as isize; + self.memory.write_primval(dest.offset(offset), val)?; + let offset = tup_layout.field_offset(1).bytes() as isize; + self.memory.write_bool(dest.offset(offset), false)?; + }, + Err(EvalError::Overflow(l, r, op, val)) => { + debug!("mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val); + let offset = tup_layout.field_offset(0).bytes() as isize; + self.memory.write_primval(dest.offset(offset), val)?; + let offset = tup_layout.field_offset(1).bytes() as isize; + self.memory.write_bool(dest.offset(offset), true)?; + }, + Err(other) => return Err(other), + } } UnaryOp(un_op, ref operand) => { diff --git a/src/lib.rs b/src/lib.rs index 4bc5a07e3c2f9..c881e79e6145d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ extern crate rustc_data_structures; extern crate rustc_mir; extern crate rustc_trans; +extern crate rustc_const_math; extern crate syntax; #[macro_use] extern crate log; extern crate log_settings; diff --git a/src/primval.rs b/src/primval.rs index 5e1cdac45a1fb..b1900874a9a79 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -17,21 +17,32 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva use rustc::mir::repr::BinOp::*; use self::PrimVal::*; + macro_rules! overflow { + ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({ + let (val, of) = $l.$op($r); + if of { + return Err(EvalError::Overflow($v($l), $v2($r), bin_op, $v(val))); + } else { + $v(val) + } + }) + } + macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ match bin_op { - Add => $v($l + $r), - Sub => $v($l - $r), - Mul => $v($l * $r), - Div => $v($l / $r), - Rem => $v($l % $r), + Add => overflow!($v, $v, $l, overflowing_add, $r), + Sub => overflow!($v, $v, $l, overflowing_sub, $r), + Mul => overflow!($v, $v, $l, overflowing_mul, $r), + Div => overflow!($v, $v, $l, overflowing_div, $r), + Rem => overflow!($v, $v, $l, overflowing_rem, $r), BitXor => $v($l ^ $r), BitAnd => $v($l & $r), BitOr => $v($l | $r), - // TODO(solson): Can have differently-typed RHS. - Shl => $v($l << $r), - Shr => $v($l >> $r), + // these have already been handled + Shl => unreachable!(), + Shr => unreachable!(), Eq => Bool($l == $r), Ne => Bool($l != $r), @@ -53,6 +64,45 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva } } + match bin_op { + // can have rhs with a different numeric type + Shl | Shr => { + let r = match right { + I8(i) if i >= 0 => i as u32, + I16(i) if i >= 0 => i as u32, + I32(i) if i >= 0 => i as u32, + I64(i) if i >= 0 && i as i32 as i64 == i => i as u32, + U8(i) => i as u32, + U16(i) => i as u32, + U32(i) => i, + U64(i) if i as u32 as u64 == i => i as u32, + _ => return Err(EvalError::InvalidBitShiftRhs(right)), + }; + macro_rules! shift { + ($v:ident, $l:ident, $r:ident) => ({ + match bin_op { + Shl => overflow!($v, U32, $l, overflowing_shl, $r), + Shr => overflow!($v, U32, $l, overflowing_shr, $r), + _ => unreachable!(), + } + }) + } + let val = match left { + I8(l) => shift!(I8, l, r), + I16(l) => shift!(I16, l, r), + I32(l) => shift!(I32, l, r), + I64(l) => shift!(I64, l, r), + U8(l) => shift!(U8, l, r), + U16(l) => shift!(U16, l, r), + U32(l) => shift!(U32, l, r), + U64(l) => shift!(U64, l, r), + _ => unreachable!(), + }; + return Ok(val); + }, + _ => {}, + } + let val = match (left, right) { (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 66b94ad88da5a..6869eb1eef04c 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -79,11 +79,11 @@ fn compile_test() { writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); }, } + if failed { + panic!("some tests failed"); + } } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); }); - if failed { - panic!("some tests failed"); - } } From 3ba4f6db04487929b2eecfc645fa9f4d8c54dfea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 15:16:41 +0200 Subject: [PATCH 0386/1096] remove code repetition and fix overflowing intrinsics --- src/interpreter/mod.rs | 191 +++++++++++++++++++++-------------------- src/lib.rs | 1 - tests/compiletest.rs | 8 +- tests/run-pass/ints.rs | 15 +--- 4 files changed, 102 insertions(+), 113 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6d367a3be2ef7..be8648ee30286 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -515,9 +515,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty); + let layout = self.type_layout(ty); let ret = return_ptr.unwrap(); - self.call_intrinsic(&name, substs, args, ret, size) + self.call_intrinsic(&name, substs, args, ret, layout) } ty::FnDiverging => unimplemented!(), } @@ -667,87 +667,126 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(if not_null { nndiscr } else { 1 - nndiscr }) } + fn intrinsic_with_overflow( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + dest: Pointer, + dest_layout: &'tcx Layout, + ) -> EvalResult<'tcx, ()> { + use rustc::ty::layout::Layout::*; + let tup_layout = match *dest_layout { + Univariant { ref variant, .. } => variant, + _ => panic!("checked bin op returns something other than a tuple"), + }; + + let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; + let offset = tup_layout.field_offset(1).bytes() as isize; + self.memory.write_bool(dest.offset(offset), overflowed) + } + + fn math( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + let left_ptr = self.eval_operand(left)?; + let left_ty = self.operand_ty(left); + let left_val = self.read_primval(left_ptr, left_ty)?; + + let right_ptr = self.eval_operand(right)?; + let right_ty = self.operand_ty(right); + let right_val = self.read_primval(right_ptr, right_ty)?; + + primval::binary_op(op, left_val, right_val) + } + + fn intrinsic_overflowing( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + dest: Pointer, + ) -> EvalResult<'tcx, bool> { + match self.math(op, left, right) { + Ok(val) => { + self.memory.write_primval(dest, val)?; + Ok(false) + }, + Err(EvalError::Overflow(l, r, op, val)) => { + debug!("operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val); + self.memory.write_primval(dest, val)?; + Ok(true) + }, + Err(other) => Err(other), + } + } + fn call_intrinsic( &mut self, name: &str, substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], dest: Pointer, - dest_size: usize + dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); - let args = args_res?; + let args_ptrs = args_res?; + + let pointer_size = self.memory.pointer_size; match name { - // FIXME(solson): Handle different integer types correctly. - "add_with_overflow" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); - let left = self.memory.read_int(args[0], size)?; - let right = self.memory.read_int(args[1], size)?; - let (n, overflowed) = unsafe { - ::std::intrinsics::add_with_overflow::(left, right) - }; - self.memory.write_int(dest, n, size)?; - self.memory.write_bool(dest.offset(size as isize), overflowed)?; - } + "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, + "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, + "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, + // FIXME: turn into an assertion to catch wrong `assume` that would cause UB in llvm "assume" => {} "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.type_size(elem_ty); - let src = self.memory.read_ptr(args[0])?; - let dest = self.memory.read_ptr(args[1])?; - let count = self.memory.read_isize(args[2])?; + let src = self.memory.read_ptr(args_ptrs[0])?; + let dest = self.memory.read_ptr(args_ptrs[1])?; + let count = self.memory.read_isize(args_ptrs[2])?; self.memory.copy(src, dest, count as usize * elem_size)?; } "discriminant_value" => { let ty = *substs.types.get(subst::FnSpace, 0); - let adt_ptr = self.memory.read_ptr(args[0])?; + let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.memory.write_uint(dest, discr_val, dest_size)?; + self.memory.write_uint(dest, discr_val, 8)?; } "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); let arg_size = self.type_size(arg_ty); - self.memory.drop_fill(args[0], arg_size)?; + self.memory.drop_fill(args_ptrs[0], arg_size)?; } - "init" => self.memory.write_repeat(dest, 0, dest_size)?, + "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - self.memory.write_int(dest, 1, dest_size)?; + // FIXME: use correct value + self.memory.write_int(dest, 1, pointer_size)?; } "move_val_init" => { let ty = *substs.types.get(subst::FnSpace, 0); - let ptr = self.memory.read_ptr(args[0])?; - self.move_(args[1], ptr, ty)?; - } - - // FIXME(solson): Handle different integer types correctly. - "mul_with_overflow" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); - let left = self.memory.read_int(args[0], size)?; - let right = self.memory.read_int(args[1], size)?; - let (n, overflowed) = unsafe { - ::std::intrinsics::mul_with_overflow::(left, right) - }; - self.memory.write_int(dest, n, size)?; - self.memory.write_bool(dest.offset(size as isize), overflowed)?; + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], ptr, ty)?; } "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args[0]; - let offset = self.memory.read_isize(args[1])?; + let ptr_arg = args_ptrs[0]; + let offset = self.memory.read_isize(args_ptrs[1])?; match self.memory.read_ptr(ptr_arg) { Ok(ptr) => { @@ -763,35 +802,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); - let left = self.memory.read_int(args[0], size)?; - let right = self.memory.read_int(args[1], size)?; - let n = left.wrapping_sub(right); - self.memory.write_int(dest, n, size)?; + self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; + } + "overflowing_mul" => { + self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; + } + "overflowing_add" => { + self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; } "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, dest_size)?; + self.memory.write_uint(dest, size, pointer_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, dest_size)?; + self.memory.write_uint(dest, size, pointer_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty) as u64; let ptr_size = self.memory.pointer_size as isize; - let n = self.memory.read_usize(args[0].offset(ptr_size))?; - self.memory.write_uint(dest, n * elem_size, dest_size)?; + let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; + self.memory.write_uint(dest, n * elem_size, pointer_size)?; } _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), @@ -801,9 +840,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let ty = *substs.types.get(subst::FnSpace, 0); - self.move_(args[0], dest, ty)?; + self.move_(args_ptrs[0], dest, ty)?; } - "uninit" => self.memory.mark_definedness(dest, dest_size, false)?, + "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), } @@ -908,50 +947,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } BinaryOp(bin_op, ref left, ref right) => { - let left_ptr = self.eval_operand(left)?; - let left_ty = self.operand_ty(left); - let left_val = self.read_primval(left_ptr, left_ty)?; - - let right_ptr = self.eval_operand(right)?; - let right_ty = self.operand_ty(right); - let right_val = self.read_primval(right_ptr, right_ty)?; - - let val = primval::binary_op(bin_op, left_val, right_val)?; - self.memory.write_primval(dest, val)?; + let result = self.math(bin_op, left, right)?; + self.memory.write_primval(dest, result)?; } - // FIXME(solson): Factor this out with BinaryOp. CheckedBinaryOp(bin_op, ref left, ref right) => { - let left_ptr = self.eval_operand(left)?; - let left_ty = self.operand_ty(left); - let left_val = self.read_primval(left_ptr, left_ty)?; - - let right_ptr = self.eval_operand(right)?; - let right_ty = self.operand_ty(right); - let right_val = self.read_primval(right_ptr, right_ty)?; - - use rustc::ty::layout::Layout::*; - let tup_layout = match *dest_layout { - Univariant { ref variant, .. } => variant, - _ => panic!("checked bin op returns something other than a tuple"), - }; - - match primval::binary_op(bin_op, left_val, right_val) { - Ok(val) => { - let offset = tup_layout.field_offset(0).bytes() as isize; - self.memory.write_primval(dest.offset(offset), val)?; - let offset = tup_layout.field_offset(1).bytes() as isize; - self.memory.write_bool(dest.offset(offset), false)?; - }, - Err(EvalError::Overflow(l, r, op, val)) => { - debug!("mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val); - let offset = tup_layout.field_offset(0).bytes() as isize; - self.memory.write_primval(dest.offset(offset), val)?; - let offset = tup_layout.field_offset(1).bytes() as isize; - self.memory.write_bool(dest.offset(offset), true)?; - }, - Err(other) => return Err(other), - } + self.intrinsic_with_overflow(bin_op, left, right, dest, dest_layout)?; } UnaryOp(un_op, ref operand) => { diff --git a/src/lib.rs b/src/lib.rs index c881e79e6145d..c983a3f716345 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ btree_range, collections, collections_bound, - core_intrinsics, filling_drop, question_mark, rustc_private, diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 6869eb1eef04c..b6c46ba4b00bc 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -38,7 +38,6 @@ fn for_all_targets(sysroot: &str, mut f: F) { #[test] fn compile_test() { - let mut failed = false; // Taken from https://github.com/Manishearth/rust-clippy/pull/911. let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); @@ -69,19 +68,16 @@ fn compile_test() { match cmd.output() { Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), Ok(output) => { - failed = true; writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); + panic!("some tests failed"); } Err(e) => { - failed = true; writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); + panic!("some tests failed"); }, } - if failed { - panic!("some tests failed"); - } } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); diff --git a/tests/run-pass/ints.rs b/tests/run-pass/ints.rs index 76091be3581ec..4f23b5ec9c381 100644 --- a/tests/run-pass/ints.rs +++ b/tests/run-pass/ints.rs @@ -1,34 +1,25 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn ret() -> i64 { 1 } -#[miri_run] fn neg() -> i64 { -1 } -#[miri_run] fn add() -> i64 { 1 + 2 } -#[miri_run] fn indirect_add() -> i64 { let x = 1; let y = 2; x + y } -#[miri_run] fn arith() -> i32 { 3*3 + 4*4 } -#[miri_run] fn match_int() -> i16 { let n = 2; match n { @@ -40,7 +31,6 @@ fn match_int() -> i16 { } } -#[miri_run] fn match_int_range() -> i64 { let n = 42; match n { @@ -53,7 +43,6 @@ fn match_int_range() -> i64 { } } -#[miri_run] fn main() { assert_eq!(ret(), 1); assert_eq!(neg(), -1); @@ -62,4 +51,8 @@ fn main() { assert_eq!(arith(), 5*5); assert_eq!(match_int(), 20); assert_eq!(match_int_range(), 4); + assert_eq!(i64::min_value().overflowing_mul(-1), (i64::min_value(), true)); + assert_eq!(i32::min_value().overflowing_mul(-1), (i32::min_value(), true)); + assert_eq!(i16::min_value().overflowing_mul(-1), (i16::min_value(), true)); + assert_eq!(i8::min_value().overflowing_mul(-1), (i8::min_value(), true)); } From 6376ef42286d83aed3dbb8778db98abf1ef0a39a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 15:21:01 +0200 Subject: [PATCH 0387/1096] run the *compiled* run-pass tests on the host machine --- tests/compiletest.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index b6c46ba4b00bc..e57869766bec3 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,23 +3,32 @@ extern crate compiletest_rs as compiletest; use std::path::{PathBuf, Path}; use std::io::Write; -fn run_mode(dir: &'static str, mode: &'static str, sysroot: &str) { +fn compile_fail(sysroot: &str) { // Disable rustc's new error fomatting. It breaks these tests. std::env::remove_var("RUST_NEW_ERROR_FORMAT"); let flags = format!("--sysroot {} -Dwarnings", sysroot); for_all_targets(sysroot, |target| { let mut config = compiletest::default_config(); config.host_rustcflags = Some(flags.clone()); - config.mode = mode.parse().expect("Invalid mode"); + config.mode = "compile-fail".parse().expect("Invalid mode"); config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib"); config.rustc_path = "target/debug/miri".into(); - config.src_base = PathBuf::from(format!("tests/{}", dir)); + config.src_base = PathBuf::from("tests/compile-fail".to_string()); config.target = target.to_owned(); config.target_rustcflags = Some(flags.clone()); compiletest::run_tests(&config); }); } +fn run_pass() { + // Disable rustc's new error fomatting. It breaks these tests. + std::env::remove_var("RUST_NEW_ERROR_FORMAT"); + let mut config = compiletest::default_config(); + config.mode = "run-pass".parse().expect("Invalid mode"); + config.src_base = PathBuf::from("tests/run-pass".to_string()); + compiletest::run_tests(&config); +} + fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { let target = target.unwrap(); @@ -47,7 +56,8 @@ fn compile_test() { .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") .to_owned(), }; - run_mode("compile-fail", "compile-fail", &sysroot); + compile_fail(&sysroot); + run_pass(); for_all_targets(&sysroot, |target| { for file in std::fs::read_dir("tests/run-pass").unwrap() { let file = file.unwrap(); From 4f48bef8969120da73154ceb7bf16aa07dc94657 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 15:48:15 +0200 Subject: [PATCH 0388/1096] cfail test for std::env::args() --- tests/compile-fail/env_args.rs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/compile-fail/env_args.rs diff --git a/tests/compile-fail/env_args.rs b/tests/compile-fail/env_args.rs new file mode 100644 index 0000000000000..b0068785e17ec --- /dev/null +++ b/tests/compile-fail/env_args.rs @@ -0,0 +1,6 @@ +//error-pattern: no mir for `std + +fn main() { + let x = std::env::args(); + assert_eq!(x.count(), 1); +} From e3a2bf84e26f0be966297d75b47b9d121feb3bf5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 16:03:11 +0200 Subject: [PATCH 0389/1096] clippy --- src/interpreter/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index be8648ee30286..abc695de4dcdd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1127,9 +1127,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Hack to support fat pointer -> thin pointer casts to keep tests for // other things passing for now. - let is_fat_ptr_cast = pointee_type(src_ty).map(|ty| { - !self.type_is_sized(ty) - }).unwrap_or(false); + let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); if dest_size == src_size || is_fat_ptr_cast { self.memory.copy(src, dest, dest_size)?; From 00eb198a82376eeb608c32e6d4252743f6dcfc87 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 17 Jun 2016 16:49:06 +0200 Subject: [PATCH 0390/1096] implement fn -> unsafe fn pointer casts --- src/interpreter/mod.rs | 11 ++++++++++- tests/compile-fail/cast_fn_ptr_unsafe.rs | 10 ++++++++++ tests/compile-fail/cast_fn_ptr_unsafe2.rs | 10 ++++++++++ tests/run-pass/cast_fn_ptr_unsafe.rs | 8 ++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/cast_fn_ptr_unsafe.rs create mode 100644 tests/compile-fail/cast_fn_ptr_unsafe2.rs create mode 100644 tests/run-pass/cast_fn_ptr_unsafe.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index abc695de4dcdd..7ae08675226a8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1144,7 +1144,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ref other => panic!("reify fn pointer on {:?}", other), }, - _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), + UnsafeFnPointer => match dest_ty.sty { + ty::TyFnPtr(unsafe_fn_ty) => { + let src = self.eval_operand(operand)?; + let ptr = self.memory.read_ptr(src)?; + let fn_def = self.memory.get_fn(ptr.alloc_id)?; + let fn_ptr = self.memory.create_fn_ptr(fn_def.def_id, fn_def.substs, unsafe_fn_ty); + self.memory.write_ptr(dest, fn_ptr)?; + }, + ref other => panic!("fn to unsafe fn cast on {:?}", other), + }, } } diff --git a/tests/compile-fail/cast_fn_ptr_unsafe.rs b/tests/compile-fail/cast_fn_ptr_unsafe.rs new file mode 100644 index 0000000000000..225cd1391bc36 --- /dev/null +++ b/tests/compile-fail/cast_fn_ptr_unsafe.rs @@ -0,0 +1,10 @@ +// just making sure that fn -> unsafe fn casts are handled by rustc so miri doesn't have to +fn main() { + fn f() {} + + let g = f as fn() as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `unsafe fn(i32)` + + unsafe { + g(42); + } +} diff --git a/tests/compile-fail/cast_fn_ptr_unsafe2.rs b/tests/compile-fail/cast_fn_ptr_unsafe2.rs new file mode 100644 index 0000000000000..c3a2fb9556f82 --- /dev/null +++ b/tests/compile-fail/cast_fn_ptr_unsafe2.rs @@ -0,0 +1,10 @@ +// just making sure that fn -> unsafe fn casts are handled by rustc so miri doesn't have to +fn main() { + fn f() {} + + let g = f as fn() as fn(i32) as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `fn(i32)` + + unsafe { + g(42); + } +} diff --git a/tests/run-pass/cast_fn_ptr_unsafe.rs b/tests/run-pass/cast_fn_ptr_unsafe.rs new file mode 100644 index 0000000000000..0cabb369bfdd9 --- /dev/null +++ b/tests/run-pass/cast_fn_ptr_unsafe.rs @@ -0,0 +1,8 @@ +fn main() { + fn f() {} + + let g = f as fn() as unsafe fn(); + unsafe { + g(); + } +} From 5ae4a0f2a9175bbd339795623214aaa89db1f5a5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 17 Jun 2016 19:48:45 -0600 Subject: [PATCH 0391/1096] Only indent trace logs. --- src/bin/miri.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 7c64ad050daf0..f2b04b19c9dd9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -106,15 +106,22 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { fn init_logger() { const NSPACES: usize = 40; let format = |record: &log::LogRecord| { - // prepend spaces to indent the final string - let indentation = log_settings::settings().indentation; - format!("{lvl}:{module}{depth:2}{indent: Date: Fri, 17 Jun 2016 19:55:24 -0600 Subject: [PATCH 0392/1096] Rename max indentation constant for clarity. --- src/bin/miri.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f2b04b19c9dd9..82b1da3ce6e35 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -104,7 +104,8 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { } fn init_logger() { - const NSPACES: usize = 40; + const MAX_INDENT: usize = 40; + let format = |record: &log::LogRecord| { if record.level() == log::LogLevel::Trace { // prepend spaces to indent the final string @@ -112,8 +113,8 @@ fn init_logger() { format!("{lvl}:{module}{depth:2}{indent: Date: Fri, 17 Jun 2016 21:06:20 -0600 Subject: [PATCH 0393/1096] Ignore non-Rust files in run-pass. Specifically, Vim's ..swp files were being run as tests. --- tests/compiletest.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 66b94ad88da5a..2f503f7a81103 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -52,20 +52,23 @@ fn compile_test() { for_all_targets(&sysroot, |target| { for file in std::fs::read_dir("tests/run-pass").unwrap() { let file = file.unwrap(); - if !file.metadata().unwrap().is_file() { + let path = file.path(); + + if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { continue; } - let file = file.path(); + let stderr = std::io::stderr(); - write!(stderr.lock(), "test [miri-pass] {} ", file.to_str().unwrap()).unwrap(); + write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); let mut cmd = std::process::Command::new("target/debug/miri"); - cmd.arg(file); + cmd.arg(path); cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); cmd.env(compiletest::procsrv::dylib_env_var(), paths); + match cmd.output() { Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), Ok(output) => { From f4cf3f36364c0234d50a2648bf3e63980bdfc716 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 17 Jun 2016 21:35:37 -0600 Subject: [PATCH 0394/1096] Get benchmarks running again and factor out some parts in common with bin/miri.rs. --- benches/miri_helper.rs | 39 +++++++++++++++++------ src/bin/miri.rs | 71 +++--------------------------------------- src/interpreter/mod.rs | 62 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +-- 4 files changed, 99 insertions(+), 78 deletions(-) diff --git a/benches/miri_helper.rs b/benches/miri_helper.rs index e085373a36dc0..1b4b14c427f04 100644 --- a/benches/miri_helper.rs +++ b/benches/miri_helper.rs @@ -8,21 +8,36 @@ extern crate rustc; extern crate rustc_driver; extern crate test; -use self::miri::interpreter; +use self::miri::eval_main; use self::rustc::session::Session; use self::rustc_driver::{driver, CompilerCalls, Compilation}; use std::cell::RefCell; use std::rc::Rc; -use std::env::var; use test::Bencher; pub struct MiriCompilerCalls<'a>(Rc>); +fn find_sysroot() -> String { + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + } +} + pub fn run(filename: &str, bencher: &mut Bencher) { - let path = var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - rustc_driver::run_compiler(&[ - "miri".to_string(), format!("benches/{}.rs", filename), "--sysroot".to_string(), path.to_string(), - ], &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher)))); + let args = &[ + "miri".to_string(), + format!("benches/{}.rs", filename), + "--sysroot".to_string(), + find_sysroot() + ]; + let compiler_calls = &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher))); + rustc_driver::run_compiler(args, compiler_calls); } impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { @@ -38,9 +53,15 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(move |state| { state.session.abort_if_errors(); - bencher.borrow_mut().iter(|| { - interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); - }) + + let tcx = state.tcx.unwrap(); + let mir_map = state.mir_map.unwrap(); + let (node_id, _) = state.session.entry_fn.borrow() + .expect("no main or start function found"); + + bencher.borrow_mut().iter(|| { eval_main(tcx, mir_map, node_id); }); + + state.session.abort_if_errors(); }); control diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 82b1da3ce6e35..6a9ad5ce8387f 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -9,17 +9,9 @@ extern crate log_settings; extern crate syntax; #[macro_use] extern crate log; -use miri::{ - EvalContext, - CachedMir, - step, - EvalError, - Frame, -}; +use miri::eval_main; use rustc::session::Session; use rustc_driver::{driver, CompilerCalls, Compilation}; -use rustc::ty::{TyCtxt, subst}; -use rustc::hir::def_id::DefId; struct MiriCompilerCalls; @@ -37,40 +29,10 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let tcx = state.tcx.unwrap(); let mir_map = state.mir_map.unwrap(); + let (node_id, _) = state.session.entry_fn.borrow() + .expect("no main or start function found"); + eval_main(tcx, mir_map, node_id); - let (node_id, span) = state.session.entry_fn.borrow().expect("no main or start function found"); - debug!("found `main` function at: {:?}", span); - - let mir = mir_map.map.get(&node_id).expect("no mir for main function"); - let def_id = tcx.map.local_def_id(node_id); - let mut ecx = EvalContext::new(tcx, mir_map); - let substs = tcx.mk_substs(subst::Substs::empty()); - let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging"); - - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)); - - if mir.arg_decls.len() == 2 { - // start function - let ptr_size = ecx.memory().pointer_size; - let nargs = ecx.memory_mut().allocate(ptr_size); - ecx.memory_mut().write_usize(nargs, 0).unwrap(); - let args = ecx.memory_mut().allocate(ptr_size); - ecx.memory_mut().write_usize(args, 0).unwrap(); - ecx.frame_mut().locals[0] = nargs; - ecx.frame_mut().locals[1] = args; - } - - loop { - match step(&mut ecx) { - Ok(true) => {} - Ok(false) => break, - // FIXME: diverging functions can end up here in some future miri - Err(e) => { - report(tcx, &ecx, e); - break; - } - } - } state.session.abort_if_errors(); }); @@ -78,31 +40,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } -fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { - let frame = ecx.stack().last().expect("stackframe was empty"); - let block = &frame.mir.basic_blocks()[frame.block]; - let span = if frame.stmt < block.statements.len() { - block.statements[frame.stmt].source_info.span - } else { - block.terminator().source_info.span - }; - let mut err = tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| Some(tcx.lookup_item_type(self.0).generics)) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); - } - err.emit(); -} - fn init_logger() { const MAX_INDENT: usize = 40; diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 213f813529f5f..6cad67cf6e0cc 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1507,3 +1507,65 @@ impl StructExt for layout::Struct { } } } + +pub fn eval_main<'a, 'tcx: 'a>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir_map: &'a MirMap<'tcx>, + node_id: ast::NodeId, +) { + let mir = mir_map.map.get(&node_id).expect("no mir for main function"); + let def_id = tcx.map.local_def_id(node_id); + let mut ecx = EvalContext::new(tcx, mir_map); + let substs = tcx.mk_substs(subst::Substs::empty()); + let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging"); + + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)); + + if mir.arg_decls.len() == 2 { + // start function + let ptr_size = ecx.memory().pointer_size; + let nargs = ecx.memory_mut().allocate(ptr_size); + ecx.memory_mut().write_usize(nargs, 0).unwrap(); + let args = ecx.memory_mut().allocate(ptr_size); + ecx.memory_mut().write_usize(args, 0).unwrap(); + ecx.frame_mut().locals[0] = nargs; + ecx.frame_mut().locals[1] = args; + } + + loop { + match step(&mut ecx) { + Ok(true) => {} + Ok(false) => break, + // FIXME: diverging functions can end up here in some future miri + Err(e) => { + report(tcx, &ecx, e); + break; + } + } + } +} + +fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { + let frame = ecx.stack().last().expect("stackframe was empty"); + let block = &frame.mir.basic_blocks()[frame.block]; + let span = if frame.stmt < block.statements.len() { + block.statements[frame.stmt].source_info.span + } else { + block.terminator().source_info.span + }; + let mut err = tcx.sess.struct_span_err(span, &e.to_string()); + for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { + // FIXME(solson): Find a way to do this without this Display impl hack. + use rustc::util::ppaux; + use std::fmt; + struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); + impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], + |tcx| Some(tcx.lookup_item_type(self.0).generics)) + } + } + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + } + err.emit(); +} diff --git a/src/lib.rs b/src/lib.rs index 4bc5a07e3c2f9..ca43aea45c951 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,10 +32,11 @@ pub use error::{ }; pub use interpreter::{ + CachedMir, EvalContext, - step, Frame, - CachedMir, + eval_main, + step, }; pub use memory::Memory; From c6ec4f43978a48b844ea3f9e53b822788d3ff394 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 17 Jun 2016 22:35:34 -0600 Subject: [PATCH 0395/1096] Reorganize benches. --- benches/fibonacci.rs | 12 +++--------- benches/{ => helpers}/fibonacci_helper.rs | 0 benches/{ => helpers}/fibonacci_helper_iterative.rs | 0 benches/{ => helpers}/miri_helper.rs | 6 +----- benches/helpers/mod.rs | 7 +++++++ benches/{ => helpers}/smoke_helper.rs | 0 benches/smoke.rs | 10 +++------- 7 files changed, 14 insertions(+), 21 deletions(-) rename benches/{ => helpers}/fibonacci_helper.rs (100%) rename benches/{ => helpers}/fibonacci_helper_iterative.rs (100%) rename benches/{ => helpers}/miri_helper.rs (93%) create mode 100644 benches/helpers/mod.rs rename benches/{ => helpers}/smoke_helper.rs (100%) diff --git a/benches/fibonacci.rs b/benches/fibonacci.rs index 1f8a2aafc9ed9..39974ad6c18ea 100644 --- a/benches/fibonacci.rs +++ b/benches/fibonacci.rs @@ -1,11 +1,9 @@ -#![feature(custom_attribute, test)] -#![feature(rustc_private)] -#![allow(unused_attributes)] +#![feature(test, rustc_private)] extern crate test; use test::Bencher; - -mod fibonacci_helper; +mod helpers; +use helpers::*; #[bench] fn fib(bencher: &mut Bencher) { @@ -14,15 +12,11 @@ fn fib(bencher: &mut Bencher) { }) } -mod miri_helper; - #[bench] fn fib_miri(bencher: &mut Bencher) { miri_helper::run("fibonacci_helper", bencher); } -mod fibonacci_helper_iterative; - #[bench] fn fib_iter(bencher: &mut Bencher) { bencher.iter(|| { diff --git a/benches/fibonacci_helper.rs b/benches/helpers/fibonacci_helper.rs similarity index 100% rename from benches/fibonacci_helper.rs rename to benches/helpers/fibonacci_helper.rs diff --git a/benches/fibonacci_helper_iterative.rs b/benches/helpers/fibonacci_helper_iterative.rs similarity index 100% rename from benches/fibonacci_helper_iterative.rs rename to benches/helpers/fibonacci_helper_iterative.rs diff --git a/benches/miri_helper.rs b/benches/helpers/miri_helper.rs similarity index 93% rename from benches/miri_helper.rs rename to benches/helpers/miri_helper.rs index 1b4b14c427f04..c8ae22e1ace57 100644 --- a/benches/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -1,7 +1,3 @@ -#![feature(custom_attribute, test)] -#![feature(rustc_private)] -#![allow(unused_attributes)] - extern crate getopts; extern crate miri; extern crate rustc; @@ -32,7 +28,7 @@ fn find_sysroot() -> String { pub fn run(filename: &str, bencher: &mut Bencher) { let args = &[ "miri".to_string(), - format!("benches/{}.rs", filename), + format!("benches/helpers/{}.rs", filename), "--sysroot".to_string(), find_sysroot() ]; diff --git a/benches/helpers/mod.rs b/benches/helpers/mod.rs new file mode 100644 index 0000000000000..27504a2cc034d --- /dev/null +++ b/benches/helpers/mod.rs @@ -0,0 +1,7 @@ +// This module gets included in multiple crates, and they each only use part of it. +#![allow(dead_code)] + +pub mod fibonacci_helper; +pub mod fibonacci_helper_iterative; +pub mod miri_helper; +pub mod smoke_helper; diff --git a/benches/smoke_helper.rs b/benches/helpers/smoke_helper.rs similarity index 100% rename from benches/smoke_helper.rs rename to benches/helpers/smoke_helper.rs diff --git a/benches/smoke.rs b/benches/smoke.rs index 43baf486df397..eabd58a86889f 100644 --- a/benches/smoke.rs +++ b/benches/smoke.rs @@ -1,11 +1,9 @@ -#![feature(custom_attribute, test)] -#![feature(rustc_private)] -#![allow(unused_attributes)] +#![feature(test, rustc_private)] extern crate test; use test::Bencher; - -mod smoke_helper; +mod helpers; +use helpers::*; #[bench] fn noop(bencher: &mut Bencher) { @@ -33,8 +31,6 @@ fn noop_miri_full(bencher: &mut Bencher) { } */ -mod miri_helper; - #[bench] fn noop_miri_interpreter(bencher: &mut Bencher) { miri_helper::run("smoke_helper", bencher); From f9c1cfa88910a61171bf09fc21867623439043cc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 17 Jun 2016 22:52:24 -0600 Subject: [PATCH 0396/1096] Remove now-useless #[miri_run] attributes. Except for `ints.rs`, which is already handled by a pending pull request. --- tests/run-pass/closures.rs | 9 ++------- tests/run-pass/constants.rs | 8 +++----- tests/run-pass/option_box_transmute_ptr.rs | 9 +++------ tests/run-pass/products.rs | 9 --------- tests/run-pass/specialization.rs | 5 ----- tests/run-pass/strings.rs | 16 +++++++--------- tests/run-pass/sums.rs | 4 ++-- tests/run-pass/trivial.rs | 10 ++++------ tests/run-pass/zst.rs | 11 +++++------ 9 files changed, 26 insertions(+), 55 deletions(-) diff --git a/tests/run-pass/closures.rs b/tests/run-pass/closures.rs index ca35992225ecc..e0b2c24c83574 100644 --- a/tests/run-pass/closures.rs +++ b/tests/run-pass/closures.rs @@ -1,14 +1,9 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn simple() -> i32 { let y = 10; let f = |x| x + y; f(2) } -#[miri_run] fn crazy_closure() -> (i32, i32, i32) { fn inner(t: T) -> (i32, T, T) { struct NonCopy; @@ -26,7 +21,7 @@ fn crazy_closure() -> (i32, i32, i32) { inner(10) } -// #[miri_run] +// TODO(solson): Implement closure argument adjustment and uncomment this test. // fn closure_arg_adjustment_problem() -> i64 { // fn once(f: F) { f(2); } // let mut y = 1; @@ -37,7 +32,7 @@ fn crazy_closure() -> (i32, i32, i32) { // y // } -#[miri_run] fn main() { assert_eq!(simple(), 12); + assert_eq!(crazy_closure(), (84, 10, 10)); } diff --git a/tests/run-pass/constants.rs b/tests/run-pass/constants.rs index 3d6b08c340df5..718c852601420 100644 --- a/tests/run-pass/constants.rs +++ b/tests/run-pass/constants.rs @@ -1,11 +1,9 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - const A: usize = *&5; -#[miri_run] fn foo() -> usize { A } -fn main() {} +fn main() { + assert_eq!(foo(), A); +} diff --git a/tests/run-pass/option_box_transmute_ptr.rs b/tests/run-pass/option_box_transmute_ptr.rs index 55beb11edd449..c81db8e8b2354 100644 --- a/tests/run-pass/option_box_transmute_ptr.rs +++ b/tests/run-pass/option_box_transmute_ptr.rs @@ -1,9 +1,4 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - // This tests that the size of Option> is the same as *const i32. - -#[miri_run] fn option_box_deref() -> i32 { let val = Some(Box::new(42)); unsafe { @@ -12,4 +7,6 @@ fn option_box_deref() -> i32 { } } -fn main() {} +fn main() { + assert_eq!(option_box_deref(), 42); +} diff --git a/tests/run-pass/products.rs b/tests/run-pass/products.rs index 68a1b429438e0..86bb71a0be560 100644 --- a/tests/run-pass/products.rs +++ b/tests/run-pass/products.rs @@ -1,17 +1,11 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn tuple() -> (i16,) { (1,) } -#[miri_run] fn tuple_2() -> (i16, i16) { (1, 2) } -#[miri_run] fn tuple_5() -> (i16, i16, i16, i16, i16) { (1, 2, 3, 4, 5) } @@ -19,19 +13,16 @@ fn tuple_5() -> (i16, i16, i16, i16, i16) { #[derive(Debug, PartialEq)] struct Pair { x: i8, y: i8 } -#[miri_run] fn pair() -> Pair { Pair { x: 10, y: 20 } } -#[miri_run] fn field_access() -> (i8, i8) { let mut p = Pair { x: 10, y: 20 }; p.x += 5; (p.x, p.y) } -#[miri_run] fn main() { assert_eq!(tuple(), (1,)); assert_eq!(tuple_2(), (1, 2)); diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index 4b5f510ad4409..9570b5b58e82f 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -1,6 +1,3 @@ -#![feature(custom_attribute, specialization)] -#![allow(dead_code, unused_attributes)] - trait IsUnit { fn is_unit() -> bool; } @@ -13,12 +10,10 @@ impl IsUnit for () { fn is_unit() -> bool { true } } -#[miri_run] fn specialization() -> (bool, bool) { (i32::is_unit(), <()>::is_unit()) } -#[miri_run] fn main() { assert_eq!(specialization(), (false, true)); } diff --git a/tests/run-pass/strings.rs b/tests/run-pass/strings.rs index 8092335116988..d5fc80b41f012 100644 --- a/tests/run-pass/strings.rs +++ b/tests/run-pass/strings.rs @@ -1,29 +1,27 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn empty() -> &'static str { "" } -#[miri_run] fn hello() -> &'static str { "Hello, world!" } -#[miri_run] fn hello_bytes() -> &'static [u8; 13] { b"Hello, world!" } -#[miri_run] fn hello_bytes_fat() -> &'static [u8] { b"Hello, world!" } -#[miri_run] fn fat_pointer_on_32_bit() { Some(5).expect("foo"); } -fn main() {} +fn main() { + assert_eq!(empty(), ""); + assert_eq!(hello(), "Hello, world!"); + assert_eq!(hello_bytes(), b"Hello, world!"); + assert_eq!(hello_bytes_fat(), b"Hello, world!"); + fat_pointer_on_32_bit(); // Should run without crashing. +} diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index adf4e8f987d32..f067b29220ce8 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -1,5 +1,5 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] +// FIXME(solson): 32-bit mode doesn't test anything currently. +#![cfg_attr(target_pointer_width = "32", allow(dead_code))] #[derive(Debug, PartialEq)] enum Unit { Unit(()) } // Force non-C-enum representation. diff --git a/tests/run-pass/trivial.rs b/tests/run-pass/trivial.rs index e9d0f694648b2..891d115206561 100644 --- a/tests/run-pass/trivial.rs +++ b/tests/run-pass/trivial.rs @@ -1,13 +1,11 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - -#[miri_run] fn empty() {} -#[miri_run] fn unit_var() { let x = (); x } -fn main() {} +fn main() { + empty(); + unit_var(); +} diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs index d4f1d4023ba7f..db98e969306ba 100644 --- a/tests/run-pass/zst.rs +++ b/tests/run-pass/zst.rs @@ -1,17 +1,16 @@ -#![feature(custom_attribute)] -#![allow(dead_code, unused_attributes)] - +#[derive(PartialEq, Debug)] struct A; -#[miri_run] fn zst_ret() -> A { A } -#[miri_run] fn use_zst() -> A { let a = A; a } -fn main() {} +fn main() { + assert_eq!(zst_ret(), A); + assert_eq!(use_zst(), A); +} From 48487f0cfc987d00252dee0e6995abaafdb57c26 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 19 Jun 2016 00:04:11 -0600 Subject: [PATCH 0397/1096] Update README. Recommend rustup. Also adds debugging info and contact info. --- README.md | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a553d046f1fdd..0d9f1b4e3da51 100644 --- a/README.md +++ b/README.md @@ -9,44 +9,54 @@ undergraduate research course at the [University of Saskatchewan][usask]. [![Build Status](https://travis-ci.org/solson/miri.svg?branch=master)](https://travis-ci.org/solson/miri) -## Download Rust nightly +## Installing Rust -I currently recommend that you install [multirust][multirust] and then use it to -install the current rustc nightly version: +I recommend that you install [rustup][rustup] and then use it to install the +current Rust nightly version: ```sh -multirust update nightly +rustup update nightly ``` -## Build +You should also make `nightly` the default version for your Miri directory by +running the following command while you're in it. If you don't do this, you can +run the later `cargo` commands by prefixing them with `rustup run nightly`. ```sh -multirust run nightly cargo build +rustup override add nightly +``` + +## Building Miri + +```sh +cargo build ``` If Miri fails to build, it's likely because a change in the latest nightly -compiler broke it. You could try an older nightly with `multirust update +compiler broke it. You could try an older nightly with `rustup update nightly-` where `` is a few days or weeks ago, e.g. `2016-05-20` for May 20th. Otherwise, you could notify me in an issue or on IRC. Or, if you know how to fix it, you could send a PR. :smile: -## Run a test +## Running tests ```sh -multirust run nightly cargo run -- \ - --sysroot $HOME/.multirust/toolchains/nightly \ - test/filename.rs +cargo run tests/run-pass/vecs.rs # Or whatever test you like. ``` -If you are using [rustup][rustup] (the name of the multirust rewrite in Rust), -the `sysroot` path will also include your build target (e.g. -`$HOME/.multirust/toolchains/nightly-x86_64-apple-darwin`). You can see the -current toolchain's directory by running `rustup which cargo` (ignoring the -trailing `/bin/cargo`). +## Debugging + +You can get detailed, statement-by-statement traces by setting the `MIRI_RUN` +environment variable to `trace`. These traces are indented based on call stack +depth. You can get a much less verbose set of information with other logging +levels such as `warn`. + +## Contributing and getting help -If you installed without using multirust or rustup, you'll need to adjust the -command to run your cargo and set the `sysroot` to the directory where your -Rust compiler is installed (`$sysroot/bin/rustc` should be a valid path). +Check out the issues on this GitHub repository for some ideas. There's lots that +needs to be done that I haven't documented in the issues yet, however. For more +ideas or help with running or hacking on Miri, you can contact me (`scott`) on +Mozilla IRC in any of the Rust IRC channels (`#rust`, `#rust-offtopic`, etc).` ## License @@ -65,5 +75,4 @@ additional terms or conditions. [rust]: https://www.rust-lang.org/ [mir]: https://github.com/rust-lang/rfcs/blob/master/text/1211-mir.md [usask]: https://www.usask.ca/ -[multirust]: https://github.com/brson/multirust [rustup]: https://www.rustup.rs From eeb30dbc9758cc3a9f47d30be831777d0afde0b3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 19 Jun 2016 00:08:43 -0600 Subject: [PATCH 0398/1096] Try moving stuff into README header. --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 0d9f1b4e3da51..794267f77c16a 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,10 @@ -# Miri +# Miri [[slides](https://solson.me/miri-slides.pdf)] [[report](https://solson.me/miri-report.pdf)] [![Build Status](https://travis-ci.org/solson/miri.svg?branch=master)](https://travis-ci.org/solson/miri) -[[slides](https://solson.me/miri-slides.pdf)] -[[report](https://solson.me/miri-report.pdf)] An experimental interpreter for [Rust][rust]'s [mid-level intermediate representation][mir] (MIR). This project began as part of my work for the undergraduate research course at the [University of Saskatchewan][usask]. -[![Build Status](https://travis-ci.org/solson/miri.svg?branch=master)](https://travis-ci.org/solson/miri) - ## Installing Rust I recommend that you install [rustup][rustup] and then use it to install the From c9d808e85f9c7ca1957ef54ddeb04d3888bd9552 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 19 Jun 2016 00:15:03 -0600 Subject: [PATCH 0399/1096] Remove stray backquote. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 794267f77c16a..5ef63b01bdfda 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ levels such as `warn`. Check out the issues on this GitHub repository for some ideas. There's lots that needs to be done that I haven't documented in the issues yet, however. For more ideas or help with running or hacking on Miri, you can contact me (`scott`) on -Mozilla IRC in any of the Rust IRC channels (`#rust`, `#rust-offtopic`, etc).` +Mozilla IRC in any of the Rust IRC channels (`#rust`, `#rust-offtopic`, etc). ## License From 874d683bfa1b916cb9e3bc57d132a62afdd411fe Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 10:34:34 +0200 Subject: [PATCH 0400/1096] improve method names and add documentation --- src/interpreter/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7ae08675226a8..0492a3dfc7a6f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -667,6 +667,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(if not_null { nndiscr } else { 1 - nndiscr }) } + /// applies the binary operation `op` to the two operands and writes a tuple of the result + /// and a boolean signifying the potential overflow to the destination fn intrinsic_with_overflow( &mut self, op: mir::BinOp, @@ -686,7 +688,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bool(dest.offset(offset), overflowed) } - fn math( + /// extracts the lhs and rhs primval from the operands and applies the binary op + fn eval_binop( &mut self, op: mir::BinOp, left: &mir::Operand<'tcx>, @@ -703,6 +706,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { primval::binary_op(op, left_val, right_val) } + /// applies the binary operation `op` to the arguments and writes the result to the destination fn intrinsic_overflowing( &mut self, op: mir::BinOp, @@ -710,7 +714,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right: &mir::Operand<'tcx>, dest: Pointer, ) -> EvalResult<'tcx, bool> { - match self.math(op, left, right) { + match self.eval_binop(op, left, right) { Ok(val) => { self.memory.write_primval(dest, val)?; Ok(false) @@ -947,7 +951,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } BinaryOp(bin_op, ref left, ref right) => { - let result = self.math(bin_op, left, right)?; + let result = self.eval_binop(bin_op, left, right)?; self.memory.write_primval(dest, result)?; } From d9776427b465283bb2368be0301bf0e5cd1459eb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 10:34:55 +0200 Subject: [PATCH 0401/1096] compiletest 2.0 uses json errors and doesn't depend on the output format anymore --- tests/compiletest.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index e57869766bec3..ffe35ad7e4e93 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -4,8 +4,6 @@ use std::path::{PathBuf, Path}; use std::io::Write; fn compile_fail(sysroot: &str) { - // Disable rustc's new error fomatting. It breaks these tests. - std::env::remove_var("RUST_NEW_ERROR_FORMAT"); let flags = format!("--sysroot {} -Dwarnings", sysroot); for_all_targets(sysroot, |target| { let mut config = compiletest::default_config(); @@ -21,8 +19,6 @@ fn compile_fail(sysroot: &str) { } fn run_pass() { - // Disable rustc's new error fomatting. It breaks these tests. - std::env::remove_var("RUST_NEW_ERROR_FORMAT"); let mut config = compiletest::default_config(); config.mode = "run-pass".parse().expect("Invalid mode"); config.src_base = PathBuf::from("tests/run-pass".to_string()); From e90ee1674ae2fd32bc5e07a3a52562c45b273c56 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 10:35:15 +0200 Subject: [PATCH 0402/1096] fix comparing of function pointers --- src/interpreter/mod.rs | 4 ++++ src/memory.rs | 26 ++++++++++++++++++-------- src/primval.rs | 14 +++++++++++++- tests/run-pass/function_pointers.rs | 2 ++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0492a3dfc7a6f..739e987a373c0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1404,6 +1404,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (8, &ty::TyUint(UintTy::Us)) | (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + (_, &ty::TyFnDef(def_id, substs, fn_ty)) => { + PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) + }, + (_, &ty::TyFnPtr(_)) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, (_, &ty::TyRef(_, ty::TypeAndMut { ty, .. })) | (_, &ty::TyRawPtr(ty::TypeAndMut { ty, .. })) => { if self.type_is_sized(ty) { diff --git a/src/memory.rs b/src/memory.rs index e20d92207dbf2..a7fecd773c907 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -42,7 +42,7 @@ impl Pointer { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] pub struct FunctionDefinition<'tcx> { pub def_id: DefId, pub substs: &'tcx Substs<'tcx>, @@ -59,6 +59,8 @@ pub struct Memory<'tcx> { /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. functions: HashMap>, + /// Inverse map of `functions` so we don't allocate a new pointer every time we need one + function_definitions: HashMap, AllocId>, next_id: AllocId, pub pointer_size: usize, } @@ -69,22 +71,29 @@ impl<'tcx> Memory<'tcx> { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), + function_definitions: HashMap::new(), next_id: AllocId(0), pointer_size: pointer_size, } } - // FIXME: never create two pointers to the same def_id + substs combination - // maybe re-use the statics cache of the EvalContext? pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { - let id = self.next_id; - debug!("creating fn ptr: {}", id); - self.next_id.0 += 1; - self.functions.insert(id, FunctionDefinition { + let def = FunctionDefinition { def_id: def_id, substs: substs, fn_ty: fn_ty, - }); + }; + if let Some(&alloc_id) = self.function_definitions.get(&def) { + return Pointer { + alloc_id: alloc_id, + offset: 0, + }; + } + let id = self.next_id; + debug!("creating fn ptr: {}", id); + self.next_id.0 += 1; + self.functions.insert(id, def); + self.function_definitions.insert(def, id); Pointer { alloc_id: id, offset: 0, @@ -361,6 +370,7 @@ impl<'tcx> Memory<'tcx> { PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), + PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), } } diff --git a/src/primval.rs b/src/primval.rs index b1900874a9a79..67ef58948fa76 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -10,6 +10,7 @@ pub enum PrimVal { U8(u8), U16(u16), U32(u32), U64(u64), AbstractPtr(Pointer), + FnPtr(Pointer), IntegerPtr(u64), } @@ -130,9 +131,20 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), - (AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) => + (AbstractPtr(_), IntegerPtr(_)) | + (IntegerPtr(_), AbstractPtr(_)) | + (FnPtr(_), AbstractPtr(_)) | + (AbstractPtr(_), FnPtr(_)) | + (FnPtr(_), IntegerPtr(_)) | + (IntegerPtr(_), FnPtr(_)) => return unrelated_ptr_ops(bin_op), + (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op { + Eq => Bool(l_ptr == r_ptr), + Ne => Bool(l_ptr != r_ptr), + _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))), + }, + (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => { if l_ptr.alloc_id != r_ptr.alloc_id { return unrelated_ptr_ops(bin_op); diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index 8361a58ea4302..2e75a5a3ea2a5 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -12,4 +12,6 @@ fn call_fn_ptr() -> i32 { fn main() { assert_eq!(call_fn_ptr(), 42); + assert!(return_fn_ptr() == f); + assert!(return_fn_ptr() as unsafe fn() -> i32 == f as fn() -> i32 as unsafe fn() -> i32); } From ed4af21605ca5939a8d45f52975632be95cd210a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 12:33:54 +0200 Subject: [PATCH 0403/1096] fix master --- tests/run-pass/specialization.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-pass/specialization.rs b/tests/run-pass/specialization.rs index 9570b5b58e82f..13894926d36db 100644 --- a/tests/run-pass/specialization.rs +++ b/tests/run-pass/specialization.rs @@ -1,3 +1,5 @@ +#![feature(specialization)] + trait IsUnit { fn is_unit() -> bool; } From b9ac85d2a94e94dd5c81d5988b0adf60c1868e0c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 16:52:43 +0200 Subject: [PATCH 0404/1096] rustc does overflow checking for us, don't duplicate it. --- src/error.rs | 5 ----- src/interpreter/mod.rs | 23 ++++++++--------------- src/primval.rs | 13 +++++++------ 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/error.rs b/src/error.rs index 885f5af6321cc..3962c09dee249 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,7 +30,6 @@ pub enum EvalError<'tcx> { ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), InvalidBitShiftRhs(PrimVal), - Overflow(PrimVal, PrimVal, mir::BinOp, PrimVal), } pub type EvalResult<'tcx, T> = Result>; @@ -71,8 +70,6 @@ impl<'tcx> Error for EvalError<'tcx> { "mathematical operation failed", EvalError::InvalidBitShiftRhs(..) => "bit shift rhs negative or not an int", - EvalError::Overflow(..) => - "mathematical operation overflowed", } } @@ -92,8 +89,6 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "array index {} out of bounds {} at {:?}", index, len, span), EvalError::Math(span, ref err) => write!(f, "mathematical operation at {:?} failed with {:?}", span, err), - EvalError::Overflow(l, r, op, val) => - write!(f, "mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index eaadfbd1df13b..4815421f246e3 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -688,13 +688,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bool(dest.offset(offset), overflowed) } - /// extracts the lhs and rhs primval from the operands and applies the binary op + /// Extracts the lhs and rhs primval from the operands and applies the binary op. + /// Returns the result and whether the operation overflowed fn eval_binop( &mut self, op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - ) -> EvalResult<'tcx, PrimVal> { + ) -> EvalResult<'tcx, (PrimVal, bool)> { let left_ptr = self.eval_operand(left)?; let left_ty = self.operand_ty(left); let left_val = self.read_primval(left_ptr, left_ty)?; @@ -714,18 +715,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right: &mir::Operand<'tcx>, dest: Pointer, ) -> EvalResult<'tcx, bool> { - match self.eval_binop(op, left, right) { - Ok(val) => { - self.memory.write_primval(dest, val)?; - Ok(false) - }, - Err(EvalError::Overflow(l, r, op, val)) => { - debug!("operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val); - self.memory.write_primval(dest, val)?; - Ok(true) - }, - Err(other) => Err(other), - } + let (val, overflow) = self.eval_binop(op, left, right)?; + self.memory.write_primval(dest, val)?; + Ok(overflow) } fn call_intrinsic( @@ -951,7 +943,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } BinaryOp(bin_op, ref left, ref right) => { - let result = self.eval_binop(bin_op, left, right)?; + // ignore overflow bit, rustc inserts check branches for us + let result = self.eval_binop(bin_op, left, right)?.0; self.memory.write_primval(dest, result)?; } diff --git a/src/primval.rs b/src/primval.rs index 67ef58948fa76..673aea4244424 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,7 +14,8 @@ pub enum PrimVal { IntegerPtr(u64), } -pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, PrimVal> { +/// returns the result of the operation and whether the operation overflowed +pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::repr::BinOp::*; use self::PrimVal::*; @@ -22,7 +23,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({ let (val, of) = $l.$op($r); if of { - return Err(EvalError::Overflow($v($l), $v2($r), bin_op, $v(val))); + return Ok(($v(val), true)); } else { $v(val) } @@ -99,7 +100,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva U64(l) => shift!(U64, l, r), _ => unreachable!(), }; - return Ok(val); + return Ok((val, false)); }, _ => {}, } @@ -137,7 +138,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (AbstractPtr(_), FnPtr(_)) | (FnPtr(_), IntegerPtr(_)) | (IntegerPtr(_), FnPtr(_)) => - return unrelated_ptr_ops(bin_op), + unrelated_ptr_ops(bin_op)?, (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op { Eq => Bool(l_ptr == r_ptr), @@ -147,7 +148,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => { if l_ptr.alloc_id != r_ptr.alloc_id { - return unrelated_ptr_ops(bin_op); + return Ok((unrelated_ptr_ops(bin_op)?, false)); } let l = l_ptr.offset; @@ -167,7 +168,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))), }; - Ok(val) + Ok((val, false)) } pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { From 68469be89bd7652cf12a0fe74fb6cd6c5915498d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 16:52:53 +0200 Subject: [PATCH 0405/1096] rename function cache member --- src/memory.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index a7fecd773c907..be0cd0ef4f321 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -60,7 +60,7 @@ pub struct Memory<'tcx> { /// we can figure out what they point to. functions: HashMap>, /// Inverse map of `functions` so we don't allocate a new pointer every time we need one - function_definitions: HashMap, AllocId>, + function_alloc_cache: HashMap, AllocId>, next_id: AllocId, pub pointer_size: usize, } @@ -71,7 +71,7 @@ impl<'tcx> Memory<'tcx> { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), - function_definitions: HashMap::new(), + function_alloc_cache: HashMap::new(), next_id: AllocId(0), pointer_size: pointer_size, } @@ -83,7 +83,7 @@ impl<'tcx> Memory<'tcx> { substs: substs, fn_ty: fn_ty, }; - if let Some(&alloc_id) = self.function_definitions.get(&def) { + if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { return Pointer { alloc_id: alloc_id, offset: 0, @@ -93,7 +93,7 @@ impl<'tcx> Memory<'tcx> { debug!("creating fn ptr: {}", id); self.next_id.0 += 1; self.functions.insert(id, def); - self.function_definitions.insert(def, id); + self.function_alloc_cache.insert(def, id); Pointer { alloc_id: id, offset: 0, From 0821a15476ad85975445fdfde3bb8016b8d8c848 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 16:57:36 +0200 Subject: [PATCH 0406/1096] no need for EvalContext::eval_binop --- src/interpreter/mod.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4815421f246e3..0a8c82c5df3f5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -688,14 +688,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bool(dest.offset(offset), overflowed) } - /// Extracts the lhs and rhs primval from the operands and applies the binary op. - /// Returns the result and whether the operation overflowed - fn eval_binop( + /// Applies the binary operation `op` to the arguments and writes the result to the destination. + /// Returns `true` if the operation overflowed. + fn intrinsic_overflowing( &mut self, op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - ) -> EvalResult<'tcx, (PrimVal, bool)> { + dest: Pointer, + ) -> EvalResult<'tcx, bool> { let left_ptr = self.eval_operand(left)?; let left_ty = self.operand_ty(left); let left_val = self.read_primval(left_ptr, left_ty)?; @@ -704,18 +705,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let right_ty = self.operand_ty(right); let right_val = self.read_primval(right_ptr, right_ty)?; - primval::binary_op(op, left_val, right_val) - } - - /// applies the binary operation `op` to the arguments and writes the result to the destination - fn intrinsic_overflowing( - &mut self, - op: mir::BinOp, - left: &mir::Operand<'tcx>, - right: &mir::Operand<'tcx>, - dest: Pointer, - ) -> EvalResult<'tcx, bool> { - let (val, overflow) = self.eval_binop(op, left, right)?; + let (val, overflow) = primval::binary_op(op, left_val, right_val)?; self.memory.write_primval(dest, val)?; Ok(overflow) } @@ -944,8 +934,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { BinaryOp(bin_op, ref left, ref right) => { // ignore overflow bit, rustc inserts check branches for us - let result = self.eval_binop(bin_op, left, right)?.0; - self.memory.write_primval(dest, result)?; + self.intrinsic_overflowing(bin_op, left, right, dest)?; } CheckedBinaryOp(bin_op, ref left, ref right) => { From 3e3aeab0ed29e7da7aa151465d5ed63d6689069b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 17:16:45 +0200 Subject: [PATCH 0407/1096] implement bit masks as the compiler would translate them --- src/error.rs | 2 +- src/primval.rs | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/error.rs b/src/error.rs index 3962c09dee249..9e5e8997468ea 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,7 +69,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::Math(..) => "mathematical operation failed", EvalError::InvalidBitShiftRhs(..) => - "bit shift rhs negative or not an int", + "bit shift rhs not an int", } } diff --git a/src/primval.rs b/src/primval.rs index 673aea4244424..586aaeee4e925 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -69,15 +69,26 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva match bin_op { // can have rhs with a different numeric type Shl | Shr => { + let mask_bits = match left { + I8(_) => 3, + I16(_) => 4, + I32(_) => 5, + I64(_) => 6, + U8(_) => 3, + U16(_) => 4, + U32(_) => 5, + U64(_) => 6, + _ => unreachable!(), + }; let r = match right { - I8(i) if i >= 0 => i as u32, - I16(i) if i >= 0 => i as u32, - I32(i) if i >= 0 => i as u32, - I64(i) if i >= 0 && i as i32 as i64 == i => i as u32, - U8(i) => i as u32, - U16(i) => i as u32, - U32(i) => i, - U64(i) if i as u32 as u64 == i => i as u32, + I8(i) => (i & ((1 << mask_bits) - 1)) as u32, + I16(i) => (i & ((1 << mask_bits) - 1)) as u32, + I32(i) => (i & ((1 << mask_bits) - 1)) as u32, + I64(i) => (i & ((1 << mask_bits) - 1)) as u32, + U8(i) => (i & ((1 << mask_bits) - 1)) as u32, + U16(i) => (i & ((1 << mask_bits) - 1)) as u32, + U32(i) => (i & ((1 << mask_bits) - 1)) as u32, + U64(i) => (i & ((1 << mask_bits) - 1)) as u32, _ => return Err(EvalError::InvalidBitShiftRhs(right)), }; macro_rules! shift { From a088f105aa2e099ba027e47f9bc29c30b06b416e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 17:52:14 +0200 Subject: [PATCH 0408/1096] add a comment explaining the magic numbers --- src/primval.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/primval.rs b/src/primval.rs index 586aaeee4e925..29d79abbbfc42 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -69,6 +69,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva match bin_op { // can have rhs with a different numeric type Shl | Shr => { + // these numbers are the maximum number of bits a bitshift rhs could possibly have + // e.g. u16 can be bitshifted by 0..16, so 2^4 - 1 is the largest possible bitshift let mask_bits = match left { I8(_) => 3, I16(_) => 4, From 001ae69212a58766c007bf8464927e18fe406d0f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 17:52:36 +0200 Subject: [PATCH 0409/1096] remove the bad rhs value error and panic instead. the typechecker prevent this --- src/error.rs | 4 ---- src/primval.rs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9e5e8997468ea..b19f63231aff6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,6 @@ use rustc::ty::BareFnTy; use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; -use primval::PrimVal; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { @@ -29,7 +28,6 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), - InvalidBitShiftRhs(PrimVal), } pub type EvalResult<'tcx, T> = Result>; @@ -68,8 +66,6 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", - EvalError::InvalidBitShiftRhs(..) => - "bit shift rhs not an int", } } diff --git a/src/primval.rs b/src/primval.rs index 29d79abbbfc42..fbb930128856c 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -91,7 +91,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva U16(i) => (i & ((1 << mask_bits) - 1)) as u32, U32(i) => (i & ((1 << mask_bits) - 1)) as u32, U64(i) => (i & ((1 << mask_bits) - 1)) as u32, - _ => return Err(EvalError::InvalidBitShiftRhs(right)), + _ => panic!("bad MIR: bitshift rhs is not integral"), }; macro_rules! shift { ($v:ident, $l:ident, $r:ident) => ({ From c7039dbb2b601cff4a929e01c4941b00becadab2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 18:01:35 +0200 Subject: [PATCH 0410/1096] simplify the masked rhs computation --- src/primval.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/primval.rs b/src/primval.rs index fbb930128856c..3399d697ed480 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -82,17 +82,19 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva U64(_) => 6, _ => unreachable!(), }; + let mask = (1 << mask_bits) - 1; let r = match right { - I8(i) => (i & ((1 << mask_bits) - 1)) as u32, - I16(i) => (i & ((1 << mask_bits) - 1)) as u32, - I32(i) => (i & ((1 << mask_bits) - 1)) as u32, - I64(i) => (i & ((1 << mask_bits) - 1)) as u32, - U8(i) => (i & ((1 << mask_bits) - 1)) as u32, - U16(i) => (i & ((1 << mask_bits) - 1)) as u32, - U32(i) => (i & ((1 << mask_bits) - 1)) as u32, - U64(i) => (i & ((1 << mask_bits) - 1)) as u32, + I8(i) => i as u8 & mask, + I16(i) => i as u8 & mask, + I32(i) => i as u8 & mask, + I64(i) => i as u8 & mask, + U8(i) => i as u8 & mask, + U16(i) => i as u8 & mask, + U32(i) => i as u8 & mask, + U64(i) => i as u8 & mask, _ => panic!("bad MIR: bitshift rhs is not integral"), }; + let r = r as u32; macro_rules! shift { ($v:ident, $l:ident, $r:ident) => ({ match bin_op { From 65de5dd2d0fd66c5277787558c3af9421545af24 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 18:15:33 +0200 Subject: [PATCH 0411/1096] simplify even more --- src/primval.rs | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/primval.rs b/src/primval.rs index 3399d697ed480..6b17a63b65862 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -69,32 +69,30 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva match bin_op { // can have rhs with a different numeric type Shl | Shr => { - // these numbers are the maximum number of bits a bitshift rhs could possibly have - // e.g. u16 can be bitshifted by 0..16, so 2^4 - 1 is the largest possible bitshift - let mask_bits = match left { - I8(_) => 3, - I16(_) => 4, - I32(_) => 5, - I64(_) => 6, - U8(_) => 3, - U16(_) => 4, - U32(_) => 5, - U64(_) => 6, + // these numbers are the maximum number a bitshift rhs could possibly have + // e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range + let type_bits: u32 = match left { + I8(_) | U8(_) => 8, + I16(_) | U16(_) => 16, + I32(_) | U32(_) => 32, + I64(_) | U64(_) => 64, _ => unreachable!(), }; - let mask = (1 << mask_bits) - 1; + assert!(type_bits.is_power_of_two()); + // turn into `u32` because `overflowing_sh{l,r}` only take `u32` let r = match right { - I8(i) => i as u8 & mask, - I16(i) => i as u8 & mask, - I32(i) => i as u8 & mask, - I64(i) => i as u8 & mask, - U8(i) => i as u8 & mask, - U16(i) => i as u8 & mask, - U32(i) => i as u8 & mask, - U64(i) => i as u8 & mask, + I8(i) => i as u32, + I16(i) => i as u32, + I32(i) => i as u32, + I64(i) => i as u32, + U8(i) => i as u32, + U16(i) => i as u32, + U32(i) => i as u32, + U64(i) => i as u32, _ => panic!("bad MIR: bitshift rhs is not integral"), }; - let r = r as u32; + // apply mask + let r = r & (type_bits - 1); macro_rules! shift { ($v:ident, $l:ident, $r:ident) => ({ match bin_op { From 2dbd30fa516c7fd91f9d13c0aeaff88c7e4e6021 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 20 Jun 2016 12:29:45 +0200 Subject: [PATCH 0412/1096] implement char handling --- src/error.rs | 5 +++++ src/interpreter/mod.rs | 13 ++++++++++++- src/memory.rs | 1 + src/primval.rs | 10 ++++++++++ tests/compile-fail/option_eq.rs | 5 +++++ tests/run-pass/char.rs | 9 +++++++++ 6 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/option_eq.rs create mode 100644 tests/run-pass/char.rs diff --git a/src/error.rs b/src/error.rs index b19f63231aff6..9725e9aafc4e3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,6 +28,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), + InvalidChar(u32), } pub type EvalResult<'tcx, T> = Result>; @@ -66,6 +67,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::InvalidChar(..) => + "tried to interpret an invalid 32-bit value as a char", } } @@ -85,6 +88,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "array index {} out of bounds {} at {:?}", index, len, span), EvalError::Math(span, ref err) => write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + EvalError::InvalidChar(c) => + write!(f, "invalid utf8 character: {}", c), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0a8c82c5df3f5..6b6779b59c47a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -208,7 +208,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bool(ptr, b)?; Ok(ptr) } - Char(_c) => unimplemented!(), + Char(c) => { + let ptr = self.memory.allocate(4); + self.memory.write_uint(ptr, c as u32 as u64, 4)?; + Ok(ptr) + }, Struct(_node_id) => unimplemented!(), Tuple(_node_id) => unimplemented!(), Function(_def_id) => unimplemented!(), @@ -1371,6 +1375,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::ast::{IntTy, UintTy}; let val = match (self.memory.pointer_size, &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), + (_, &ty::TyChar) => { + let c = self.memory.read_uint(ptr, 4)? as u32; + match ::std::char::from_u32(c) { + Some(ch) => PrimVal::Char(ch), + None => return Err(EvalError::InvalidChar(c)), + } + } (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), (2, &ty::TyInt(IntTy::Is)) | (_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), diff --git a/src/memory.rs b/src/memory.rs index be0cd0ef4f321..17a42e6fccf2a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -369,6 +369,7 @@ impl<'tcx> Memory<'tcx> { PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), + PrimVal::Char(c) => self.write_uint(ptr, c as u32 as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), diff --git a/src/primval.rs b/src/primval.rs index 6b17a63b65862..f3aedfc19743d 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -12,6 +12,7 @@ pub enum PrimVal { AbstractPtr(Pointer), FnPtr(Pointer), IntegerPtr(u64), + Char(char), } /// returns the result of the operation and whether the operation overflowed @@ -127,6 +128,15 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (U16(l), U16(r)) => int_binops!(U16, l, r), (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (Char(l), Char(r)) => match bin_op { + Eq => Bool(l == r), + Ne => Bool(l != r), + Lt => Bool(l < r), + Le => Bool(l <= r), + Gt => Bool(l > r), + Ge => Bool(l >= r), + _ => panic!("invalid char op: {:?}", bin_op), + }, (Bool(l), Bool(r)) => { Bool(match bin_op { diff --git a/tests/compile-fail/option_eq.rs b/tests/compile-fail/option_eq.rs new file mode 100644 index 0000000000000..8315ae7af84a7 --- /dev/null +++ b/tests/compile-fail/option_eq.rs @@ -0,0 +1,5 @@ +//error-pattern: can't handle cast: tmp0 as isize (Misc) +// no-ignore-x86 ignore-x86_64 +fn main() { + assert_eq!(std::char::from_u32('x' as u32), Some('x')); +} diff --git a/tests/run-pass/char.rs b/tests/run-pass/char.rs new file mode 100644 index 0000000000000..505c09b0ad885 --- /dev/null +++ b/tests/run-pass/char.rs @@ -0,0 +1,9 @@ +fn main() { + let c = 'x'; + assert_eq!(c, 'x'); + assert!('a' < 'z'); + assert!('1' < '9'); + assert_eq!(std::char::from_u32('x' as u32).unwrap(), 'x'); + // FIXME: + // assert_eq!(std::char::from_u32('x' as u32), Some('x')); +} From 422e5edd28bba8599bf3e816f67d2746528c1af8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:43:27 +0200 Subject: [PATCH 0413/1096] error message improvements --- src/error.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9725e9aafc4e3..da5d8cf787f49 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,11 +85,11 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::FunctionPointerTyMismatch(expected, got) => write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => - write!(f, "array index {} out of bounds {} at {:?}", index, len, span), + write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => - write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + write!(f, "{:?} at {:?}", err, span), EvalError::InvalidChar(c) => - write!(f, "invalid utf8 character: {}", c), + write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), _ => write!(f, "{}", self.description()), } } From 7a9272c8e153b9264fffb3a19a113638e89b7857 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:43:45 +0200 Subject: [PATCH 0414/1096] no need to cast chars as u32 before casting to u64 --- src/interpreter/mod.rs | 2 +- src/memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6b6779b59c47a..18f97f02697d7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Char(c) => { let ptr = self.memory.allocate(4); - self.memory.write_uint(ptr, c as u32 as u64, 4)?; + self.memory.write_uint(ptr, c as u64, 4)?; Ok(ptr) }, Struct(_node_id) => unimplemented!(), diff --git a/src/memory.rs b/src/memory.rs index 17a42e6fccf2a..a16fbc57a612a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -369,7 +369,7 @@ impl<'tcx> Memory<'tcx> { PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), - PrimVal::Char(c) => self.write_uint(ptr, c as u32 as u64, 4), + PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), From b10fc7a99ff127b68260d134ea2c70a3aa5021c1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:44:01 +0200 Subject: [PATCH 0415/1096] make sure miri never switches over an invalid char value --- src/interpreter/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 18f97f02697d7..41828d7a23136 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -406,11 +406,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + let discr_ty = self.lvalue_ty(discr); let discr_size = self - .type_layout(self.lvalue_ty(discr)) + .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; + if let ty::TyChar = discr_ty.sty { + if ::std::char::from_u32(discr_val as u32).is_none() { + return Err(EvalError::InvalidChar(discr_val as u32)); + } + } // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; From 7bda9f24d6591799c8685af6bb97ff8145549cbd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 23 Jun 2016 00:02:47 -0600 Subject: [PATCH 0416/1096] Make `step` an `EvalContext` method and remove `Stepper`. --- src/interpreter/mod.rs | 6 +-- src/interpreter/stepper.rs | 86 ++++++++++++++++++-------------------- src/lib.rs | 1 - 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 41828d7a23136..410b7a3d79e9a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -25,10 +25,6 @@ use std::collections::HashMap; mod stepper; -pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, bool> { - stepper::Stepper::new(ecx).step() -} - pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -1571,7 +1567,7 @@ pub fn eval_main<'a, 'tcx: 'a>( } loop { - match step(&mut ecx) { + match ecx.step() { Ok(true) => {} Ok(false) => break, // FIXME: diverging functions can end up here in some future miri diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index f1dfcd6cfcd73..37a7aa4d01179 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -1,3 +1,7 @@ +//! This module contains the `EvalContext` methods for executing a single step of the interpreter. +//! +//! The main entry point is the `step` method. + use super::{ CachedMir, ConstantId, @@ -12,57 +16,28 @@ use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -pub(super) struct Stepper<'ecx, 'a: 'ecx, 'tcx: 'a>{ - ecx: &'ecx mut EvalContext<'a, 'tcx>, -} - -impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { - pub(super) fn new(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> Self { - Stepper { - ecx: ecx, - } - } - - fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { - trace!("{:?}", stmt); - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - self.ecx.eval_assignment(lvalue, rvalue)?; - self.ecx.frame_mut().stmt += 1; - Ok(()) - } - - fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { - // after a terminator we go to a new block - self.ecx.frame_mut().stmt = 0; - trace!("{:?}", terminator.kind); - self.ecx.eval_terminator(terminator)?; - if !self.ecx.stack.is_empty() { - trace!("// {:?}", self.ecx.frame().block); - } - Ok(()) - } - - // returns true as long as there are more things to do - pub(super) fn step(&mut self) -> EvalResult<'tcx, bool> { - if self.ecx.stack.is_empty() { +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + /// Returns true as long as there are more things to do. + pub fn step(&mut self) -> EvalResult<'tcx, bool> { + if self.stack.is_empty() { return Ok(false); } - let block = self.ecx.frame().block; - let stmt = self.ecx.frame().stmt; - let mir = self.ecx.mir(); + let block = self.frame().block; + let stmt = self.frame().stmt; + let mir = self.mir(); let basic_block = &mir.basic_blocks()[block]; if let Some(ref stmt) = basic_block.statements.get(stmt) { - let current_stack = self.ecx.stack.len(); + let current_stack = self.stack.len(); ConstantExtractor { span: stmt.source_info.span, - substs: self.ecx.substs(), - def_id: self.ecx.frame().def_id, - ecx: self.ecx, + substs: self.substs(), + def_id: self.frame().def_id, + ecx: self, mir: &mir, }.visit_statement(block, stmt); - if current_stack == self.ecx.stack.len() { + if current_stack == self.stack.len() { self.statement(stmt)?; } else { // ConstantExtractor added some new frames for statics/constants/promoteds @@ -73,15 +48,15 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { } let terminator = basic_block.terminator(); - let current_stack = self.ecx.stack.len(); + let current_stack = self.stack.len(); ConstantExtractor { span: terminator.source_info.span, - substs: self.ecx.substs(), - def_id: self.ecx.frame().def_id, - ecx: self.ecx, + substs: self.substs(), + def_id: self.frame().def_id, + ecx: self, mir: &mir, }.visit_terminator(block, terminator); - if current_stack == self.ecx.stack.len() { + if current_stack == self.stack.len() { self.terminator(terminator)?; } else { // ConstantExtractor added some new frames for statics/constants/promoteds @@ -90,6 +65,25 @@ impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { } Ok(true) } + + fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { + trace!("{:?}", stmt); + let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; + self.eval_assignment(lvalue, rvalue)?; + self.frame_mut().stmt += 1; + Ok(()) + } + + fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { + // after a terminator we go to a new block + self.frame_mut().stmt = 0; + trace!("{:?}", terminator.kind); + self.eval_terminator(terminator)?; + if !self.stack.is_empty() { + trace!("// {:?}", self.frame().block); + } + Ok(()) + } } // WARNING: make sure that any methods implemented on this type don't ever access ecx.stack diff --git a/src/lib.rs b/src/lib.rs index 8addef87b2fb2..3745a960a271e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,6 @@ pub use interpreter::{ EvalContext, Frame, eval_main, - step, }; pub use memory::Memory; From d80cf91ef2b9e17218299906def9f02750abd050 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 23 Jun 2016 00:04:10 -0600 Subject: [PATCH 0417/1096] Rename `stepper` module to `step`. --- src/interpreter/mod.rs | 2 +- src/interpreter/{stepper.rs => step.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/interpreter/{stepper.rs => step.rs} (100%) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 410b7a3d79e9a..6a21714f0d609 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -23,7 +23,7 @@ use primval::{self, PrimVal}; use std::collections::HashMap; -mod stepper; +mod step; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. diff --git a/src/interpreter/stepper.rs b/src/interpreter/step.rs similarity index 100% rename from src/interpreter/stepper.rs rename to src/interpreter/step.rs From 0c720f6e6bb5269f18732273cb26ee1a1416fc22 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 23 Jun 2016 01:03:58 -0600 Subject: [PATCH 0418/1096] Split terminator evaluation into a new module. --- src/interpreter/mod.rs | 682 ++-------------------------------- src/interpreter/terminator.rs | 625 +++++++++++++++++++++++++++++++ 2 files changed, 665 insertions(+), 642 deletions(-) create mode 100644 src/interpreter/terminator.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6a21714f0d609..7b65d5f645742 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -2,28 +2,27 @@ use rustc::middle::const_val; use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; -use rustc::traits::{self, ProjectionMode}; -use rustc::ty::fold::TypeFoldable; +use rustc::traits::ProjectionMode; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; -use std::{iter, mem}; +use std::iter; use syntax::ast; -use syntax::attr; -use syntax::codemap::{self, DUMMY_SP, Span}; +use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{Memory, Pointer, FunctionDefinition}; +use memory::{Memory, Pointer}; use primval::{self, PrimVal}; use std::collections::HashMap; mod step; +mod terminator; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -218,87 +217,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) - } - fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { - // Do the initial selection for the obligation. This yields the shallow result we are - // looking for -- that is, what specific impl. - self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // Currently, we use a fulfillment context to completely resolve all nested obligations. - // This is because they can inform the inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) - }) - } - - /// Trait method, which has to be resolved to an impl method. - pub fn trait_method( - &self, - def_id: DefId, - substs: &'tcx Substs<'tcx> - ) -> (DefId, &'tcx Substs<'tcx>) { - let method_item = self.tcx.impl_or_trait_item(def_id); - let trait_id = method_item.container().id(); - let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); - match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(vtable_impl) => { - let impl_did = vtable_impl.impl_def_id; - let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the impl - // and those from the method: - let impl_substs = vtable_impl.substs.with_method_from(substs); - let substs = self.tcx.mk_substs(impl_substs); - let mth = get_impl_method(self.tcx, impl_did, substs, mname); - - (mth.method.def_id, mth.substs) - } - - traits::VtableClosure(vtable_closure) => - (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), - - traits::VtableFnPointer(_fn_ty) => { - let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - unimplemented!() - // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); - - // let method_ty = def_ty(tcx, def_id, substs); - // let fn_ptr_ty = match method_ty.sty { - // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - // _ => unreachable!("expected fn item type, found {}", - // method_ty) - // }; - // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) - } - - traits::VtableObject(ref _data) => { - unimplemented!() - // Callee { - // data: Virtual(traits::get_vtable_index_of_object_method( - // tcx, data, def_id)), - // ty: def_ty(tcx, def_id, substs) - // } - } - vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), - } - } - fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { use rustc_trans::back::symbol_names::def_id_to_string; match self.tcx.map.as_local_node_id(def_id) { @@ -384,297 +306,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Deallocate local variables. } - fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) - -> EvalResult<'tcx, ()> { - use rustc::mir::repr::TerminatorKind::*; - match terminator.kind { - Return => self.pop_stack_frame(), - - Goto { target } => { - self.frame_mut().block = target; - }, - - If { ref cond, targets: (then_target, else_target) } => { - let cond_ptr = self.eval_operand(cond)?; - let cond_val = self.memory.read_bool(cond_ptr)?; - self.frame_mut().block = if cond_val { then_target } else { else_target }; - } - - SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); - let discr_ty = self.lvalue_ty(discr); - let discr_size = self - .type_layout(discr_ty) - .size(&self.tcx.data_layout) - .bytes() as usize; - let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; - if let ty::TyChar = discr_ty.sty { - if ::std::char::from_u32(discr_val as u32).is_none() { - return Err(EvalError::InvalidChar(discr_val as u32)); - } - } - - // Branch to the `otherwise` case by default, if no match is found. - let mut target_block = targets[targets.len() - 1]; - - for (index, val_const) in values.iter().enumerate() { - let ptr = self.const_to_ptr(val_const)?; - let val = self.memory.read_uint(ptr, discr_size)?; - if discr_val == val { - target_block = targets[index]; - break; - } - } - - self.frame_mut().block = target_block; - } - - Switch { ref discr, ref targets, adt_def } => { - let adt_ptr = self.eval_lvalue(discr)?.to_ptr(); - let adt_ty = self.lvalue_ty(discr); - let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; - let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u64_unchecked()); - - match matching { - Some(i) => { - self.frame_mut().block = targets[i]; - }, - None => return Err(EvalError::InvalidDiscriminant), - } - } - - Call { ref func, ref args, ref destination, .. } => { - let mut return_ptr = None; - if let Some((ref lv, target)) = *destination { - self.frame_mut().block = target; - return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); - } - - let func_ty = self.operand_ty(func); - match func_ty.sty { - ty::TyFnPtr(bare_fn_ty) => { - let ptr = self.eval_operand(func)?; - assert_eq!(ptr.offset, 0); - let fn_ptr = self.memory.read_ptr(ptr)?; - let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; - if fn_ty != bare_fn_ty { - return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); - } - self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, - terminator.source_info.span)? - }, - ty::TyFnDef(def_id, substs, fn_ty) => { - self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, - terminator.source_info.span)? - } - - _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), - } - } - - Drop { ref location, target, .. } => { - let ptr = self.eval_lvalue(location)?.to_ptr(); - let ty = self.lvalue_ty(location); - self.drop(ptr, ty)?; - self.frame_mut().block = target; - } - - Assert { ref cond, expected, ref msg, target, .. } => { - let cond_ptr = self.eval_operand(cond)?; - if expected == self.memory.read_bool(cond_ptr)? { - self.frame_mut().block = target; - } else { - return match *msg { - mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.eval_operand(len).expect("can't eval len"); - let len = self.memory.read_usize(len).expect("can't read len"); - let index = self.eval_operand(index).expect("can't eval index"); - let index = self.memory.read_usize(index).expect("can't read index"); - Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) - }, - mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())), - } - } - }, - - DropAndReplace { .. } => unimplemented!(), - Resume => unimplemented!(), - Unreachable => unimplemented!(), - } - - Ok(()) - } - - pub fn eval_fn_call( - &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - fn_ty: &'tcx BareFnTy, - return_ptr: Option, - args: &[mir::Operand<'tcx>], - span: Span, - ) -> EvalResult<'tcx, ()> { - use syntax::abi::Abi; - match fn_ty.abi { - Abi::RustIntrinsic => { - let name = self.tcx.item_name(def_id).as_str(); - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let layout = self.type_layout(ty); - let ret = return_ptr.unwrap(); - self.call_intrinsic(&name, substs, args, ret, layout) - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::C => { - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size) - } - ty::FnDiverging => unimplemented!(), - } - } - - Abi::Rust | Abi::RustCall => { - // TODO(solson): Adjust the first argument when calling a Fn or - // FnMut closure via FnOnce::call_once. - - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { - self.trait_method(def_id, substs) - } else { - (def_id, substs) - }; - - let mut arg_srcs = Vec::new(); - for arg in args { - let src = self.eval_operand(arg)?; - let src_ty = self.operand_ty(arg); - arg_srcs.push((src, src_ty)); - } - - if fn_ty.abi == Abi::RustCall && !args.is_empty() { - arg_srcs.pop(); - let last_arg = args.last().unwrap(); - let last = self.eval_operand(last_arg)?; - let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); - } - } - ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - - let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr); - - for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { - let dest = self.frame().locals[i]; - self.move_(src, dest, src_ty)?; - } - - Ok(()) - } - - abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), - } - } - - fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { - if !self.type_needs_drop(ty) { - debug!("no need to drop {:?}", ty); - return Ok(()); - } - trace!("-need to drop {:?}", ty); - - // TODO(solson): Call user-defined Drop::drop impls. - - match ty.sty { - ty::TyBox(contents_ty) => { - match self.memory.read_ptr(ptr) { - Ok(contents_ptr) => { - self.drop(contents_ptr, contents_ty)?; - trace!("-deallocating box"); - self.memory.deallocate(contents_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let size = self.memory.pointer_size; - let possible_drop_fill = self.memory.read_bytes(ptr, size)?; - if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { - return Ok(()); - } else { - return Err(EvalError::ReadBytesAsPointer); - } - } - Err(e) => return Err(e), - } - } - - // TODO(solson): Implement drop for other relevant types (e.g. aggregates). - _ => {} - } - - // Filling drop. - // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty); - self.memory.drop_fill(ptr, size)?; - - Ok(()) - } - - fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { - use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty); - - let discr_val = match *adt_layout { - General { discr, .. } | CEnum { discr, .. } => { - let discr_size = discr.size().bytes(); - self.memory.read_uint(adt_ptr, discr_size as usize)? - } - - RawNullablePointer { nndiscr, .. } => { - self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? - } - - StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes() as isize); - self.read_nonnull_discriminant_value(nonnull, nndiscr)? - } - - // The discriminant_value intrinsic returns 0 for non-sum types. - Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | - Vector { .. } => 0, - }; - - Ok(discr_val) - } - - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { - let not_null = match self.memory.read_usize(ptr) { - Ok(0) => false, - Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, - Err(e) => return Err(e), - }; - assert!(nndiscr == 0 || nndiscr == 1); - Ok(if not_null { nndiscr } else { 1 - nndiscr }) - } - - /// applies the binary operation `op` to the two operands and writes a tuple of the result - /// and a boolean signifying the potential overflow to the destination + /// Applies the binary operation `op` to the two operands and writes a tuple of the result + /// and a boolean signifying the potential overflow to the destination. fn intrinsic_with_overflow( &mut self, op: mir::BinOp, @@ -716,199 +349,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(overflow) } - fn call_intrinsic( - &mut self, - name: &str, - substs: &'tcx Substs<'tcx>, - args: &[mir::Operand<'tcx>], - dest: Pointer, - dest_layout: &'tcx Layout, - ) -> EvalResult<'tcx, ()> { - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) - .collect(); - let args_ptrs = args_res?; - - let pointer_size = self.memory.pointer_size; - - match name { - "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, - "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, - "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, - - // FIXME: turn into an assertion to catch wrong `assume` that would cause UB in llvm - "assume" => {} - - "copy_nonoverlapping" => { - let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty); - let src = self.memory.read_ptr(args_ptrs[0])?; - let dest = self.memory.read_ptr(args_ptrs[1])?; - let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size)?; - } - - "discriminant_value" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; - let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.memory.write_uint(dest, discr_val, 8)?; - } - - "forget" => { - let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty); - self.memory.drop_fill(args_ptrs[0], arg_size)?; - } - - "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, - - "min_align_of" => { - // FIXME: use correct value - self.memory.write_int(dest, 1, pointer_size)?; - } - - "move_val_init" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], ptr, ty)?; - } - - "offset" => { - let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args_ptrs[0]; - let offset = self.memory.read_isize(args_ptrs[1])?; - - match self.memory.read_ptr(ptr_arg) { - Ok(ptr) => { - let result_ptr = ptr.offset(offset as isize * pointee_size); - self.memory.write_ptr(dest, result_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let addr = self.memory.read_isize(ptr_arg)?; - let result_addr = addr + offset * pointee_size as i64; - self.memory.write_isize(dest, result_addr)?; - } - Err(e) => return Err(e), - } - } - - "overflowing_sub" => { - self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; - } - "overflowing_mul" => { - self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; - } - "overflowing_add" => { - self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; - } - - "size_of" => { - let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } - - "size_of_val" => { - let ty = *substs.types.get(subst::FnSpace, 0); - if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } else { - match ty.sty { - ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size as isize; - let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; - self.memory.write_uint(dest, n * elem_size, pointer_size)?; - } - - _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), - } - } - } - - "transmute" => { - let ty = *substs.types.get(subst::FnSpace, 0); - self.move_(args_ptrs[0], dest, ty)?; - } - "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, - - name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), - } - - // Since we pushed no stack frame, the main loop will act - // as if the call just completed and it's returning to the - // current frame. - Ok(()) - } - - fn call_c_abi( - &mut self, - def_id: DefId, - args: &[mir::Operand<'tcx>], - dest: Pointer, - dest_size: usize, - ) -> EvalResult<'tcx, ()> { - let name = self.tcx.item_name(def_id); - let attrs = self.tcx.get_attrs(def_id); - let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { - Some(ln) => ln.clone(), - None => name.as_str(), - }; - - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) - .collect(); - let args = args_res?; - - match &link_name[..] { - "__rust_allocate" => { - let size = self.memory.read_usize(args[0])?; - let ptr = self.memory.allocate(size as usize); - self.memory.write_ptr(dest, ptr)?; - } - - "__rust_reallocate" => { - let ptr = self.memory.read_ptr(args[0])?; - let size = self.memory.read_usize(args[2])?; - self.memory.reallocate(ptr, size as usize)?; - self.memory.write_ptr(dest, ptr)?; - } - - "memcmp" => { - let left = self.memory.read_ptr(args[0])?; - let right = self.memory.read_ptr(args[1])?; - let n = self.memory.read_usize(args[2])? as usize; - - let result = { - let left_bytes = self.memory.read_bytes(left, n)?; - let right_bytes = self.memory.read_bytes(right, n)?; - - use std::cmp::Ordering::*; - match left_bytes.cmp(right_bytes) { - Less => -1, - Equal => 0, - Greater => 1, - } - }; - - self.memory.write_int(dest, result, dest_size)?; - } - - _ => { - return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); - } - } - - // Since we pushed no stack frame, the main loop will act - // as if the call just completed and it's returning to the - // current frame. - Ok(()) - } - fn assign_fields>( &mut self, dest: Pointer, @@ -1468,80 +908,6 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { } } -#[derive(Debug)] -pub struct ImplMethod<'tcx> { - pub method: Rc>, - pub substs: &'tcx Substs<'tcx>, - pub is_provided: bool, -} - -/// Locates the applicable definition of a method, given its name. -pub fn get_impl_method<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - impl_def_id: DefId, - substs: &'tcx Substs<'tcx>, - name: ast::Name, -) -> ImplMethod<'tcx> { - assert!(!substs.types.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { - Some(node_item) => { - let substs = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { - let substs = traits::translate_substs(&infcx, impl_def_id, - substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("trans::meth::get_impl_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - ImplMethod { - method: node_item.item, - substs: substs, - is_provided: node_item.node.is_from_trait(), - } - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} - -// TODO(solson): Upstream these methods into rustc::ty::layout. - -trait IntegerExt { - fn size(self) -> Size; -} - -impl IntegerExt for layout::Integer { - fn size(self) -> Size { - use rustc::ty::layout::Integer::*; - match self { - I1 | I8 => Size::from_bits(8), - I16 => Size::from_bits(16), - I32 => Size::from_bits(32), - I64 => Size::from_bits(64), - } - } -} - -trait StructExt { - fn field_offset(&self, index: usize) -> Size; -} - -impl StructExt for layout::Struct { - fn field_offset(&self, index: usize) -> Size { - if index == 0 { - Size::from_bytes(0) - } else { - self.offset_after_field[index - 1] - } - } -} - pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, @@ -1603,3 +969,35 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { } err.emit(); } + +// TODO(solson): Upstream these methods into rustc::ty::layout. + +trait IntegerExt { + fn size(self) -> Size; +} + +impl IntegerExt for layout::Integer { + fn size(self) -> Size { + use rustc::ty::layout::Integer::*; + match self { + I1 | I8 => Size::from_bits(8), + I16 => Size::from_bits(16), + I32 => Size::from_bits(32), + I64 => Size::from_bits(64), + } + } +} + +trait StructExt { + fn field_offset(&self, index: usize) -> Size; +} + +impl StructExt for layout::Struct { + fn field_offset(&self, index: usize) -> Size { + if index == 0 { + Size::from_bytes(0) + } else { + self.offset_after_field[index - 1] + } + } +} diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs new file mode 100644 index 0000000000000..3d7bef825b900 --- /dev/null +++ b/src/interpreter/terminator.rs @@ -0,0 +1,625 @@ +use rustc::hir::def_id::DefId; +use rustc::mir::repr as mir; +use rustc::traits::{self, ProjectionMode}; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::layout::Layout; +use rustc::ty::subst::{self, Substs}; +use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; +use std::rc::Rc; +use std::{iter, mem}; +use syntax::{ast, attr}; +use syntax::codemap::{DUMMY_SP, Span}; + +use super::{EvalContext, IntegerExt}; +use error::{EvalError, EvalResult}; +use memory::{Pointer, FunctionDefinition}; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(super) fn eval_terminator( + &mut self, + terminator: &mir::Terminator<'tcx>, + ) -> EvalResult<'tcx, ()> { + use rustc::mir::repr::TerminatorKind::*; + match terminator.kind { + Return => self.pop_stack_frame(), + + Goto { target } => { + self.frame_mut().block = target; + }, + + If { ref cond, targets: (then_target, else_target) } => { + let cond_ptr = self.eval_operand(cond)?; + let cond_val = self.memory.read_bool(cond_ptr)?; + self.frame_mut().block = if cond_val { then_target } else { else_target }; + } + + SwitchInt { ref discr, ref values, ref targets, .. } => { + let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + let discr_ty = self.lvalue_ty(discr); + let discr_size = self + .type_layout(discr_ty) + .size(&self.tcx.data_layout) + .bytes() as usize; + let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; + if let ty::TyChar = discr_ty.sty { + if ::std::char::from_u32(discr_val as u32).is_none() { + return Err(EvalError::InvalidChar(discr_val as u32)); + } + } + + // Branch to the `otherwise` case by default, if no match is found. + let mut target_block = targets[targets.len() - 1]; + + for (index, val_const) in values.iter().enumerate() { + let ptr = self.const_to_ptr(val_const)?; + let val = self.memory.read_uint(ptr, discr_size)?; + if discr_val == val { + target_block = targets[index]; + break; + } + } + + self.frame_mut().block = target_block; + } + + Switch { ref discr, ref targets, adt_def } => { + let adt_ptr = self.eval_lvalue(discr)?.to_ptr(); + let adt_ty = self.lvalue_ty(discr); + let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; + let matching = adt_def.variants.iter() + .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + + match matching { + Some(i) => { + self.frame_mut().block = targets[i]; + }, + None => return Err(EvalError::InvalidDiscriminant), + } + } + + Call { ref func, ref args, ref destination, .. } => { + let mut return_ptr = None; + if let Some((ref lv, target)) = *destination { + self.frame_mut().block = target; + return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); + } + + let func_ty = self.operand_ty(func); + match func_ty.sty { + ty::TyFnPtr(bare_fn_ty) => { + let ptr = self.eval_operand(func)?; + assert_eq!(ptr.offset, 0); + let fn_ptr = self.memory.read_ptr(ptr)?; + let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; + if fn_ty != bare_fn_ty { + return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); + } + self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, + terminator.source_info.span)? + }, + ty::TyFnDef(def_id, substs, fn_ty) => { + self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, + terminator.source_info.span)? + } + + _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), + } + } + + Drop { ref location, target, .. } => { + let ptr = self.eval_lvalue(location)?.to_ptr(); + let ty = self.lvalue_ty(location); + self.drop(ptr, ty)?; + self.frame_mut().block = target; + } + + Assert { ref cond, expected, ref msg, target, .. } => { + let cond_ptr = self.eval_operand(cond)?; + if expected == self.memory.read_bool(cond_ptr)? { + self.frame_mut().block = target; + } else { + return match *msg { + mir::AssertMessage::BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("can't eval len"); + let len = self.memory.read_usize(len).expect("can't read len"); + let index = self.eval_operand(index).expect("can't eval index"); + let index = self.memory.read_usize(index).expect("can't read index"); + Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) + }, + mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())), + } + } + }, + + DropAndReplace { .. } => unimplemented!(), + Resume => unimplemented!(), + Unreachable => unimplemented!(), + } + + Ok(()) + } + + fn eval_fn_call( + &mut self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + fn_ty: &'tcx BareFnTy, + return_ptr: Option, + args: &[mir::Operand<'tcx>], + span: Span, + ) -> EvalResult<'tcx, ()> { + use syntax::abi::Abi; + match fn_ty.abi { + Abi::RustIntrinsic => { + let name = self.tcx.item_name(def_id).as_str(); + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let layout = self.type_layout(ty); + let ret = return_ptr.unwrap(); + self.call_intrinsic(&name, substs, args, ret, layout) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::C => { + match fn_ty.sig.0.output { + ty::FnConverging(ty) => { + let size = self.type_size(ty); + self.call_c_abi(def_id, args, return_ptr.unwrap(), size) + } + ty::FnDiverging => unimplemented!(), + } + } + + Abi::Rust | Abi::RustCall => { + // TODO(solson): Adjust the first argument when calling a Fn or + // FnMut closure via FnOnce::call_once. + + // Only trait methods can have a Self parameter. + let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { + self.trait_method(def_id, substs) + } else { + (def_id, substs) + }; + + let mut arg_srcs = Vec::new(); + for arg in args { + let src = self.eval_operand(arg)?; + let src_ty = self.operand_ty(arg); + arg_srcs.push((src, src_ty)); + } + + if fn_ty.abi == Abi::RustCall && !args.is_empty() { + arg_srcs.pop(); + let last_arg = args.last().unwrap(); + let last = self.eval_operand(last_arg)?; + let last_ty = self.operand_ty(last_arg); + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + arg_srcs.push((src, ty)); + } + } + ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + + let mir = self.load_mir(resolved_def_id); + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr); + + for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { + let dest = self.frame().locals[i]; + self.move_(src, dest, src_ty)?; + } + + Ok(()) + } + + abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + } + } + + fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { + use rustc::ty::layout::Layout::*; + let adt_layout = self.type_layout(adt_ty); + + let discr_val = match *adt_layout { + General { discr, .. } | CEnum { discr, .. } => { + let discr_size = discr.size().bytes(); + self.memory.read_uint(adt_ptr, discr_size as usize)? + } + + RawNullablePointer { nndiscr, .. } => { + self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? + } + + StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { + let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; + let nonnull = adt_ptr.offset(offset.bytes() as isize); + self.read_nonnull_discriminant_value(nonnull, nndiscr)? + } + + // The discriminant_value intrinsic returns 0 for non-sum types. + Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | + Vector { .. } => 0, + }; + + Ok(discr_val) + } + + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { + let not_null = match self.memory.read_usize(ptr) { + Ok(0) => false, + Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, + Err(e) => return Err(e), + }; + assert!(nndiscr == 0 || nndiscr == 1); + Ok(if not_null { nndiscr } else { 1 - nndiscr }) + } + + fn call_intrinsic( + &mut self, + name: &str, + substs: &'tcx Substs<'tcx>, + args: &[mir::Operand<'tcx>], + dest: Pointer, + dest_layout: &'tcx Layout, + ) -> EvalResult<'tcx, ()> { + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args_ptrs = args_res?; + + let pointer_size = self.memory.pointer_size; + + match name { + "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, + "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, + "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, + + // FIXME: turn into an assertion to catch wrong `assume` that would cause UB in llvm + "assume" => {} + + "copy_nonoverlapping" => { + let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_size = self.type_size(elem_ty); + let src = self.memory.read_ptr(args_ptrs[0])?; + let dest = self.memory.read_ptr(args_ptrs[1])?; + let count = self.memory.read_isize(args_ptrs[2])?; + self.memory.copy(src, dest, count as usize * elem_size)?; + } + + "discriminant_value" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; + let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + self.memory.write_uint(dest, discr_val, 8)?; + } + + "forget" => { + let arg_ty = *substs.types.get(subst::FnSpace, 0); + let arg_size = self.type_size(arg_ty); + self.memory.drop_fill(args_ptrs[0], arg_size)?; + } + + "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, + + "min_align_of" => { + // FIXME: use correct value + self.memory.write_int(dest, 1, pointer_size)?; + } + + "move_val_init" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], ptr, ty)?; + } + + "offset" => { + let pointee_ty = *substs.types.get(subst::FnSpace, 0); + let pointee_size = self.type_size(pointee_ty) as isize; + let ptr_arg = args_ptrs[0]; + let offset = self.memory.read_isize(args_ptrs[1])?; + + match self.memory.read_ptr(ptr_arg) { + Ok(ptr) => { + let result_ptr = ptr.offset(offset as isize * pointee_size); + self.memory.write_ptr(dest, result_ptr)?; + } + Err(EvalError::ReadBytesAsPointer) => { + let addr = self.memory.read_isize(ptr_arg)?; + let result_addr = addr + offset * pointee_size as i64; + self.memory.write_isize(dest, result_addr)?; + } + Err(e) => return Err(e), + } + } + + "overflowing_sub" => { + self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; + } + "overflowing_mul" => { + self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; + } + "overflowing_add" => { + self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; + } + + "size_of" => { + let ty = *substs.types.get(subst::FnSpace, 0); + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } + + "size_of_val" => { + let ty = *substs.types.get(subst::FnSpace, 0); + if self.type_is_sized(ty) { + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } else { + match ty.sty { + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty) as u64; + let ptr_size = self.memory.pointer_size as isize; + let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; + self.memory.write_uint(dest, n * elem_size, pointer_size)?; + } + + _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), + } + } + } + + "transmute" => { + let ty = *substs.types.get(subst::FnSpace, 0); + self.move_(args_ptrs[0], dest, ty)?; + } + "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, + + name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(()) + } + + fn call_c_abi( + &mut self, + def_id: DefId, + args: &[mir::Operand<'tcx>], + dest: Pointer, + dest_size: usize, + ) -> EvalResult<'tcx, ()> { + let name = self.tcx.item_name(def_id); + let attrs = self.tcx.get_attrs(def_id); + let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { + Some(ln) => ln.clone(), + None => name.as_str(), + }; + + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + if link_name.starts_with("pthread_") { + warn!("ignoring C ABI call: {}", link_name); + return Ok(()); + } + + match &link_name[..] { + "__rust_allocate" => { + let size = self.memory.read_usize(args[0])?; + let ptr = self.memory.allocate(size as usize); + self.memory.write_ptr(dest, ptr)?; + } + + "__rust_reallocate" => { + let ptr = self.memory.read_ptr(args[0])?; + let size = self.memory.read_usize(args[2])?; + self.memory.reallocate(ptr, size as usize)?; + self.memory.write_ptr(dest, ptr)?; + } + + "memcmp" => { + let left = self.memory.read_ptr(args[0])?; + let right = self.memory.read_ptr(args[1])?; + let n = self.memory.read_usize(args[2])? as usize; + + let result = { + let left_bytes = self.memory.read_bytes(left, n)?; + let right_bytes = self.memory.read_bytes(right, n)?; + + use std::cmp::Ordering::*; + match left_bytes.cmp(right_bytes) { + Less => -1, + Equal => 0, + Greater => 1, + } + }; + + self.memory.write_int(dest, result, dest_size)?; + } + + _ => { + return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); + } + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(()) + } + + fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + // Do the initial selection for the obligation. This yields the shallow result we are + // looking for -- that is, what specific impl. + self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // Currently, we use a fulfillment context to completely resolve all nested obligations. + // This is because they can inform the inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) + }) + } + + /// Trait method, which has to be resolved to an impl method. + fn trait_method( + &self, + def_id: DefId, + substs: &'tcx Substs<'tcx> + ) -> (DefId, &'tcx Substs<'tcx>) { + let method_item = self.tcx.impl_or_trait_item(def_id); + let trait_id = method_item.container().id(); + let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); + match self.fulfill_obligation(trait_ref) { + traits::VtableImpl(vtable_impl) => { + let impl_did = vtable_impl.impl_def_id; + let mname = self.tcx.item_name(def_id); + // Create a concatenated set of substitutions which includes those from the impl + // and those from the method: + let impl_substs = vtable_impl.substs.with_method_from(substs); + let substs = self.tcx.mk_substs(impl_substs); + let mth = get_impl_method(self.tcx, impl_did, substs, mname); + + (mth.method.def_id, mth.substs) + } + + traits::VtableClosure(vtable_closure) => + (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), + + traits::VtableFnPointer(_fn_ty) => { + let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + unimplemented!() + // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); + + // let method_ty = def_ty(tcx, def_id, substs); + // let fn_ptr_ty = match method_ty.sty { + // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), + // _ => unreachable!("expected fn item type, found {}", + // method_ty) + // }; + // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + } + + traits::VtableObject(ref _data) => { + unimplemented!() + // Callee { + // data: Virtual(traits::get_vtable_index_of_object_method( + // tcx, data, def_id)), + // ty: def_ty(tcx, def_id, substs) + // } + } + vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), + } + } + + pub(super) fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) + } + + fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + if !self.type_needs_drop(ty) { + debug!("no need to drop {:?}", ty); + return Ok(()); + } + trace!("-need to drop {:?}", ty); + + // TODO(solson): Call user-defined Drop::drop impls. + + match ty.sty { + ty::TyBox(contents_ty) => { + match self.memory.read_ptr(ptr) { + Ok(contents_ptr) => { + self.drop(contents_ptr, contents_ty)?; + trace!("-deallocating box"); + self.memory.deallocate(contents_ptr)?; + } + Err(EvalError::ReadBytesAsPointer) => { + let size = self.memory.pointer_size; + let possible_drop_fill = self.memory.read_bytes(ptr, size)?; + if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { + return Ok(()); + } else { + return Err(EvalError::ReadBytesAsPointer); + } + } + Err(e) => return Err(e), + } + } + + // TODO(solson): Implement drop for other relevant types (e.g. aggregates). + _ => {} + } + + // Filling drop. + // FIXME(solson): Trait objects (with no static size) probably get filled, too. + let size = self.type_size(ty); + self.memory.drop_fill(ptr, size)?; + + Ok(()) + } +} + +#[derive(Debug)] +struct ImplMethod<'tcx> { + method: Rc>, + substs: &'tcx Substs<'tcx>, + is_provided: bool, +} + +/// Locates the applicable definition of a method, given its name. +fn get_impl_method<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + impl_def_id: DefId, + substs: &'tcx Substs<'tcx>, + name: ast::Name, +) -> ImplMethod<'tcx> { + assert!(!substs.types.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + let substs = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let substs = traits::translate_substs(&infcx, impl_def_id, + substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("trans::meth::get_impl_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); + ImplMethod { + method: node_item.item, + substs: substs, + is_provided: node_item.node.is_from_trait(), + } + } + None => { + bug!("method {:?} not found in {:?}", name, impl_def_id) + } + } +} From 3404a9da2a6f100a94b6bb36942e31ff2a2b3c52 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 09:53:26 +0200 Subject: [PATCH 0419/1096] add test for invalid char --- tests/compile-fail/match_char.rs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/compile-fail/match_char.rs diff --git a/tests/compile-fail/match_char.rs b/tests/compile-fail/match_char.rs new file mode 100644 index 0000000000000..a91c7fef6aa1e --- /dev/null +++ b/tests/compile-fail/match_char.rs @@ -0,0 +1,8 @@ +fn main() { + assert!(std::char::from_u32(-1_i32 as u32).is_none()); + match unsafe { std::mem::transmute::(-1) } { + 'a' => {}, //~ERROR tried to interpret an invalid 32-bit value as a char: 4294967295 + 'b' => {}, + _ => {}, + } +} From b33a9f34311834807f580191394e45136bd58210 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Jun 2016 11:11:51 +0200 Subject: [PATCH 0420/1096] there can never be too many tests --- tests/compile-fail/invalid_enum_discriminant.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/compile-fail/invalid_enum_discriminant.rs diff --git a/tests/compile-fail/invalid_enum_discriminant.rs b/tests/compile-fail/invalid_enum_discriminant.rs new file mode 100644 index 0000000000000..bde78200b3c47 --- /dev/null +++ b/tests/compile-fail/invalid_enum_discriminant.rs @@ -0,0 +1,14 @@ +#[repr(C)] +pub enum Foo { + A, B, C, D +} + +fn main() { + let f = unsafe { std::mem::transmute::(42) }; + match f { + Foo::A => {}, //~ ERROR invalid enum discriminant value read + Foo::B => {}, + Foo::C => {}, + Foo::D => {}, + } +} From 055b6a8d3824b9a97f601346b8aa051fd6f827ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 09:36:24 +0200 Subject: [PATCH 0421/1096] store full TargetDataLayout in Memory instead of just pointer size --- src/error.rs | 2 +- src/interpreter/mod.rs | 10 +++------- src/memory.rs | 12 ++++++------ tests/compiletest.rs | 2 +- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/error.rs b/src/error.rs index da5d8cf787f49..ec6ac0cbcc6fc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ pub enum EvalError<'tcx> { PointerOutOfBounds { ptr: Pointer, size: usize, - allocation_size: usize, + allocation_size: u64, }, ReadPointerAsBytes, ReadBytesAsPointer, diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7b65d5f645742..5531718a879ec 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -35,7 +35,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { mir_cache: RefCell>>>, /// The virtual memory system. - memory: Memory<'tcx>, + memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. statics: HashMap, Pointer>, @@ -138,11 +138,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - memory: Memory::new(tcx.sess - .target - .uint_type - .bit_width() - .expect("Session::target::uint_type was usize")/8), + memory: Memory::new(&tcx.data_layout), statics: HashMap::new(), stack: Vec::new(), } @@ -162,7 +158,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.memory } - pub fn memory_mut(&mut self) -> &mut Memory<'tcx> { + pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { &mut self.memory } diff --git a/src/memory.rs b/src/memory.rs index a16fbc57a612a..cf704c36cb205 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,6 +6,7 @@ use std::{fmt, iter, mem, ptr}; use rustc::hir::def_id::DefId; use rustc::ty::BareFnTy; use rustc::ty::subst::Substs; +use rustc::ty::layout::{Size, TargetDataLayout}; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -53,7 +54,7 @@ pub struct FunctionDefinition<'tcx> { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory<'tcx> { +pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, /// Function "allocations". They exist solely so pointers have something to point to, and @@ -62,18 +63,17 @@ pub struct Memory<'tcx> { /// Inverse map of `functions` so we don't allocate a new pointer every time we need one function_alloc_cache: HashMap, AllocId>, next_id: AllocId, - pub pointer_size: usize, + pub layout: &'a TargetDataLayout, } -impl<'tcx> Memory<'tcx> { - // FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.) - pub fn new(pointer_size: usize) -> Self { +impl<'a, 'tcx> Memory<'a, 'tcx> { + pub fn new(layout: &'a TargetDataLayout) -> Self { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), next_id: AllocId(0), - pointer_size: pointer_size, + layout: layout, } } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 5430501f05225..a401257c6ae7e 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -77,7 +77,7 @@ fn compile_test() { match cmd.output() { Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), Ok(output) => { - writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap(); + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); panic!("some tests failed"); From 205a988c1bfa6123974e823a8bde58e88a897c34 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 09:40:01 +0200 Subject: [PATCH 0422/1096] improve rustdoc rendering --- src/memory.rs | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index cf704c36cb205..acf2b20b965a7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -155,11 +155,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } +} - //////////////////////////////////////////////////////////////////////////////// - // Allocation accessors - //////////////////////////////////////////////////////////////////////////////// - +/// Allocation accessors +impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { match self.alloc_map.get(&id) { Some(alloc) => Ok(alloc), @@ -245,11 +244,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } } +} - //////////////////////////////////////////////////////////////////////////////// - // Byte accessors - //////////////////////////////////////////////////////////////////////////////// - +/// Byte accessors +impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { @@ -287,11 +285,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) } +} - //////////////////////////////////////////////////////////////////////////////// - // Reading and writing - //////////////////////////////////////////////////////////////////////////////// - +/// Reading and writing +impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; @@ -422,11 +419,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let size = self.pointer_size; self.write_uint(ptr, n, size) } +} - //////////////////////////////////////////////////////////////////////////////// - // Relocations - //////////////////////////////////////////////////////////////////////////////// - +/// Relocations +impl<'a, 'tcx> Memory<'a, 'tcx> { fn relocations(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, btree_map::Range> { @@ -478,11 +474,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_mut(dest.alloc_id)?.relocations.extend(relocations); Ok(()) } +} - //////////////////////////////////////////////////////////////////////////////// - // Undefined bytes - //////////////////////////////////////////////////////////////////////////////// - +/// Undefined bytes +impl<'a, 'tcx> Memory<'a, 'tcx> { // FIXME(solson): This is a very naive, slow version. fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. From d13153c424b77b19096bd5cc52d6ff72cce4323b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 09:59:16 +0200 Subject: [PATCH 0423/1096] add a pointer_size method to Memory for easy access --- src/memory.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index acf2b20b965a7..64774a27f2253 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,7 +6,7 @@ use std::{fmt, iter, mem, ptr}; use rustc::hir::def_id::DefId; use rustc::ty::BareFnTy; use rustc::ty::subst::Substs; -use rustc::ty::layout::{Size, TargetDataLayout}; +use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -155,6 +155,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } + + pub fn pointer_size(&self) -> usize { + self.layout.pointer_size.bytes() as usize + } } /// Allocation accessors From 4c7aae73bc2e7484b27cad5658cf5de9c6bad2b4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 10:00:31 +0200 Subject: [PATCH 0424/1096] adjust all pointer_size checks to use the method --- src/error.rs | 2 +- src/interpreter/mod.rs | 16 ++++++++-------- src/interpreter/terminator.rs | 6 +++--- src/memory.rs | 22 +++++++++++----------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/error.rs b/src/error.rs index ec6ac0cbcc6fc..da5d8cf787f49 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ pub enum EvalError<'tcx> { PointerOutOfBounds { ptr: Pointer, size: usize, - allocation_size: u64, + allocation_size: usize, }, ReadPointerAsBytes, ReadBytesAsPointer, diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5531718a879ec..3a31aa4f36c22 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(ptr) } Str(ref s) => { - let psize = self.memory.pointer_size; + let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len()); let ptr = self.memory.allocate(psize * 2); self.memory.write_bytes(static_ptr, s.as_bytes())?; @@ -187,7 +187,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(ptr) } ByteStr(ref bs) => { - let psize = self.memory.pointer_size; + let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(bs.len()); let ptr = self.memory.allocate(psize); self.memory.write_bytes(static_ptr, bs)?; @@ -511,7 +511,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { - let len_ptr = dest.offset(self.memory.pointer_size as isize); + let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(len_ptr, len)?; } LvalueExtra::DowncastVariant(..) => @@ -537,7 +537,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let len_ptr = dest.offset(self.memory.pointer_size as isize); + let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(len_ptr, length as u64)?; } @@ -651,7 +651,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Size::from_bytes(0)) } FatPointer { .. } => { - let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size; + let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size(); Ok(Size::from_bytes(bytes as u64)) } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))), @@ -762,7 +762,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { - let len_ptr = base.ptr.offset(self.memory.pointer_size as isize); + let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize); let len = self.memory.read_usize(len_ptr)?; LvalueExtra::Length(len) } @@ -811,7 +811,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy}; - let val = match (self.memory.pointer_size, &ty.sty) { + let val = match (self.memory.pointer_size(), &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), (_, &ty::TyChar) => { let c = self.memory.read_uint(ptr, 4)? as u32; @@ -919,7 +919,7 @@ pub fn eval_main<'a, 'tcx: 'a>( if mir.arg_decls.len() == 2 { // start function - let ptr_size = ecx.memory().pointer_size; + let ptr_size = ecx.memory().pointer_size(); let nargs = ecx.memory_mut().allocate(ptr_size); ecx.memory_mut().write_usize(nargs, 0).unwrap(); let args = ecx.memory_mut().allocate(ptr_size); diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 3d7bef825b900..536d9b678ab37 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -277,7 +277,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect(); let args_ptrs = args_res?; - let pointer_size = self.memory.pointer_size; + let pointer_size = self.memory.pointer_size(); match name { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size as isize; + let ptr_size = self.memory.pointer_size() as isize; let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, pointer_size)?; } @@ -557,7 +557,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.deallocate(contents_ptr)?; } Err(EvalError::ReadBytesAsPointer) => { - let size = self.memory.pointer_size; + let size = self.memory.pointer_size(); let possible_drop_fill = self.memory.read_bytes(ptr, size)?; if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { return Ok(()); diff --git a/src/memory.rs b/src/memory.rs index 64774a27f2253..89c7ad7b7506f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -238,11 +238,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if !relocations.is_empty() { print!("{:1$}", "", prefix.len()); // Print spaces. let mut pos = 0; - let relocation_width = (self.pointer_size - 1) * 3; + let relocation_width = (self.pointer_size() - 1) * 3; for (i, target_id) in relocations { print!("{:1$}", "", (i - pos) * 3); print!("└{0:─^1$}┘ ", format!("({})", target_id), relocation_width); - pos = i + self.pointer_size; + pos = i + self.pointer_size(); } println!(""); } @@ -337,7 +337,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { - let size = self.pointer_size; + let size = self.pointer_size(); self.check_defined(ptr, size)?; let offset = self.get_bytes_unchecked(ptr, size)? .read_uint::(size).unwrap() as usize; @@ -350,7 +350,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> { { - let size = self.pointer_size; + let size = self.pointer_size(); let mut bytes = self.get_bytes_mut(dest, size)?; bytes.write_uint::(ptr.offset as u64, size).unwrap(); } @@ -359,7 +359,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - let pointer_size = self.pointer_size; + let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), @@ -407,20 +407,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { - self.read_int(ptr, self.pointer_size) + self.read_int(ptr, self.pointer_size()) } pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx, ()> { - let size = self.pointer_size; + let size = self.pointer_size(); self.write_int(ptr, n, size) } pub fn read_usize(&self, ptr: Pointer) -> EvalResult<'tcx, u64> { - self.read_uint(ptr, self.pointer_size) + self.read_uint(ptr, self.pointer_size()) } pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { - let size = self.pointer_size; + let size = self.pointer_size(); self.write_uint(ptr, n, size) } } @@ -430,7 +430,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn relocations(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, btree_map::Range> { - let start = ptr.offset.saturating_sub(self.pointer_size - 1); + let start = ptr.offset.saturating_sub(self.pointer_size() - 1); let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } @@ -444,7 +444,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let start = ptr.offset; let end = start + size; let first = *keys.first().unwrap(); - let last = *keys.last().unwrap() + self.pointer_size; + let last = *keys.last().unwrap() + self.pointer_size(); let alloc = self.get_mut(ptr.alloc_id)?; From 86040c0d295d84a0875e44a296372e745e1314c3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 13:04:05 +0200 Subject: [PATCH 0425/1096] simplify write_ptr --- src/memory.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 89c7ad7b7506f..9718527280df7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -349,11 +349,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> { - { - let size = self.pointer_size(); - let mut bytes = self.get_bytes_mut(dest, size)?; - bytes.write_uint::(ptr.offset as u64, size).unwrap(); - } + self.write_usize(dest, ptr.offset as u64)?; self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); Ok(()) } From 0288486b731b3da05dd4b42635bfcf3dae285400 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 15:16:25 +0200 Subject: [PATCH 0426/1096] use target byte order --- src/memory.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 9718527280df7..d537e9fc91ecb 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,4 @@ -use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian, self}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, mem, ptr}; @@ -6,7 +6,7 @@ use std::{fmt, iter, mem, ptr}; use rustc::hir::def_id::DefId; use rustc::ty::BareFnTy; use rustc::ty::subst::Substs; -use rustc::ty::layout::TargetDataLayout; +use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -159,6 +159,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn pointer_size(&self) -> usize { self.layout.pointer_size.bytes() as usize } + + pub fn endianess(&self) -> layout::Endian { + self.layout.endian + } } /// Allocation accessors @@ -340,7 +344,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let size = self.pointer_size(); self.check_defined(ptr, size)?; let offset = self.get_bytes_unchecked(ptr, size)? - .read_uint::(size).unwrap() as usize; + .read_target_uint(size, self.endianess()).unwrap() as usize; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), @@ -387,19 +391,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { - self.get_bytes(ptr, size).map(|mut b| b.read_int::(size).unwrap()) + self.get_bytes(ptr, size).map(|mut b| b.read_target_int(size, self.endianess()).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { - self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::(n, size).unwrap()) + let endianess = self.endianess(); + self.get_bytes_mut(ptr, size).map(|mut b| b.write_target_int(n, size, endianess).unwrap()) } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { - self.get_bytes(ptr, size).map(|mut b| b.read_uint::(size).unwrap()) + self.get_bytes(ptr, size).map(|mut b| b.read_target_uint(size, self.endianess()).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { - self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::(n, size).unwrap()) + let endianess = self.endianess(); + self.get_bytes_mut(ptr, size).map(|mut b| b.write_target_uint(n, size, endianess).unwrap()) } pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { @@ -585,3 +591,43 @@ impl UndefMask { fn bit_index(bits: usize) -> (usize, usize) { (bits / BLOCK_SIZE, bits % BLOCK_SIZE) } + +trait ReadBytesExt2 { + fn read_target_uint(&mut self, nbytes: usize, endian: layout::Endian) -> Result; + fn read_target_int(&mut self, nbytes: usize, endian: layout::Endian) -> Result; +} + +impl ReadBytesExt2 for T { + fn read_target_uint(&mut self, nbytes: usize, endian: layout::Endian) -> Result { + match endian { + layout::Endian::Little => ReadBytesExt::read_uint::(self, nbytes), + layout::Endian::Big => ReadBytesExt::read_uint::(self, nbytes), + } + } + fn read_target_int(&mut self, nbytes: usize, endian: layout::Endian) -> Result { + match endian { + layout::Endian::Little => ReadBytesExt::read_int::(self, nbytes), + layout::Endian::Big => ReadBytesExt::read_int::(self, nbytes), + } + } +} + +trait WriteBytesExt2 { + fn write_target_uint(&mut self, data: u64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error>; + fn write_target_int(&mut self, data: i64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error>; +} + +impl WriteBytesExt2 for T { + fn write_target_uint(&mut self, data: u64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error> { + match endian { + layout::Endian::Little => WriteBytesExt::write_uint::(self, data, nbytes), + layout::Endian::Big => WriteBytesExt::write_uint::(self, data, nbytes), + } + } + fn write_target_int(&mut self, data: i64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error> { + match endian { + layout::Endian::Little => WriteBytesExt::write_int::(self, data, nbytes), + layout::Endian::Big => WriteBytesExt::write_int::(self, data, nbytes), + } + } +} From e23fdd1f4951165be7e3b604be6ba742027b0b94 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Jun 2016 15:40:46 +0200 Subject: [PATCH 0427/1096] fix const -> pointer writing (noticeable on big endian) --- src/interpreter/mod.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3a31aa4f36c22..e8e3f09621ddf 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -169,14 +169,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { use rustc::middle::const_val::ConstVal::*; + use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; + macro_rules! i2p { + ($i:ident, $n:expr) => {{ + let ptr = self.memory.allocate($n); + self.memory.write_int(ptr, $i as i64, $n)?; + Ok(ptr) + }} + } match *const_val { Float(_f) => unimplemented!(), - Integral(int) => { - // TODO(solson): Check int constant type. - let ptr = self.memory.allocate(8); - self.memory.write_uint(ptr, int.to_u64_unchecked(), 8)?; - Ok(ptr) - } + Integral(ConstInt::Infer(_)) => unreachable!(), + Integral(ConstInt::InferSigned(_)) => unreachable!(), + Integral(ConstInt::I8(i)) => i2p!(i, 1), + Integral(ConstInt::U8(i)) => i2p!(i, 1), + Integral(ConstInt::I16(i)) => i2p!(i, 2), + Integral(ConstInt::U16(i)) => i2p!(i, 2), + Integral(ConstInt::I32(i)) => i2p!(i, 4), + Integral(ConstInt::U32(i)) => i2p!(i, 4), + Integral(ConstInt::I64(i)) => i2p!(i, 8), + Integral(ConstInt::U64(i)) => i2p!(i, 8), + Integral(ConstInt::Isize(ConstIsize::Is16(i))) => i2p!(i, 2), + Integral(ConstInt::Isize(ConstIsize::Is32(i))) => i2p!(i, 4), + Integral(ConstInt::Isize(ConstIsize::Is64(i))) => i2p!(i, 8), + Integral(ConstInt::Usize(ConstUsize::Us16(i))) => i2p!(i, 2), + Integral(ConstInt::Usize(ConstUsize::Us32(i))) => i2p!(i, 4), + Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8), Str(ref s) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len()); From 37287d2a5d68888f4a39539d148921b57cc3ac49 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sat, 25 Jun 2016 16:50:33 +0200 Subject: [PATCH 0428/1096] use free methods instead of traits --- src/memory.rs | 89 +++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index d537e9fc91ecb..dd3c491e6b2b9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -343,8 +343,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { let size = self.pointer_size(); self.check_defined(ptr, size)?; - let offset = self.get_bytes_unchecked(ptr, size)? - .read_target_uint(size, self.endianess()).unwrap() as usize; + let endianess = self.endianess(); + let bytes = self.get_bytes_unchecked(ptr, size)?; + let offset = read_target_uint(endianess, bytes).unwrap() as usize; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), @@ -391,21 +392,25 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { - self.get_bytes(ptr, size).map(|mut b| b.read_target_int(size, self.endianess()).unwrap()) + self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - self.get_bytes_mut(ptr, size).map(|mut b| b.write_target_int(n, size, endianess).unwrap()) + let b = self.get_bytes_mut(ptr, size)?; + write_target_int(endianess, b, n).unwrap(); + Ok(()) } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { - self.get_bytes(ptr, size).map(|mut b| b.read_target_uint(size, self.endianess()).unwrap()) + self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - self.get_bytes_mut(ptr, size).map(|mut b| b.write_target_uint(n, size, endianess).unwrap()) + let b = self.get_bytes_mut(ptr, size)?; + write_target_uint(endianess, b, n).unwrap(); + Ok(()) } pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { @@ -515,6 +520,38 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } +//////////////////////////////////////////////////////////////////////////////// +// Methods to access integers in the target endianess +//////////////////////////////////////////////////////////////////////////////// + +fn write_target_uint(endianess: layout::Endian, mut target: &mut [u8], data: u64) -> Result<(), byteorder::Error> { + let len = target.len(); + match endianess { + layout::Endian::Little => target.write_uint::(data, len), + layout::Endian::Big => target.write_uint::(data, len), + } +} +fn write_target_int(endianess: layout::Endian, mut target: &mut [u8], data: i64) -> Result<(), byteorder::Error> { + let len = target.len(); + match endianess { + layout::Endian::Little => target.write_int::(data, len), + layout::Endian::Big => target.write_int::(data, len), + } +} + +fn read_target_uint(endianess: layout::Endian, mut source: &[u8]) -> Result { + match endianess { + layout::Endian::Little => source.read_uint::(source.len()), + layout::Endian::Big => source.read_uint::(source.len()), + } +} +fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result { + match endianess { + layout::Endian::Little => source.read_int::(source.len()), + layout::Endian::Big => source.read_int::(source.len()), + } +} + //////////////////////////////////////////////////////////////////////////////// // Undefined byte tracking //////////////////////////////////////////////////////////////////////////////// @@ -591,43 +628,3 @@ impl UndefMask { fn bit_index(bits: usize) -> (usize, usize) { (bits / BLOCK_SIZE, bits % BLOCK_SIZE) } - -trait ReadBytesExt2 { - fn read_target_uint(&mut self, nbytes: usize, endian: layout::Endian) -> Result; - fn read_target_int(&mut self, nbytes: usize, endian: layout::Endian) -> Result; -} - -impl ReadBytesExt2 for T { - fn read_target_uint(&mut self, nbytes: usize, endian: layout::Endian) -> Result { - match endian { - layout::Endian::Little => ReadBytesExt::read_uint::(self, nbytes), - layout::Endian::Big => ReadBytesExt::read_uint::(self, nbytes), - } - } - fn read_target_int(&mut self, nbytes: usize, endian: layout::Endian) -> Result { - match endian { - layout::Endian::Little => ReadBytesExt::read_int::(self, nbytes), - layout::Endian::Big => ReadBytesExt::read_int::(self, nbytes), - } - } -} - -trait WriteBytesExt2 { - fn write_target_uint(&mut self, data: u64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error>; - fn write_target_int(&mut self, data: i64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error>; -} - -impl WriteBytesExt2 for T { - fn write_target_uint(&mut self, data: u64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error> { - match endian { - layout::Endian::Little => WriteBytesExt::write_uint::(self, data, nbytes), - layout::Endian::Big => WriteBytesExt::write_uint::(self, data, nbytes), - } - } - fn write_target_int(&mut self, data: i64, nbytes: usize, endian: layout::Endian) -> Result<(), byteorder::Error> { - match endian { - layout::Endian::Little => WriteBytesExt::write_int::(self, data, nbytes), - layout::Endian::Big => WriteBytesExt::write_int::(self, data, nbytes), - } - } -} From 7d574f7b1c842f2675f945528b61064d599366d0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 28 Jun 2016 15:06:44 +0200 Subject: [PATCH 0429/1096] don't execute the first statement of a constant/static/promoted right away This might create confusion, because attempting to execute a statement can cause arbitrary stackframes to be added for the constants/statics/promoteds required by that statement. Before this commit, the first statement of the last added stackframe was executed immediately. Thus there was no way to inspect the state before that first statement. --- src/interpreter/step.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 37a7aa4d01179..6d4d8aad2cc0a 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -39,11 +39,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }.visit_statement(block, stmt); if current_stack == self.stack.len() { self.statement(stmt)?; - } else { - // ConstantExtractor added some new frames for statics/constants/promoteds - // self.step() can't be "done", so it can't return false - assert!(self.step()?); } + // if ConstantExtractor added new frames, we don't execute anything here + // but await the next call to step return Ok(true); } @@ -58,11 +56,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }.visit_terminator(block, terminator); if current_stack == self.stack.len() { self.terminator(terminator)?; - } else { - // ConstantExtractor added some new frames for statics/constants/promoteds - // self.step() can't be "done", so it can't return false - assert!(self.step()?); } + // if ConstantExtractor added new frames, we don't execute anything here + // but await the next call to step Ok(true) } From ae3c49a9e52b7f1fcc2fef3cb97d7a69265dfa9c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 29 Jun 2016 17:07:05 +0200 Subject: [PATCH 0430/1096] use the item path printer that prints user friendly textual paths --- src/interpreter/mod.rs | 3 +-- tests/compile-fail/env_args.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e8e3f09621ddf..7354602513bc0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -236,7 +236,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { - use rustc_trans::back::symbol_names::def_id_to_string; match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), None => { @@ -247,7 +246,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for `{}`", def_id_to_string(self.tcx, def_id)); + panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); diff --git a/tests/compile-fail/env_args.rs b/tests/compile-fail/env_args.rs index b0068785e17ec..ac061920b53c1 100644 --- a/tests/compile-fail/env_args.rs +++ b/tests/compile-fail/env_args.rs @@ -1,4 +1,4 @@ -//error-pattern: no mir for `std +//error-pattern: no mir for `std::env::args` fn main() { let x = std::env::args(); From b91338b2200d9452a4ddfc1deb4ee5ba1f20f6b9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 30 Jun 2016 11:29:25 +0200 Subject: [PATCH 0431/1096] things priroda needs to be public or changed --- src/interpreter/mod.rs | 8 ++++---- src/lib.rs | 6 +++++- src/memory.rs | 8 ++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7354602513bc0..d17640bb675a6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -154,7 +154,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn memory(&self) -> &Memory { + pub fn memory(&self) -> &Memory<'a, 'tcx> { &self.memory } @@ -162,7 +162,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self.memory } - pub fn stack(&self) -> &[Frame] { + pub fn stack(&self) -> &[Frame<'a, 'tcx>] { &self.stack } @@ -235,7 +235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + pub fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { match self.tcx.map.as_local_node_id(def_id) { Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), None => { @@ -255,7 +255,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { + pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, substs); self.tcx.normalize_associated_type(&substituted) } diff --git a/src/lib.rs b/src/lib.rs index 3745a960a271e..93dfe4e220174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,4 +38,8 @@ pub use interpreter::{ eval_main, }; -pub use memory::Memory; +pub use memory::{ + Memory, + Pointer, + AllocId, +}; diff --git a/src/memory.rs b/src/memory.rs index dd3c491e6b2b9..e3e4279049a98 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -16,7 +16,7 @@ use primval::PrimVal; //////////////////////////////////////////////////////////////////////////////// #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub struct AllocId(u64); +pub struct AllocId(pub u64); impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -77,6 +77,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } + pub fn allocations<'b>(&'b self) -> ::std::collections::hash_map::Iter<'b, AllocId, Allocation> { + self.alloc_map.iter() + } + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { let def = FunctionDefinition { def_id: def_id, @@ -576,7 +580,7 @@ impl UndefMask { } /// Check whether the range `start..end` (end-exclusive) is entirely defined. - fn is_range_defined(&self, start: usize, end: usize) -> bool { + pub fn is_range_defined(&self, start: usize, end: usize) -> bool { if end > self.len { return false; } for i in start..end { if !self.get(i) { return false; } From 756d73b3ca9a64a8d0d6e3b7d0dd29b6cac853be Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 30 Jun 2016 21:30:03 -0600 Subject: [PATCH 0432/1096] Remove filling drop to prep for elaborated drops. --- src/interpreter/mod.rs | 3 --- src/interpreter/terminator.rs | 36 +++++++---------------------------- src/lib.rs | 1 - src/memory.rs | 6 +----- 4 files changed, 8 insertions(+), 38 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d17640bb675a6..dd352cc241088 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -820,9 +820,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); self.memory.copy(src, dest, size)?; - if self.type_needs_drop(ty) { - self.memory.drop_fill(src, size)?; - } Ok(()) } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 536d9b678ab37..a9bb1fa5317e8 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -6,7 +6,7 @@ use rustc::ty::layout::Layout; use rustc::ty::subst::{self, Substs}; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use std::rc::Rc; -use std::{iter, mem}; +use std::iter; use syntax::{ast, attr}; use syntax::codemap::{DUMMY_SP, Span}; @@ -303,11 +303,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_uint(dest, discr_val, 8)?; } - "forget" => { - let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty); - self.memory.drop_fill(args_ptrs[0], arg_size)?; - } + "forget" => {} "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, @@ -549,35 +545,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Call user-defined Drop::drop impls. match ty.sty { - ty::TyBox(contents_ty) => { - match self.memory.read_ptr(ptr) { - Ok(contents_ptr) => { - self.drop(contents_ptr, contents_ty)?; - trace!("-deallocating box"); - self.memory.deallocate(contents_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let size = self.memory.pointer_size(); - let possible_drop_fill = self.memory.read_bytes(ptr, size)?; - if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { - return Ok(()); - } else { - return Err(EvalError::ReadBytesAsPointer); - } - } - Err(e) => return Err(e), - } + ty::TyBox(_contents_ty) => { + let contents_ptr = self.memory.read_ptr(ptr)?; + // self.drop(contents_ptr, contents_ty)?; + trace!("-deallocating box"); + self.memory.deallocate(contents_ptr)?; } // TODO(solson): Implement drop for other relevant types (e.g. aggregates). _ => {} } - // Filling drop. - // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty); - self.memory.drop_fill(ptr, size)?; - Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 93dfe4e220174..83a1aeec68b1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ btree_range, collections, collections_bound, - filling_drop, question_mark, rustc_private, pub_restricted, diff --git a/src/memory.rs b/src/memory.rs index e3e4279049a98..64c105a4e7f96 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian, self}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{fmt, iter, mem, ptr}; +use std::{fmt, iter, ptr}; use rustc::hir::def_id::DefId; use rustc::ty::BareFnTy; @@ -340,10 +340,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn drop_fill(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { - self.write_repeat(ptr, mem::POST_DROP_U8, size) - } - pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { let size = self.pointer_size(); self.check_defined(ptr, size)?; From 64eca52ad3fcb0c3621feadf001a9491ab84410c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 30 Jun 2016 21:33:24 -0600 Subject: [PATCH 0433/1096] Run Mir passes (copied from rustc pre-trans). --- benches/helpers/miri_helper.rs | 7 +++++-- src/bin/miri.rs | 8 ++++++-- src/interpreter/mod.rs | 19 +++++++++++++++++++ src/lib.rs | 2 ++ tests/compile-fail/execute_memory.rs | 6 +++--- tests/compile-fail/out_of_bounds_read.rs | 2 +- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index c8ae22e1ace57..cdd1412204f99 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -4,8 +4,9 @@ extern crate rustc; extern crate rustc_driver; extern crate test; -use self::miri::eval_main; +use self::miri::{eval_main, run_mir_passes}; use self::rustc::session::Session; +use self::rustc::mir::mir_map::MirMap; use self::rustc_driver::{driver, CompilerCalls, Compilation}; use std::cell::RefCell; use std::rc::Rc; @@ -55,7 +56,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { let (node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); - bencher.borrow_mut().iter(|| { eval_main(tcx, mir_map, node_id); }); + let mut mir_map = MirMap { map: mir_map.map.clone() }; + run_mir_passes(tcx, &mut mir_map); + bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id); }); state.session.abort_if_errors(); }); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6a9ad5ce8387f..4340729d7e02a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -9,8 +9,9 @@ extern crate log_settings; extern crate syntax; #[macro_use] extern crate log; -use miri::eval_main; +use miri::{eval_main, run_mir_passes}; use rustc::session::Session; +use rustc::mir::mir_map::MirMap; use rustc_driver::{driver, CompilerCalls, Compilation}; struct MiriCompilerCalls; @@ -31,7 +32,10 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mir_map = state.mir_map.unwrap(); let (node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); - eval_main(tcx, mir_map, node_id); + + let mut mir_map = MirMap { map: mir_map.map.clone() }; + run_mir_passes(tcx, &mut mir_map); + eval_main(tcx, &mir_map, node_id); state.session.abort_if_errors(); }); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index dd352cc241088..a572c9b2c70ba 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -980,6 +980,25 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { err.emit(); } +pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &mut MirMap<'tcx>) { + let mut passes = ::rustc::mir::transform::Passes::new(); + passes.push_hook(Box::new(::rustc_mir::transform::dump_mir::DumpMir)); + passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); + passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads"))); + + passes.push_pass(Box::new(::rustc_mir::transform::erase_regions::EraseRegions)); + + passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); + passes.push_pass(Box::new(::rustc_borrowck::ElaborateDrops)); + passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); + passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"))); + + passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); + passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); + + passes.run_passes(tcx, mir_map); +} + // TODO(solson): Upstream these methods into rustc::ty::layout. trait IntegerExt { diff --git a/src/lib.rs b/src/lib.rs index 83a1aeec68b1b..46bca86e5c532 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ // From rustc. #[macro_use] extern crate rustc; +extern crate rustc_borrowck; extern crate rustc_data_structures; extern crate rustc_mir; extern crate rustc_trans; @@ -35,6 +36,7 @@ pub use interpreter::{ EvalContext, Frame, eval_main, + run_mir_passes, }; pub use memory::{ diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs index 413c6602114ba..054f91b3cf924 100644 --- a/tests/compile-fail/execute_memory.rs +++ b/tests/compile-fail/execute_memory.rs @@ -1,8 +1,8 @@ #![feature(box_syntax)] -fn main() { - //FIXME: this span is wrong - let x = box 42; //~ ERROR: tried to treat a memory pointer as a function pointer +// FIXME: This span is wrong. +fn main() { //~ ERROR: tried to treat a memory pointer as a function pointer + let x = box 42; unsafe { let f = std::mem::transmute::, fn()>(x); f() diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index 3e9e87cdc6c9e..fef6ff66c3071 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation 29 which has size 2 + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation 31 which has size 2 panic!("this should never print: {}", x); } From 1720b1f4af5edaf10cdc97a81d233c81a16eee0d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 30 Jun 2016 21:39:35 -0600 Subject: [PATCH 0434/1096] Remove AddCallGuards. It's useless for Miri. --- src/interpreter/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a572c9b2c70ba..4e564005f23ff 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -988,12 +988,9 @@ pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &mut MirMa passes.push_pass(Box::new(::rustc_mir::transform::erase_regions::EraseRegions)); - passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); passes.push_pass(Box::new(::rustc_borrowck::ElaborateDrops)); passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"))); - - passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); passes.run_passes(tcx, mir_map); From 594f1d79da1eebb2acb674b686d98b4fc1ef165f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 30 Jun 2016 16:42:09 +0200 Subject: [PATCH 0435/1096] optimize all ZST allocations into one single allocation --- src/interpreter/terminator.rs | 4 ++-- src/memory.rs | 45 +++++++++++++++++++++++++++++++---- tests/run-pass/zst.rs | 8 +++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index a9bb1fa5317e8..8f5ec0e1b597b 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -423,8 +423,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_reallocate" => { let ptr = self.memory.read_ptr(args[0])?; let size = self.memory.read_usize(args[2])?; - self.memory.reallocate(ptr, size as usize)?; - self.memory.write_ptr(dest, ptr)?; + let new_ptr = self.memory.reallocate(ptr, size as usize)?; + self.memory.write_ptr(dest, new_ptr)?; } "memcmp" => { diff --git a/src/memory.rs b/src/memory.rs index 64c105a4e7f96..25612c435e260 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -39,8 +39,18 @@ pub struct Pointer { impl Pointer { pub fn offset(self, i: isize) -> Self { + // FIXME: prevent offsetting ZST ptrs in tracing mode Pointer { offset: (self.offset as isize + i) as usize, ..self } } + pub fn points_to_zst(&self) -> bool { + self.alloc_id.0 == 0 + } + fn zst_ptr() -> Self { + Pointer { + alloc_id: AllocId(0), + offset: 0, + } + } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] @@ -68,13 +78,25 @@ pub struct Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout) -> Self { - Memory { + let mut mem = Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), - next_id: AllocId(0), + next_id: AllocId(1), layout: layout, - } + }; + // alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST + // (e.g. function pointers, (), [], ...) from requiring memory + let alloc = Allocation { + bytes: Vec::new(), + relocations: BTreeMap::new(), + undef_mask: UndefMask::new(0), + }; + mem.alloc_map.insert(AllocId(0), alloc); + // check that additional zst allocs work + debug_assert!(mem.allocate(0).points_to_zst()); + debug_assert!(mem.get(AllocId(0)).is_ok()); + mem } pub fn allocations<'b>(&'b self) -> ::std::collections::hash_map::Iter<'b, AllocId, Allocation> { @@ -105,6 +127,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn allocate(&mut self, size: usize) -> Pointer { + if size == 0 { + return Pointer::zst_ptr(); + } let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new(), @@ -121,7 +146,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, ()> { + pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> { + if ptr.points_to_zst() { + if new_size != 0 { + return Ok(self.allocate(new_size)); + } else { + return Ok(ptr); + } + } if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); @@ -141,11 +173,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.truncate(new_size); } - Ok(()) + Ok(ptr) } // TODO(solson): See comment on `reallocate`. pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx, ()> { + if ptr.points_to_zst() { + return Ok(()); + } if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs index db98e969306ba..4ebb2001e7203 100644 --- a/tests/run-pass/zst.rs +++ b/tests/run-pass/zst.rs @@ -1,3 +1,9 @@ +// the following flag prevents this test from running on the host machine +// this should only be run on miri, because rust doesn't (yet?) optimize ZSTs of different types +// into the same memory location +// ignore-test + + #[derive(PartialEq, Debug)] struct A; @@ -13,4 +19,6 @@ fn use_zst() -> A { fn main() { assert_eq!(zst_ret(), A); assert_eq!(use_zst(), A); + assert_eq!(&A as *const A as *const (), &() as *const _); + assert_eq!(&A as *const A, &A as *const A); } From 3d9588332f82a085586b0a847715ff3835986861 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 1 Jul 2016 13:08:19 +0200 Subject: [PATCH 0436/1096] address comments --- src/memory.rs | 23 +++++++++++------------ tests/compile-fail/out_of_bounds_read.rs | 2 +- tests/compile-fail/out_of_bounds_read2.rs | 5 +++++ 3 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 tests/compile-fail/out_of_bounds_read2.rs diff --git a/src/memory.rs b/src/memory.rs index 25612c435e260..2f5b4470cf993 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -39,7 +39,6 @@ pub struct Pointer { impl Pointer { pub fn offset(self, i: isize) -> Self { - // FIXME: prevent offsetting ZST ptrs in tracing mode Pointer { offset: (self.offset as isize + i) as usize, ..self } } pub fn points_to_zst(&self) -> bool { @@ -47,7 +46,7 @@ impl Pointer { } fn zst_ptr() -> Self { Pointer { - alloc_id: AllocId(0), + alloc_id: ZST_ALLOC_ID, offset: 0, } } @@ -76,6 +75,8 @@ pub struct Memory<'a, 'tcx> { pub layout: &'a TargetDataLayout, } +const ZST_ALLOC_ID: AllocId = AllocId(0); + impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout) -> Self { let mut mem = Memory { @@ -86,16 +87,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { layout: layout, }; // alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST - // (e.g. function pointers, (), [], ...) from requiring memory + // (e.g. function items, (), [], ...) from requiring memory let alloc = Allocation { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), }; - mem.alloc_map.insert(AllocId(0), alloc); + mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work debug_assert!(mem.allocate(0).points_to_zst()); - debug_assert!(mem.get(AllocId(0)).is_ok()); + debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } @@ -147,17 +148,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> { - if ptr.points_to_zst() { - if new_size != 0 { - return Ok(self.allocate(new_size)); - } else { - return Ok(ptr); - } - } if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } + if ptr.points_to_zst() { + return Ok(self.allocate(new_size)); + } let size = self.get_mut(ptr.alloc_id)?.bytes.len(); @@ -187,10 +184,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } if self.alloc_map.remove(&ptr.alloc_id).is_none() { + debug!("deallocated a pointer twice: {}", ptr.alloc_id); // TODO(solson): Report error about erroneous free. This is blocked on properly tracking // already-dropped state since this if-statement is entered even in safe code without // it. } + debug!("deallocated : {}", ptr.alloc_id); Ok(()) } diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index fef6ff66c3071..f6a305840c241 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation 31 which has size 2 + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2 panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/out_of_bounds_read2.rs b/tests/compile-fail/out_of_bounds_read2.rs new file mode 100644 index 0000000000000..5509a8346e552 --- /dev/null +++ b/tests/compile-fail/out_of_bounds_read2.rs @@ -0,0 +1,5 @@ +fn main() { + let v: Vec = vec![1, 2]; + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation + panic!("this should never print: {}", x); +} From a7cc77a010cb842c258b36130df8b9f601970fde Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 1 Jul 2016 16:40:52 -0600 Subject: [PATCH 0437/1096] Compare against ZST_ALLOC_ID in points_to_zst. --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 2f5b4470cf993..f3072b886b813 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -42,7 +42,7 @@ impl Pointer { Pointer { offset: (self.offset as isize + i) as usize, ..self } } pub fn points_to_zst(&self) -> bool { - self.alloc_id.0 == 0 + self.alloc_id == ZST_ALLOC_ID } fn zst_ptr() -> Self { Pointer { From 4b831569f67a8e8bd5131f92966cd186784b495f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 09:08:24 +0200 Subject: [PATCH 0438/1096] implement floats by running the ops on the host architecture --- src/interpreter/mod.rs | 24 +++++++++++++++++++++--- src/memory.rs | 3 +++ src/primval.rs | 35 +++++++++++++++++++++++++++++++++++ tests/run-pass/floats.rs | 8 ++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/floats.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4e564005f23ff..8ca37a6517715 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -169,7 +169,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { use rustc::middle::const_val::ConstVal::*; - use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; + use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; + use std::mem::transmute; macro_rules! i2p { ($i:ident, $n:expr) => {{ let ptr = self.memory.allocate($n); @@ -178,7 +179,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }} } match *const_val { - Float(_f) => unimplemented!(), + Float(ConstFloat::F32(f)) => { + let i = unsafe { transmute::<_, u32>(f) }; + i2p!(i, 4) + }, + Float(ConstFloat::F64(f)) => { + let i = unsafe { transmute::<_, u64>(f) }; + i2p!(i, 8) + }, + Float(ConstFloat::FInfer{..}) => unreachable!(), Integral(ConstInt::Infer(_)) => unreachable!(), Integral(ConstInt::InferSigned(_)) => unreachable!(), Integral(ConstInt::I8(i)) => i2p!(i, 1), @@ -824,7 +833,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use syntax::ast::{IntTy, UintTy}; + use syntax::ast::{IntTy, UintTy, FloatTy}; + use std::mem::transmute; let val = match (self.memory.pointer_size(), &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), (_, &ty::TyChar) => { @@ -848,6 +858,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), (8, &ty::TyUint(UintTy::Us)) | (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + (_, &ty::TyFloat(FloatTy::F32)) => { + let i = self.memory.read_uint(ptr, 4)? as u32; + PrimVal::F32(unsafe { transmute(i) }) + }, + (_, &ty::TyFloat(FloatTy::F64)) => { + let i = self.memory.read_uint(ptr, 8)?; + PrimVal::F64(unsafe { transmute(i) }) + }, (_, &ty::TyFnDef(def_id, substs, fn_ty)) => { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) diff --git a/src/memory.rs b/src/memory.rs index f3072b886b813..92f69d575f18c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -394,6 +394,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { + use std::mem::transmute; let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), @@ -407,6 +408,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), + PrimVal::F32(f) => self.write_uint(ptr, unsafe { transmute::<_, u32>(f) } as u64, 4), + PrimVal::F64(f) => self.write_uint(ptr, unsafe { transmute::<_, u64>(f) }, 8), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), } diff --git a/src/primval.rs b/src/primval.rs index f3aedfc19743d..f6f9a025770a2 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -13,6 +13,8 @@ pub enum PrimVal { FnPtr(Pointer), IntegerPtr(u64), Char(char), + + F32(f32), F64(f64), } /// returns the result of the operation and whether the operation overflowed @@ -57,6 +59,34 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva }) } + macro_rules! float_binops { + ($v:ident, $l:ident, $r:ident) => ({ + match bin_op { + Add => $v($l + $r), + Sub => $v($l - $r), + Mul => $v($l * $r), + Div => $v($l / $r), + Rem => $v($l % $r), + + // invalid float ops + BitXor => unreachable!(), + BitAnd => unreachable!(), + BitOr => unreachable!(), + Shl => unreachable!(), + Shr => unreachable!(), + + // directly comparing floats is questionable + // miri could forbid it, or at least miri as rust const eval should forbid it + Eq => Bool($l == $r), + Ne => Bool($l != $r), + Lt => Bool($l < $r), + Le => Bool($l <= $r), + Gt => Bool($l > $r), + Ge => Bool($l >= $r), + } + }) + } + fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::BinOp::*; match bin_op { @@ -128,6 +158,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (U16(l), U16(r)) => int_binops!(U16, l, r), (U32(l), U32(r)) => int_binops!(U32, l, r), (U64(l), U64(r)) => int_binops!(U64, l, r), + (F32(l), F32(r)) => float_binops!(F32, l, r), + (F64(l), F64(r)) => float_binops!(F64, l, r), (Char(l), Char(r)) => match bin_op { Eq => Bool(l == r), Ne => Bool(l != r), @@ -211,6 +243,9 @@ pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVa (Not, U16(n)) => Ok(U16(!n)), (Not, U32(n)) => Ok(U32(!n)), (Not, U64(n)) => Ok(U64(!n)), + + (Neg, F64(n)) => Ok(F64(-n)), + (Neg, F32(n)) => Ok(F32(-n)), _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))), } } diff --git a/tests/run-pass/floats.rs b/tests/run-pass/floats.rs new file mode 100644 index 0000000000000..504fffca75608 --- /dev/null +++ b/tests/run-pass/floats.rs @@ -0,0 +1,8 @@ + +fn main() { + assert_eq!(6.0_f32*6.0_f32, 36.0_f32); + assert_eq!(6.0_f64*6.0_f64, 36.0_f64); + assert_eq!(-{5.0_f32}, -5.0_f32); + assert!((5.0_f32/0.0).is_infinite()); + assert!((-5.0_f32).sqrt().is_nan()); +} From 756fbcce480c3ab93a735436f7f27bf38a0dfb10 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 10:47:10 +0200 Subject: [PATCH 0439/1096] add a memory limit --- src/error.rs | 10 +++++++ src/interpreter/mod.rs | 53 ++++++++++++++++++++--------------- src/interpreter/step.rs | 41 +++++++++++++++++++-------- src/interpreter/terminator.rs | 4 +-- src/memory.rs | 36 ++++++++++++++++++------ 5 files changed, 101 insertions(+), 43 deletions(-) diff --git a/src/error.rs b/src/error.rs index da5d8cf787f49..4467347b49bcc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,6 +29,11 @@ pub enum EvalError<'tcx> { ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), InvalidChar(u32), + OutOfMemory { + allocation_size: u64, + memory_size: u64, + memory_usage: u64, + } } pub type EvalResult<'tcx, T> = Result>; @@ -69,6 +74,8 @@ impl<'tcx> Error for EvalError<'tcx> { "mathematical operation failed", EvalError::InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", + EvalError::OutOfMemory{..} => + "could not allocate more memory" } } @@ -90,6 +97,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "{:?} at {:?}", err, span), EvalError::InvalidChar(c) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), + EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => + write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", + allocation_size, memory_size - memory_usage, memory_size), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4e564005f23ff..66d147e2d8b47 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -138,19 +138,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - memory: Memory::new(&tcx.data_layout), + memory: Memory::new(&tcx.data_layout, 100*1024*1024 /* 100MB */), statics: HashMap::new(), stack: Vec::new(), } } - pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { match output_ty { ty::FnConverging(ty) => { let size = self.type_size_with_substs(ty, substs); - Some(self.memory.allocate(size)) + self.memory.allocate(size).map(Some) } - ty::FnDiverging => None, + ty::FnDiverging => Ok(None), } } @@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; macro_rules! i2p { ($i:ident, $n:expr) => {{ - let ptr = self.memory.allocate($n); + let ptr = self.memory.allocate($n)?; self.memory.write_int(ptr, $i as i64, $n)?; Ok(ptr) }} @@ -197,8 +197,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8), Str(ref s) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(s.len()); - let ptr = self.memory.allocate(psize * 2); + let static_ptr = self.memory.allocate(s.len())?; + let ptr = self.memory.allocate(psize * 2)?; self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; @@ -206,19 +206,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ByteStr(ref bs) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(bs.len()); - let ptr = self.memory.allocate(psize); + let static_ptr = self.memory.allocate(bs.len())?; + let ptr = self.memory.allocate(psize)?; self.memory.write_bytes(static_ptr, bs)?; self.memory.write_ptr(ptr, static_ptr)?; Ok(ptr) } Bool(b) => { - let ptr = self.memory.allocate(1); + let ptr = self.memory.allocate(1)?; self.memory.write_bool(ptr, b)?; Ok(ptr) } Char(c) => { - let ptr = self.memory.allocate(4); + let ptr = self.memory.allocate(4)?; self.memory.write_uint(ptr, c as u64, 4)?; Ok(ptr) }, @@ -282,9 +282,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } - pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, - return_ptr: Option) - { + pub fn push_stack_frame( + &mut self, + def_id: DefId, + span: codemap::Span, + mir: CachedMir<'a, 'tcx>, + substs: &'tcx Substs<'tcx>, + return_ptr: Option, + ) -> EvalResult<'tcx, ()> { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); let temp_tys = mir.temp_decls.iter().map(|t| t.ty); @@ -294,7 +299,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation += 1; - let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + let locals: EvalResult<'tcx, Vec> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { let size = self.type_size_with_substs(ty, substs); self.memory.allocate(size) }).collect(); @@ -303,7 +308,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir: mir.clone(), block: mir::START_BLOCK, return_ptr: return_ptr, - locals: locals, + locals: locals?, var_offset: num_args, temp_offset: num_args + num_vars, span: span, @@ -311,6 +316,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, stmt: 0, }); + Ok(()) } fn pop_stack_frame(&mut self) { @@ -538,7 +544,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let size = self.type_size(ty); - let ptr = self.memory.allocate(size); + let ptr = self.memory.allocate(size)?; self.memory.write_ptr(dest, ptr)?; } @@ -686,7 +692,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Ok(self.memory.allocate(0)) + Ok(self.memory.allocate(0)?) } else { let cid = ConstantId { def_id: def_id, @@ -927,16 +933,19 @@ pub fn eval_main<'a, 'tcx: 'a>( let def_id = tcx.map.local_def_id(node_id); let mut ecx = EvalContext::new(tcx, mir_map); let substs = tcx.mk_substs(subst::Substs::empty()); - let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging"); + let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) + .expect("should at least be able to allocate space for the main function's return value") + .expect("main function should not be diverging"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)); + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)) + .expect("could not allocate first stack frame"); if mir.arg_decls.len() == 2 { // start function let ptr_size = ecx.memory().pointer_size(); - let nargs = ecx.memory_mut().allocate(ptr_size); + let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs"); ecx.memory_mut().write_usize(nargs, 0).unwrap(); - let args = ecx.memory_mut().allocate(ptr_size); + let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer"); ecx.memory_mut().write_usize(args, 0).unwrap(); ecx.frame_mut().locals[0] = nargs; ecx.frame_mut().locals[1] = args; diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 6d4d8aad2cc0a..5bfe85fff92fc 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -29,15 +29,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let basic_block = &mir.basic_blocks()[block]; if let Some(ref stmt) = basic_block.statements.get(stmt) { - let current_stack = self.stack.len(); + let mut new = Ok(0); ConstantExtractor { span: stmt.source_info.span, substs: self.substs(), def_id: self.frame().def_id, ecx: self, mir: &mir, + new_constants: &mut new, }.visit_statement(block, stmt); - if current_stack == self.stack.len() { + if new? == 0 { self.statement(stmt)?; } // if ConstantExtractor added new frames, we don't execute anything here @@ -46,15 +47,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let terminator = basic_block.terminator(); - let current_stack = self.stack.len(); + let mut new = Ok(0); ConstantExtractor { span: terminator.source_info.span, substs: self.substs(), def_id: self.frame().def_id, ecx: self, mir: &mir, + new_constants: &mut new, }.visit_terminator(block, terminator); - if current_stack == self.stack.len() { + if new? == 0 { self.terminator(terminator)?; } // if ConstantExtractor added new frames, we don't execute anything here @@ -92,6 +94,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { mir: &'a mir::Mir<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, + new_constants: &'a mut EvalResult<'tcx, u64>, } impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { @@ -105,9 +108,22 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { return; } let mir = self.ecx.load_mir(def_id); - let ptr = self.ecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); - self.ecx.statics.insert(cid.clone(), ptr); - self.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)); + self.try(|this| { + let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; + let ptr = ptr.expect("there's no such thing as an unreachable static"); + this.ecx.statics.insert(cid.clone(), ptr); + this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)) + }); + } + fn try EvalResult<'tcx, ()>>(&mut self, f: F) { + if let Ok(ref mut n) = *self.new_constants { + *n += 1; + } else { + return; + } + if let Err(e) = f(self) { + *self.new_constants = Err(e); + } } } @@ -137,10 +153,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; - let return_ptr = self.ecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); - let mir = CachedMir::Owned(Rc::new(mir)); - self.ecx.statics.insert(cid.clone(), return_ptr); - self.ecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr)); + self.try(|this| { + let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; + let return_ptr = return_ptr.expect("there's no such thing as an unreachable static"); + let mir = CachedMir::Owned(Rc::new(mir)); + this.ecx.statics.insert(cid.clone(), return_ptr); + this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr)) + }); } } } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 8f5ec0e1b597b..2fbc9a63ef68d 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -212,7 +212,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr); + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr)?; for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; @@ -416,7 +416,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { let size = self.memory.read_usize(args[0])?; - let ptr = self.memory.allocate(size as usize); + let ptr = self.memory.allocate(size as usize)?; self.memory.write_ptr(dest, ptr)?; } diff --git a/src/memory.rs b/src/memory.rs index f3072b886b813..2453b1f6e67a8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -66,6 +66,10 @@ pub struct FunctionDefinition<'tcx> { pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, + /// Number of virtual bytes allocated + memory_usage: u64, + /// Maximum number of virtual bytes that may be allocated + memory_size: u64, /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. functions: HashMap>, @@ -78,13 +82,15 @@ pub struct Memory<'a, 'tcx> { const ZST_ALLOC_ID: AllocId = AllocId(0); impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn new(layout: &'a TargetDataLayout) -> Self { + pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self { let mut mem = Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), next_id: AllocId(1), layout: layout, + memory_size: max_memory, + memory_usage: 0, }; // alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST // (e.g. function items, (), [], ...) from requiring memory @@ -95,7 +101,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work - debug_assert!(mem.allocate(0).points_to_zst()); + debug_assert!(mem.allocate(0).unwrap().points_to_zst()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } @@ -127,10 +133,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn allocate(&mut self, size: usize) -> Pointer { + pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> { if size == 0 { - return Pointer::zst_ptr(); + return Ok(Pointer::zst_ptr()); } + if self.memory_size - self.memory_usage < size as u64 { + return Err(EvalError::OutOfMemory { + allocation_size: size as u64, + memory_size: self.memory_size, + memory_usage: self.memory_usage, + }); + } + self.memory_usage += size as u64; let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new(), @@ -139,10 +153,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let id = self.next_id; self.next_id.0 += 1; self.alloc_map.insert(id, alloc); - Pointer { + Ok(Pointer { alloc_id: id, offset: 0, - } + }) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error @@ -153,17 +167,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } if ptr.points_to_zst() { - return Ok(self.allocate(new_size)); + return self.allocate(new_size); } let size = self.get_mut(ptr.alloc_id)?.bytes.len(); if new_size > size { let amount = new_size - size; + self.memory_usage += amount as u64; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { + // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable + // through the memory_usage value, by allocating a lot and reallocating to zero + self.memory_usage -= (size - new_size) as u64; self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.truncate(new_size); @@ -183,7 +201,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - if self.alloc_map.remove(&ptr.alloc_id).is_none() { + if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { + self.memory_usage -= alloc.bytes.len() as u64; + } else { debug!("deallocated a pointer twice: {}", ptr.alloc_id); // TODO(solson): Report error about erroneous free. This is blocked on properly tracking // already-dropped state since this if-statement is entered even in safe code without From 1444cabc081f67ef30fbb86d6088754ec76163ac Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 13:04:46 +0200 Subject: [PATCH 0440/1096] make the memory limit configurable --- src/bin/miri.rs | 34 ++++++++++++++++++++++++++++++++-- src/interpreter/mod.rs | 7 ++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4340729d7e02a..087da20ac8be1 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -4,6 +4,7 @@ extern crate getopts; extern crate miri; extern crate rustc; extern crate rustc_driver; +extern crate rustc_plugin; extern crate env_logger; extern crate log_settings; extern crate syntax; @@ -13,6 +14,7 @@ use miri::{eval_main, run_mir_passes}; use rustc::session::Session; use rustc::mir::mir_map::MirMap; use rustc_driver::{driver, CompilerCalls, Compilation}; +use syntax::ast::MetaItemKind; struct MiriCompilerCalls; @@ -23,7 +25,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { _: &getopts::Matches ) -> driver::CompileController<'a> { let mut control = driver::CompileController::basic(); - + control.after_hir_lowering.callback = Box::new(|state| { + state.session.plugin_attributes.borrow_mut().push(("miri".to_owned(), syntax::feature_gate::AttributeType::Whitelisted)); + }); control.after_analysis.stop = Compilation::Stop; control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); @@ -33,9 +37,35 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let (node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); + let krate = state.hir_crate.as_ref().unwrap(); + let mut memory_size = 100*1024*1024; // 100MB + fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { + match lit.node { + syntax::ast::LitKind::Str(ref s, _) => s.clone(), + _ => panic!("attribute values need to be strings"), + } + } + for attr in krate.attrs.iter() { + match attr.node.value.node { + MetaItemKind::List(ref name, _) if name != "miri" => {} + MetaItemKind::List(_, ref items) => for item in items { + match item.node { + MetaItemKind::NameValue(ref name, ref value) => { + match &**name { + "memory_size" => memory_size = extract_str(value).parse::().expect("not a number"), + _ => state.session.span_err(item.span, "unknown miri attribute"), + } + } + _ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"), + } + }, + _ => {}, + } + } + let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - eval_main(tcx, &mir_map, node_id); + eval_main(tcx, &mir_map, node_id, memory_size); state.session.abort_if_errors(); }); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 66d147e2d8b47..ea1f82f5f62a7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -133,12 +133,12 @@ enum ConstantKind { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64) -> Self { EvalContext { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - memory: Memory::new(&tcx.data_layout, 100*1024*1024 /* 100MB */), + memory: Memory::new(&tcx.data_layout, memory_size), statics: HashMap::new(), stack: Vec::new(), } @@ -928,10 +928,11 @@ pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, node_id: ast::NodeId, + memory_size: u64, ) { let mir = mir_map.map.get(&node_id).expect("no mir for main function"); let def_id = tcx.map.local_def_id(node_id); - let mut ecx = EvalContext::new(tcx, mir_map); + let mut ecx = EvalContext::new(tcx, mir_map, memory_size); let substs = tcx.mk_substs(subst::Substs::empty()); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value") From 3e5d86bb0809cdc84e565880284965f1b06cf73f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 13:04:53 +0200 Subject: [PATCH 0441/1096] test the memory limit --- tests/compile-fail/oom.rs | 11 +++++++++++ tests/compile-fail/oom2.rs | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/compile-fail/oom.rs create mode 100644 tests/compile-fail/oom2.rs diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs new file mode 100644 index 0000000000000..83109a77e6201 --- /dev/null +++ b/tests/compile-fail/oom.rs @@ -0,0 +1,11 @@ +#![feature(custom_attribute)] +#![miri(memory_size="0")] + +fn bar() { + let x = 5; + assert_eq!(x, 6); +} + +fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory + bar(); +} diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs new file mode 100644 index 0000000000000..63c51dbaa7d26 --- /dev/null +++ b/tests/compile-fail/oom2.rs @@ -0,0 +1,39 @@ +#![feature(custom_attribute)] +#![miri(memory_size="1000")] + +fn bar(i: i32) { + if i < 1000 { + bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory + //~^NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + } +} + +fn main() { //~NOTE inside call to main + bar(1); + //~^NOTE inside call to bar +} From 88d98998e1cfdfce8ce7e02a020cc9d19f318bd7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 13:17:40 +0200 Subject: [PATCH 0442/1096] add execution time limit --- src/bin/miri.rs | 4 +++- src/error.rs | 7 +++++-- src/interpreter/mod.rs | 8 +++++--- tests/compile-fail/timeout.rs | 9 +++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 tests/compile-fail/timeout.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 087da20ac8be1..6997bbd011df6 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -39,6 +39,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let krate = state.hir_crate.as_ref().unwrap(); let mut memory_size = 100*1024*1024; // 100MB + let mut step_limit = 1000_000; fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { match lit.node { syntax::ast::LitKind::Str(ref s, _) => s.clone(), @@ -53,6 +54,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { MetaItemKind::NameValue(ref name, ref value) => { match &**name { "memory_size" => memory_size = extract_str(value).parse::().expect("not a number"), + "step_limit" => step_limit = extract_str(value).parse::().expect("not a number"), _ => state.session.span_err(item.span, "unknown miri attribute"), } } @@ -65,7 +67,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - eval_main(tcx, &mir_map, node_id, memory_size); + eval_main(tcx, &mir_map, node_id, memory_size, step_limit); state.session.abort_if_errors(); }); diff --git a/src/error.rs b/src/error.rs index 4467347b49bcc..5c2410ed840a6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,7 +33,8 @@ pub enum EvalError<'tcx> { allocation_size: u64, memory_size: u64, memory_usage: u64, - } + }, + ExecutionTimeLimitReached, } pub type EvalResult<'tcx, T> = Result>; @@ -75,7 +76,9 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", EvalError::OutOfMemory{..} => - "could not allocate more memory" + "could not allocate more memory", + EvalError::ExecutionTimeLimitReached => + "reached the configured maximum execution time", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ea1f82f5f62a7..5b8947ec7353e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -929,6 +929,7 @@ pub fn eval_main<'a, 'tcx: 'a>( mir_map: &'a MirMap<'tcx>, node_id: ast::NodeId, memory_size: u64, + step_limit: u64, ) { let mir = mir_map.map.get(&node_id).expect("no mir for main function"); let def_id = tcx.map.local_def_id(node_id); @@ -952,17 +953,18 @@ pub fn eval_main<'a, 'tcx: 'a>( ecx.frame_mut().locals[1] = args; } - loop { + for _ in 0..step_limit { match ecx.step() { Ok(true) => {} - Ok(false) => break, + Ok(false) => return, // FIXME: diverging functions can end up here in some future miri Err(e) => { report(tcx, &ecx, e); - break; + return; } } } + report(tcx, &ecx, EvalError::ExecutionTimeLimitReached); } fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { diff --git a/tests/compile-fail/timeout.rs b/tests/compile-fail/timeout.rs new file mode 100644 index 0000000000000..bcb6c993089a7 --- /dev/null +++ b/tests/compile-fail/timeout.rs @@ -0,0 +1,9 @@ +//error-pattern: reached the configured maximum execution time +#![feature(custom_attribute)] +#![miri(step_limit="1000")] + +fn main() { + for i in 0..1000000 { + assert!(i < 1000); + } +} From 4781a6ba5431e00b3bb6a5c085e4811f3b4d988f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 13:23:58 +0200 Subject: [PATCH 0443/1096] add attribute to limit the stack size --- src/bin/miri.rs | 4 +++- src/error.rs | 3 +++ src/interpreter/mod.rs | 16 +++++++++++++--- tests/compile-fail/stack_limit.rs | 20 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/compile-fail/stack_limit.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6997bbd011df6..e11ab5a86099d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -40,6 +40,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let krate = state.hir_crate.as_ref().unwrap(); let mut memory_size = 100*1024*1024; // 100MB let mut step_limit = 1000_000; + let mut stack_limit = 100; fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { match lit.node { syntax::ast::LitKind::Str(ref s, _) => s.clone(), @@ -55,6 +56,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { match &**name { "memory_size" => memory_size = extract_str(value).parse::().expect("not a number"), "step_limit" => step_limit = extract_str(value).parse::().expect("not a number"), + "stack_limit" => stack_limit = extract_str(value).parse::().expect("not a number"), _ => state.session.span_err(item.span, "unknown miri attribute"), } } @@ -67,7 +69,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - eval_main(tcx, &mir_map, node_id, memory_size, step_limit); + eval_main(tcx, &mir_map, node_id, memory_size, step_limit, stack_limit); state.session.abort_if_errors(); }); diff --git a/src/error.rs b/src/error.rs index 5c2410ed840a6..9952cfd9a7e83 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,6 +35,7 @@ pub enum EvalError<'tcx> { memory_usage: u64, }, ExecutionTimeLimitReached, + StackFrameLimitReached, } pub type EvalResult<'tcx, T> = Result>; @@ -79,6 +80,8 @@ impl<'tcx> Error for EvalError<'tcx> { "could not allocate more memory", EvalError::ExecutionTimeLimitReached => "reached the configured maximum execution time", + EvalError::StackFrameLimitReached => + "reached the configured maximum number of stack frames", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5b8947ec7353e..34fa9d8de6fd5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -42,6 +42,9 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The virtual call stack. stack: Vec>, + + /// The maximum number of stack frames allowed + stack_limit: usize, } /// A stack frame. @@ -133,7 +136,8 @@ enum ConstantKind { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64, stack_limit: u64) -> Self { + assert_eq!(stack_limit as usize as u64, stack_limit); EvalContext { tcx: tcx, mir_map: mir_map, @@ -141,6 +145,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { memory: Memory::new(&tcx.data_layout, memory_size), statics: HashMap::new(), stack: Vec::new(), + stack_limit: stack_limit as usize, } } @@ -316,7 +321,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, stmt: 0, }); - Ok(()) + if self.stack.len() > self.stack_limit { + Err(EvalError::StackFrameLimitReached) + } else { + Ok(()) + } } fn pop_stack_frame(&mut self) { @@ -930,10 +939,11 @@ pub fn eval_main<'a, 'tcx: 'a>( node_id: ast::NodeId, memory_size: u64, step_limit: u64, + stack_limit: u64, ) { let mir = mir_map.map.get(&node_id).expect("no mir for main function"); let def_id = tcx.map.local_def_id(node_id); - let mut ecx = EvalContext::new(tcx, mir_map, memory_size); + let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); let substs = tcx.mk_substs(subst::Substs::empty()); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value") diff --git a/tests/compile-fail/stack_limit.rs b/tests/compile-fail/stack_limit.rs new file mode 100644 index 0000000000000..3b6d4186dc905 --- /dev/null +++ b/tests/compile-fail/stack_limit.rs @@ -0,0 +1,20 @@ +#![feature(custom_attribute)] +#![miri(stack_limit="2")] + +fn bar() { + foo(); +} + +fn foo() { + cake(); //~ ERROR reached the configured maximum number of stack frames +} + +fn cake() { + flubber(); +} + +fn flubber() {} + +fn main() { + bar(); +} From 082effb3ee6b36bfac00ae8ff8cf2f4e1b253b5c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 5 Jul 2016 14:27:27 +0200 Subject: [PATCH 0444/1096] align allocations in the worst possible way --- src/error.rs | 9 +++++ src/interpreter/mod.rs | 37 +++++++++++------ src/interpreter/terminator.rs | 7 ++-- src/memory.rs | 49 ++++++++++++++--------- tests/compile-fail/oom.rs | 2 +- tests/compile-fail/oom2.rs | 2 +- tests/compile-fail/out_of_bounds_read.rs | 2 +- tests/compile-fail/out_of_bounds_read2.rs | 2 +- 8 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9952cfd9a7e83..6f033557b9658 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,6 +36,10 @@ pub enum EvalError<'tcx> { }, ExecutionTimeLimitReached, StackFrameLimitReached, + AlignmentCheckFailed { + required: usize, + has: usize, + }, } pub type EvalResult<'tcx, T> = Result>; @@ -82,6 +86,8 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the configured maximum execution time", EvalError::StackFrameLimitReached => "reached the configured maximum number of stack frames", + EvalError::AlignmentCheckFailed{..} => + "tried to execute a misaligned read or write", } } @@ -106,6 +112,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", allocation_size, memory_size - memory_usage, memory_size), + EvalError::AlignmentCheckFailed { required, has } => + write!(f, "tried to access memory with alignment {}, but alignment {} is required", + has, required), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 34fa9d8de6fd5..8d6c526c0c20c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -153,7 +153,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match output_ty { ty::FnConverging(ty) => { let size = self.type_size_with_substs(ty, substs); - self.memory.allocate(size).map(Some) + let align = self.type_align_with_substs(ty, substs); + self.memory.allocate(size, align).map(Some) } ty::FnDiverging => Ok(None), } @@ -177,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; macro_rules! i2p { ($i:ident, $n:expr) => {{ - let ptr = self.memory.allocate($n)?; + let ptr = self.memory.allocate($n, $n)?; self.memory.write_int(ptr, $i as i64, $n)?; Ok(ptr) }} @@ -202,8 +203,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8), Str(ref s) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(s.len())?; - let ptr = self.memory.allocate(psize * 2)?; + let static_ptr = self.memory.allocate(s.len(), 1)?; + let ptr = self.memory.allocate(psize * 2, psize)?; self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; @@ -211,19 +212,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ByteStr(ref bs) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(bs.len())?; - let ptr = self.memory.allocate(psize)?; + let static_ptr = self.memory.allocate(bs.len(), 1)?; + let ptr = self.memory.allocate(psize, psize)?; self.memory.write_bytes(static_ptr, bs)?; self.memory.write_ptr(ptr, static_ptr)?; Ok(ptr) } Bool(b) => { - let ptr = self.memory.allocate(1)?; + let ptr = self.memory.allocate(1, 1)?; self.memory.write_bool(ptr, b)?; Ok(ptr) } Char(c) => { - let ptr = self.memory.allocate(4)?; + let ptr = self.memory.allocate(4, 4)?; self.memory.write_uint(ptr, c as u64, 4)?; Ok(ptr) }, @@ -269,10 +270,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.type_size_with_substs(ty, self.substs()) } + fn type_align(&self, ty: Ty<'tcx>) -> usize { + self.type_align_with_substs(ty, self.substs()) + } + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize } + fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).pref() as usize + } + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { self.type_layout_with_substs(ty, self.substs()) } @@ -306,7 +315,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let locals: EvalResult<'tcx, Vec> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { let size = self.type_size_with_substs(ty, substs); - self.memory.allocate(size) + let align = self.type_align_with_substs(ty, substs); + self.memory.allocate(size, align) }).collect(); self.stack.push(Frame { @@ -553,7 +563,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let size = self.type_size(ty); - let ptr = self.memory.allocate(size)?; + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; self.memory.write_ptr(dest, ptr)?; } @@ -701,7 +712,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Ok(self.memory.allocate(0)?) + Ok(self.memory.allocate(0, 0)?) } else { let cid = ConstantId { def_id: def_id, @@ -955,9 +966,9 @@ pub fn eval_main<'a, 'tcx: 'a>( if mir.arg_decls.len() == 2 { // start function let ptr_size = ecx.memory().pointer_size(); - let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs"); + let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs"); ecx.memory_mut().write_usize(nargs, 0).unwrap(); - let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer"); + let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer"); ecx.memory_mut().write_usize(args, 0).unwrap(); ecx.frame_mut().locals[0] = nargs; ecx.frame_mut().locals[1] = args; diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 2fbc9a63ef68d..28b130e3d5728 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -88,7 +88,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let ptr = self.eval_operand(func)?; - assert_eq!(ptr.offset, 0); let fn_ptr = self.memory.read_ptr(ptr)?; let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { @@ -416,14 +415,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { let size = self.memory.read_usize(args[0])?; - let ptr = self.memory.allocate(size as usize)?; + let align = self.memory.read_usize(args[1])?; + let ptr = self.memory.allocate(size as usize, align as usize)?; self.memory.write_ptr(dest, ptr)?; } "__rust_reallocate" => { let ptr = self.memory.read_ptr(args[0])?; let size = self.memory.read_usize(args[2])?; - let new_ptr = self.memory.reallocate(ptr, size as usize)?; + let align = self.memory.read_usize(args[3])?; + let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; self.memory.write_ptr(dest, new_ptr)?; } diff --git a/src/memory.rs b/src/memory.rs index 2453b1f6e67a8..ca041dc1d9539 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -29,6 +29,7 @@ pub struct Allocation { pub bytes: Vec, pub relocations: BTreeMap, pub undef_mask: UndefMask, + pub align: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -98,10 +99,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), + align: 0, }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work - debug_assert!(mem.allocate(0).unwrap().points_to_zst()); + debug_assert!(mem.allocate(0, 0).unwrap().points_to_zst()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } @@ -133,62 +135,71 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> { + pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { if size == 0 { return Ok(Pointer::zst_ptr()); } + // make sure we can offset the result pointer by the worst possible alignment + // this allows cheaply checking for alignment directly in the pointer + let least_aligned_size = size + align; if self.memory_size - self.memory_usage < size as u64 { return Err(EvalError::OutOfMemory { - allocation_size: size as u64, + allocation_size: least_aligned_size as u64, memory_size: self.memory_size, memory_usage: self.memory_usage, }); } self.memory_usage += size as u64; let alloc = Allocation { - bytes: vec![0; size], + bytes: vec![0; least_aligned_size], relocations: BTreeMap::new(), - undef_mask: UndefMask::new(size), + undef_mask: UndefMask::new(least_aligned_size), + align: align, }; let id = self.next_id; self.next_id.0 += 1; self.alloc_map.insert(id, alloc); Ok(Pointer { alloc_id: id, - offset: 0, + // offset by the alignment, so larger accesses will fail + offset: align, }) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> { - if ptr.offset != 0 { + pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + if ptr.offset != self.get(ptr.alloc_id)?.align { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } if ptr.points_to_zst() { - return self.allocate(new_size); + return self.allocate(new_size, align); } - let size = self.get_mut(ptr.alloc_id)?.bytes.len(); + let size = self.get(ptr.alloc_id)?.bytes.len(); + let least_aligned_size = new_size + align; - if new_size > size { - let amount = new_size - size; + if least_aligned_size > size { + let amount = least_aligned_size - size; self.memory_usage += amount as u64; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); - } else if size > new_size { + } else if size > least_aligned_size { // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable // through the memory_usage value, by allocating a lot and reallocating to zero - self.memory_usage -= (size - new_size) as u64; - self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; + self.memory_usage -= (size - least_aligned_size) as u64; + self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?; let alloc = self.get_mut(ptr.alloc_id)?; - alloc.bytes.truncate(new_size); - alloc.undef_mask.truncate(new_size); + alloc.bytes.truncate(least_aligned_size); + alloc.undef_mask.truncate(least_aligned_size); } - Ok(ptr) + Ok(Pointer { + alloc_id: ptr.alloc_id, + offset: align, + }) } // TODO(solson): See comment on `reallocate`. @@ -196,7 +207,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if ptr.points_to_zst() { return Ok(()); } - if ptr.offset != 0 { + if ptr.offset != self.get(ptr.alloc_id)?.align { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index 83109a77e6201..d5f021bda704f 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -6,6 +6,6 @@ fn bar() { assert_eq!(x, 6); } -fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory +fn main() { //~ ERROR tried to allocate 8 more bytes, but only 0 bytes are free of the 0 byte memory bar(); } diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index 63c51dbaa7d26..65ef0dd0b7daa 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -3,7 +3,7 @@ fn bar(i: i32) { if i < 1000 { - bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory + bar(i + 1) //~ ERROR tried to allocate 8 more bytes, but only 1 bytes are free of the 1000 byte memory //~^NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index f6a305840c241..29355992e44c0 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2 + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 3 panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/out_of_bounds_read2.rs b/tests/compile-fail/out_of_bounds_read2.rs index 5509a8346e552..388ed8b130639 100644 --- a/tests/compile-fail/out_of_bounds_read2.rs +++ b/tests/compile-fail/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 6..7 outside bounds of allocation panic!("this should never print: {}", x); } From 7161c72320816948733a85b1759b46a1cf2d9d92 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 10:58:26 +0200 Subject: [PATCH 0445/1096] abi alignment is the correct one --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8d6c526c0c20c..3454fdc7dbc4c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -279,7 +279,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).pref() as usize + self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).abi() as usize } fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { From 50987e3697d58a1d1e48a778d1e5875d6adea657 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 10:58:51 +0200 Subject: [PATCH 0446/1096] some methods to check pointers for correct alignment --- src/memory.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index ca041dc1d9539..4afcf05d67ad7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -51,6 +51,25 @@ impl Pointer { offset: 0, } } + pub fn is_aligned_to(&self, align: usize) -> bool { + self.offset % align == 0 + } + pub fn check_align(&self, align: usize) -> EvalResult<'static, ()> { + if self.is_aligned_to(align) { + Ok(()) + } else { + let mut best = self.offset; + let mut i = 1; + while best > 0 && (best & 1 == 0) { + best >>= 1; + i <<= 1; + } + Err(EvalError::AlignmentCheckFailed { + required: align, + has: i, + }) + } + } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] From 1bd8e04228d807c6ea0b7813c6c8689527f65252 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 11:12:44 +0200 Subject: [PATCH 0447/1096] check alignment in various places --- src/interpreter/mod.rs | 10 ++++++++-- src/interpreter/terminator.rs | 8 ++++++-- src/memory.rs | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3454fdc7dbc4c..c5ebd96cda6bf 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -520,12 +520,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Repeat(ref operand, _) => { - let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + let (elem_size, elem_align, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; let src = self.eval_operand(operand)?; + src.check_align(elem_align)?; + dest.check_align(elem_align)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); self.memory.copy(src, elem_dest, elem_size)?; @@ -592,6 +594,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); // FIXME(solson): Wrong for almost everything. + // FIXME: check alignment warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); let dest_size = self.type_size(dest_ty); let src_size = self.type_size(src_ty); @@ -845,6 +848,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); + let align = self.type_align(ty); + src.check_align(align)?; + dest.check_align(align)?; self.memory.copy(src, dest, size)?; Ok(()) } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 28b130e3d5728..d5b7dbaf62386 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -289,8 +289,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.type_size(elem_ty); + let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; + src.check_align(elem_align)?; let dest = self.memory.read_ptr(args_ptrs[1])?; + dest.check_align(elem_align)?; let count = self.memory.read_isize(args_ptrs[2])?; self.memory.copy(src, dest, count as usize * elem_size)?; } @@ -307,8 +310,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - // FIXME: use correct value - self.memory.write_int(dest, 1, pointer_size)?; + let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_align = self.type_align(elem_ty); + self.memory.write_uint(dest, elem_align as u64, pointer_size)?; } "move_val_init" => { diff --git a/src/memory.rs b/src/memory.rs index 4afcf05d67ad7..cac4a8bb7c194 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -463,6 +463,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { + ptr.check_align(self.layout.i1_align.abi() as usize)?; let bytes = self.get_bytes(ptr, 1)?; match bytes[0] { 0 => Ok(false), @@ -472,14 +473,27 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { + ptr.check_align(self.layout.i1_align.abi() as usize)?; self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) } + fn check_int_align(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + match size { + 1 => ptr.check_align(self.layout.i8_align.abi() as usize), + 2 => ptr.check_align(self.layout.i16_align.abi() as usize), + 4 => ptr.check_align(self.layout.i32_align.abi() as usize), + 8 => ptr.check_align(self.layout.i64_align.abi() as usize), + _ => panic!("bad integer size"), + } + } + pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { + self.check_int_align(ptr, size)?; self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { + self.check_int_align(ptr, size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size)?; write_target_int(endianess, b, n).unwrap(); @@ -487,10 +501,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { + self.check_int_align(ptr, size)?; self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { + self.check_int_align(ptr, size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size)?; write_target_uint(endianess, b, n).unwrap(); From aca691160dbeae0c18049d1c23091e644261b528 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 11:19:24 +0200 Subject: [PATCH 0448/1096] add a test --- tests/compile-fail/alignment.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/compile-fail/alignment.rs diff --git a/tests/compile-fail/alignment.rs b/tests/compile-fail/alignment.rs new file mode 100644 index 0000000000000..4faaa359df624 --- /dev/null +++ b/tests/compile-fail/alignment.rs @@ -0,0 +1,11 @@ +fn main() { + // miri always gives allocations the worst possible alignment, so a `u8` array is guaranteed + // to be at the virtual location 1 (so one byte offset from the ultimate alignemnt location 0) + let mut x = [0u8; 20]; + let x_ptr: *mut u8 = &mut x[0]; + let y_ptr = x_ptr as *mut u64; + unsafe { + *y_ptr = 42; //~ ERROR tried to access memory with alignment 1, but alignment + } + panic!("unreachable in miri"); +} From 51ce4a2584f7356702f8e4faa1343e2ce47db5f8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 11:51:32 +0200 Subject: [PATCH 0449/1096] use byteorder's write_f{32,64} instead of transmuting --- src/interpreter/mod.rs | 23 +++++++--------- src/memory.rs | 57 +++++++++++++++++++++++++++++++++++++--- tests/run-pass/floats.rs | 3 +++ 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8ca37a6517715..9ef51a0414526 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -170,7 +170,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; - use std::mem::transmute; macro_rules! i2p { ($i:ident, $n:expr) => {{ let ptr = self.memory.allocate($n); @@ -180,12 +179,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } match *const_val { Float(ConstFloat::F32(f)) => { - let i = unsafe { transmute::<_, u32>(f) }; - i2p!(i, 4) + let ptr = self.memory.allocate(4); + self.memory.write_f32(ptr, f)?; + Ok(ptr) }, Float(ConstFloat::F64(f)) => { - let i = unsafe { transmute::<_, u64>(f) }; - i2p!(i, 8) + let ptr = self.memory.allocate(8); + self.memory.write_f64(ptr, f)?; + Ok(ptr) }, Float(ConstFloat::FInfer{..}) => unreachable!(), Integral(ConstInt::Infer(_)) => unreachable!(), @@ -834,7 +835,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy, FloatTy}; - use std::mem::transmute; let val = match (self.memory.pointer_size(), &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), (_, &ty::TyChar) => { @@ -858,14 +858,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), (8, &ty::TyUint(UintTy::Us)) | (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - (_, &ty::TyFloat(FloatTy::F32)) => { - let i = self.memory.read_uint(ptr, 4)? as u32; - PrimVal::F32(unsafe { transmute(i) }) - }, - (_, &ty::TyFloat(FloatTy::F64)) => { - let i = self.memory.read_uint(ptr, 8)?; - PrimVal::F64(unsafe { transmute(i) }) - }, + + (_, &ty::TyFloat(FloatTy::F32)) => PrimVal::F32(self.memory.read_f32(ptr)?), + (_, &ty::TyFloat(FloatTy::F64)) => PrimVal::F64(self.memory.read_f64(ptr)?), (_, &ty::TyFnDef(def_id, substs, fn_ty)) => { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) diff --git a/src/memory.rs b/src/memory.rs index 92f69d575f18c..293887ba86255 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -394,7 +394,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - use std::mem::transmute; let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), @@ -408,8 +407,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), - PrimVal::F32(f) => self.write_uint(ptr, unsafe { transmute::<_, u32>(f) } as u64, 4), - PrimVal::F64(f) => self.write_uint(ptr, unsafe { transmute::<_, u64>(f) }, 8), + PrimVal::F32(f) => self.write_f32(ptr, f), + PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(_p) | PrimVal::AbstractPtr(_p) => unimplemented!(), } @@ -467,6 +466,28 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let size = self.pointer_size(); self.write_uint(ptr, n, size) } + + pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { + let endianess = self.endianess(); + let b = self.get_bytes_mut(ptr, 4)?; + write_target_f32(endianess, b, f).unwrap(); + Ok(()) + } + + pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { + let endianess = self.endianess(); + let b = self.get_bytes_mut(ptr, 8)?; + write_target_f64(endianess, b, f).unwrap(); + Ok(()) + } + + pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { + self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap()) + } + + pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { + self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap()) + } } /// Relocations @@ -589,6 +610,36 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result Result<(), byteorder::Error> { + match endianess { + layout::Endian::Little => target.write_f32::(data), + layout::Endian::Big => target.write_f32::(data), + } +} +fn write_target_f64(endianess: layout::Endian, mut target: &mut [u8], data: f64) -> Result<(), byteorder::Error> { + match endianess { + layout::Endian::Little => target.write_f64::(data), + layout::Endian::Big => target.write_f64::(data), + } +} + +fn read_target_f32(endianess: layout::Endian, mut source: &[u8]) -> Result { + match endianess { + layout::Endian::Little => source.read_f32::(), + layout::Endian::Big => source.read_f32::(), + } +} +fn read_target_f64(endianess: layout::Endian, mut source: &[u8]) -> Result { + match endianess { + layout::Endian::Little => source.read_f64::(), + layout::Endian::Big => source.read_f64::(), + } +} + //////////////////////////////////////////////////////////////////////////////// // Undefined byte tracking //////////////////////////////////////////////////////////////////////////////// diff --git a/tests/run-pass/floats.rs b/tests/run-pass/floats.rs index 504fffca75608..9c4d0594d1c99 100644 --- a/tests/run-pass/floats.rs +++ b/tests/run-pass/floats.rs @@ -5,4 +5,7 @@ fn main() { assert_eq!(-{5.0_f32}, -5.0_f32); assert!((5.0_f32/0.0).is_infinite()); assert!((-5.0_f32).sqrt().is_nan()); + let x: u64 = unsafe { std::mem::transmute(42.0_f64) }; + let y: f64 = unsafe { std::mem::transmute(x) }; + assert_eq!(y, 42.0_f64); } From 7613ef0563843f26fb8abdc47dbd2f9e38eb4895 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 11:53:03 +0200 Subject: [PATCH 0450/1096] comparing floats is necessary in rare cases --- src/primval.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/primval.rs b/src/primval.rs index f6f9a025770a2..966196d8d1a5f 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -75,8 +75,6 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva Shl => unreachable!(), Shr => unreachable!(), - // directly comparing floats is questionable - // miri could forbid it, or at least miri as rust const eval should forbid it Eq => Bool($l == $r), Ne => Bool($l != $r), Lt => Bool($l < $r), From 7d2803ae3fa1d5aed6efa56d9d60776fd62d644c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 11:19:55 +0200 Subject: [PATCH 0451/1096] remove unused extern crate --- src/bin/miri.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index e11ab5a86099d..f5b0c6fbf6792 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -4,7 +4,6 @@ extern crate getopts; extern crate miri; extern crate rustc; extern crate rustc_driver; -extern crate rustc_plugin; extern crate env_logger; extern crate log_settings; extern crate syntax; From a7d3a85d9e22e978e8156c2ad57b9d3c4a67dd13 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 11:20:09 +0200 Subject: [PATCH 0452/1096] infer type of the various limits --- src/bin/miri.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f5b0c6fbf6792..14a947302c65e 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -53,9 +53,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { match item.node { MetaItemKind::NameValue(ref name, ref value) => { match &**name { - "memory_size" => memory_size = extract_str(value).parse::().expect("not a number"), - "step_limit" => step_limit = extract_str(value).parse::().expect("not a number"), - "stack_limit" => stack_limit = extract_str(value).parse::().expect("not a number"), + "memory_size" => memory_size = extract_str(value).parse().expect("not a number"), + "step_limit" => step_limit = extract_str(value).parse().expect("not a number"), + "stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"), _ => state.session.span_err(item.span, "unknown miri attribute"), } } From 8d3817cfc63674db308e56371ed8f1cc32af4d92 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 11:20:46 +0200 Subject: [PATCH 0453/1096] use usize instead of u64 for memory limits --- src/error.rs | 6 +++--- src/interpreter/mod.rs | 9 ++++----- src/memory.rs | 18 +++++++++--------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9952cfd9a7e83..2d723e6923525 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,9 +30,9 @@ pub enum EvalError<'tcx> { Math(Span, ConstMathErr), InvalidChar(u32), OutOfMemory { - allocation_size: u64, - memory_size: u64, - memory_usage: u64, + allocation_size: usize, + memory_size: usize, + memory_usage: usize, }, ExecutionTimeLimitReached, StackFrameLimitReached, diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 34fa9d8de6fd5..04d7d9f9d0509 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -136,8 +136,7 @@ enum ConstantKind { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64, stack_limit: u64) -> Self { - assert_eq!(stack_limit as usize as u64, stack_limit); + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self { EvalContext { tcx: tcx, mir_map: mir_map, @@ -145,7 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { memory: Memory::new(&tcx.data_layout, memory_size), statics: HashMap::new(), stack: Vec::new(), - stack_limit: stack_limit as usize, + stack_limit: stack_limit, } } @@ -937,9 +936,9 @@ pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, node_id: ast::NodeId, - memory_size: u64, + memory_size: usize, step_limit: u64, - stack_limit: u64, + stack_limit: usize, ) { let mir = mir_map.map.get(&node_id).expect("no mir for main function"); let def_id = tcx.map.local_def_id(node_id); diff --git a/src/memory.rs b/src/memory.rs index 2453b1f6e67a8..79130b4e24b1d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -67,9 +67,9 @@ pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, /// Number of virtual bytes allocated - memory_usage: u64, + memory_usage: usize, /// Maximum number of virtual bytes that may be allocated - memory_size: u64, + memory_size: usize, /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. functions: HashMap>, @@ -82,7 +82,7 @@ pub struct Memory<'a, 'tcx> { const ZST_ALLOC_ID: AllocId = AllocId(0); impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self { + pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { let mut mem = Memory { alloc_map: HashMap::new(), functions: HashMap::new(), @@ -137,14 +137,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(Pointer::zst_ptr()); } - if self.memory_size - self.memory_usage < size as u64 { + if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { - allocation_size: size as u64, + allocation_size: size, memory_size: self.memory_size, memory_usage: self.memory_usage, }); } - self.memory_usage += size as u64; + self.memory_usage += size; let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new(), @@ -174,14 +174,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if new_size > size { let amount = new_size - size; - self.memory_usage += amount as u64; + self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable // through the memory_usage value, by allocating a lot and reallocating to zero - self.memory_usage -= (size - new_size) as u64; + self.memory_usage -= size - new_size; self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.truncate(new_size); @@ -202,7 +202,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { - self.memory_usage -= alloc.bytes.len() as u64; + self.memory_usage -= alloc.bytes.len(); } else { debug!("deallocated a pointer twice: {}", ptr.alloc_id); // TODO(solson): Report error about erroneous free. This is blocked on properly tracking From 5381981446320107536a6058e20f2e52283dd8c5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 11:21:18 +0200 Subject: [PATCH 0454/1096] shrink_to_fit some vectors to prevent interpreted code from passing the memory limits --- src/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 79130b4e24b1d..f7ee6b61e5acd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -179,12 +179,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable - // through the memory_usage value, by allocating a lot and reallocating to zero self.memory_usage -= size - new_size; self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.truncate(new_size); + alloc.bytes.shrink_to_fit(); alloc.undef_mask.truncate(new_size); } @@ -676,6 +675,7 @@ impl UndefMask { fn truncate(&mut self, length: usize) { self.len = length; self.blocks.truncate(self.len / BLOCK_SIZE + 1); + self.blocks.shrink_to_fit(); } } From 44bef2523552b3764506d027f4afc0d7996e376c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 11:30:00 +0200 Subject: [PATCH 0455/1096] allocating memory for floats can fail, too --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7bef32b1d5618..4200340e6a9c6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -183,12 +183,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } match *const_val { Float(ConstFloat::F32(f)) => { - let ptr = self.memory.allocate(4); + let ptr = self.memory.allocate(4)?; self.memory.write_f32(ptr, f)?; Ok(ptr) }, Float(ConstFloat::F64(f)) => { - let ptr = self.memory.allocate(8); + let ptr = self.memory.allocate(8)?; self.memory.write_f64(ptr, f)?; Ok(ptr) }, From 613d15c672b3a857521ebd15daef384e2ae997f3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 7 Jul 2016 13:19:17 +0200 Subject: [PATCH 0456/1096] clippy --- src/interpreter/mod.rs | 12 ++++++------ src/memory.rs | 2 +- src/primval.rs | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 16f2608528083..0506335dc221f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -198,18 +198,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Integral(ConstInt::InferSigned(_)) => unreachable!(), Integral(ConstInt::I8(i)) => i2p!(i, 1), Integral(ConstInt::U8(i)) => i2p!(i, 1), + Integral(ConstInt::Isize(ConstIsize::Is16(i))) | Integral(ConstInt::I16(i)) => i2p!(i, 2), + Integral(ConstInt::Usize(ConstUsize::Us16(i))) | Integral(ConstInt::U16(i)) => i2p!(i, 2), + Integral(ConstInt::Isize(ConstIsize::Is32(i))) | Integral(ConstInt::I32(i)) => i2p!(i, 4), + Integral(ConstInt::Usize(ConstUsize::Us32(i))) | Integral(ConstInt::U32(i)) => i2p!(i, 4), + Integral(ConstInt::Isize(ConstIsize::Is64(i))) | Integral(ConstInt::I64(i)) => i2p!(i, 8), + Integral(ConstInt::Usize(ConstUsize::Us64(i))) | Integral(ConstInt::U64(i)) => i2p!(i, 8), - Integral(ConstInt::Isize(ConstIsize::Is16(i))) => i2p!(i, 2), - Integral(ConstInt::Isize(ConstIsize::Is32(i))) => i2p!(i, 4), - Integral(ConstInt::Isize(ConstIsize::Is64(i))) => i2p!(i, 8), - Integral(ConstInt::Usize(ConstUsize::Us16(i))) => i2p!(i, 2), - Integral(ConstInt::Usize(ConstUsize::Us32(i))) => i2p!(i, 4), - Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8), Str(ref s) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len(), 1)?; diff --git a/src/memory.rs b/src/memory.rs index 534276b7378c3..d4f8eb58f2f72 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -127,7 +127,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { mem } - pub fn allocations<'b>(&'b self) -> ::std::collections::hash_map::Iter<'b, AllocId, Allocation> { + pub fn allocations(&self) -> ::std::collections::hash_map::Iter { self.alloc_map.iter() } diff --git a/src/primval.rs b/src/primval.rs index 966196d8d1a5f..1ec7516817034 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,3 +1,6 @@ +#![allow(unknown_lints)] +#![allow(float_cmp)] + use rustc::mir::repr as mir; use error::{EvalError, EvalResult}; From ec897f9156332a1d1bb39975bfcfc32a3ea8e473 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 22 Jul 2016 16:35:39 +0200 Subject: [PATCH 0457/1096] don't allow runtime-aligning of memory --- src/interpreter/mod.rs | 12 +- src/interpreter/terminator.rs | 4 +- src/memory.rs | 142 +++++++++++----------- tests/compile-fail/oom.rs | 2 +- tests/compile-fail/oom2.rs | 2 +- tests/compile-fail/out_of_bounds_read.rs | 2 +- tests/compile-fail/out_of_bounds_read2.rs | 2 +- 7 files changed, 79 insertions(+), 87 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0506335dc221f..c1e1fff999fd3 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -535,11 +535,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let src = self.eval_operand(operand)?; - src.check_align(elem_align)?; - dest.check_align(elem_align)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.memory.copy(src, elem_dest, elem_size)?; + self.memory.copy(src, elem_dest, elem_size, elem_align)?; } } @@ -603,17 +601,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); // FIXME(solson): Wrong for almost everything. - // FIXME: check alignment warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); let dest_size = self.type_size(dest_ty); let src_size = self.type_size(src_ty); + let dest_align = self.type_align(dest_ty); // Hack to support fat pointer -> thin pointer casts to keep tests for // other things passing for now. let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); if dest_size == src_size || is_fat_ptr_cast { - self.memory.copy(src, dest, dest_size)?; + self.memory.copy(src, dest, dest_size, dest_align)?; } else { return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); } @@ -858,9 +856,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); - src.check_align(align)?; - dest.check_align(align)?; - self.memory.copy(src, dest, size)?; + self.memory.copy(src, dest, size, align)?; Ok(()) } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index d5b7dbaf62386..568d8358f0874 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -291,11 +291,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; - src.check_align(elem_align)?; let dest = self.memory.read_ptr(args_ptrs[1])?; - dest.check_align(elem_align)?; let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size)?; + self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } "discriminant_value" => { diff --git a/src/memory.rs b/src/memory.rs index d4f8eb58f2f72..91ae90da8b6f6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -51,25 +51,6 @@ impl Pointer { offset: 0, } } - pub fn is_aligned_to(&self, align: usize) -> bool { - self.offset % align == 0 - } - pub fn check_align(&self, align: usize) -> EvalResult<'static, ()> { - if self.is_aligned_to(align) { - Ok(()) - } else { - let mut best = self.offset; - let mut i = 1; - while best > 0 && (best & 1 == 0) { - best >>= 1; - i <<= 1; - } - Err(EvalError::AlignmentCheckFailed { - required: align, - has: i, - }) - } - } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] @@ -118,11 +99,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), - align: 0, + align: 1, }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work - debug_assert!(mem.allocate(0, 0).unwrap().points_to_zst()); + debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } @@ -155,24 +136,22 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + assert!(align != 0); if size == 0 { return Ok(Pointer::zst_ptr()); } - // make sure we can offset the result pointer by the worst possible alignment - // this allows cheaply checking for alignment directly in the pointer - let least_aligned_size = size + align; if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { - allocation_size: least_aligned_size, + allocation_size: size, memory_size: self.memory_size, memory_usage: self.memory_usage, }); } self.memory_usage += size; let alloc = Allocation { - bytes: vec![0; least_aligned_size], + bytes: vec![0; size], relocations: BTreeMap::new(), - undef_mask: UndefMask::new(least_aligned_size), + undef_mask: UndefMask::new(size), align: align, }; let id = self.next_id; @@ -180,15 +159,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.insert(id, alloc); Ok(Pointer { alloc_id: id, - // offset by the alignment, so larger accesses will fail - offset: align, + offset: 0, }) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { - if ptr.offset != self.get(ptr.alloc_id)?.align { + // TODO(solson): Report error about non-__rust_allocate'd pointer. + if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } @@ -197,27 +176,26 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } let size = self.get(ptr.alloc_id)?.bytes.len(); - let least_aligned_size = new_size + align; - if least_aligned_size > size { - let amount = least_aligned_size - size; + if new_size > size { + let amount = new_size - size; self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); - } else if size > least_aligned_size { + } else if size > new_size { // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable // through the memory_usage value, by allocating a lot and reallocating to zero - self.memory_usage -= size - least_aligned_size; - self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?; + self.memory_usage -= size - new_size; + self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; - alloc.bytes.truncate(least_aligned_size); - alloc.undef_mask.truncate(least_aligned_size); + alloc.bytes.truncate(new_size); + alloc.undef_mask.truncate(new_size); } Ok(Pointer { alloc_id: ptr.alloc_id, - offset: align, + offset: 0, }) } @@ -226,7 +204,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if ptr.points_to_zst() { return Ok(()); } - if ptr.offset != self.get(ptr.alloc_id)?.align { + if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } @@ -251,6 +229,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn endianess(&self) -> layout::Endian { self.layout.endian } + + pub fn check_align(&self, ptr: Pointer, align: usize) -> EvalResult<'tcx, ()> { + let alloc = self.get(ptr.alloc_id)?; + if alloc.align < align { + return Err(EvalError::AlignmentCheckFailed { + has: alloc.align, + required: align, + }); + } + if ptr.offset % align == 0 { + Ok(()) + } else { + Err(EvalError::AlignmentCheckFailed { + has: ptr.offset % align, + required: align, + }) + } + } } /// Allocation accessors @@ -368,7 +364,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + self.check_align(ptr, align)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -376,7 +373,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes_unchecked(ptr, size) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + self.check_align(ptr, align)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) @@ -385,11 +383,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); - let dest_bytes = self.get_bytes_mut(dest, size)?.as_mut_ptr(); + let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and @@ -409,17 +407,17 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { - self.get_bytes(ptr, size) + self.get_bytes(ptr, size, 1) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, src.len())?; + let bytes = self.get_bytes_mut(ptr, src.len(), 1)?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, count)?; + let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) } @@ -465,8 +463,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { - ptr.check_align(self.layout.i1_align.abi() as usize)?; - let bytes = self.get_bytes(ptr, 1)?; + let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi() as usize)?; match bytes[0] { 0 => Ok(false), 1 => Ok(true), @@ -475,42 +472,43 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.i1_align.abi() as usize)?; - self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) + let align = self.layout.i1_align.abi() as usize; + self.get_bytes_mut(ptr, 1, align) + .map(|bytes| bytes[0] = b as u8) } - fn check_int_align(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn int_align(&self, size: usize) -> EvalResult<'tcx, usize> { match size { - 1 => ptr.check_align(self.layout.i8_align.abi() as usize), - 2 => ptr.check_align(self.layout.i16_align.abi() as usize), - 4 => ptr.check_align(self.layout.i32_align.abi() as usize), - 8 => ptr.check_align(self.layout.i64_align.abi() as usize), + 1 => Ok(self.layout.i8_align.abi() as usize), + 2 => Ok(self.layout.i16_align.abi() as usize), + 4 => Ok(self.layout.i32_align.abi() as usize), + 8 => Ok(self.layout.i64_align.abi() as usize), _ => panic!("bad integer size"), } } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { - self.check_int_align(ptr, size)?; - self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { - self.check_int_align(ptr, size)?; + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_int(endianess, b, n).unwrap(); Ok(()) } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { - self.check_int_align(ptr, size)?; - self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { - self.check_int_align(ptr, size)?; + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_uint(endianess, b, n).unwrap(); Ok(()) } @@ -534,29 +532,29 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.f32_align.abi() as usize)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 4)?; + let align = self.layout.f32_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 4, align)?; write_target_f32(endianess, b, f).unwrap(); Ok(()) } pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { - ptr.check_align(self.layout.f64_align.abi() as usize)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 8)?; + let align = self.layout.f64_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 8, align)?; write_target_f64(endianess, b, f).unwrap(); Ok(()) } pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { - ptr.check_align(self.layout.f32_align.abi() as usize)?; - self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 4, self.layout.f32_align.abi() as usize) + .map(|b| read_target_f32(self.endianess(), b).unwrap()) } pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { - ptr.check_align(self.layout.f64_align.abi() as usize)?; - self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 8, self.layout.f64_align.abi() as usize) + .map(|b| read_target_f64(self.endianess(), b).unwrap()) } } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index d5f021bda704f..83109a77e6201 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -6,6 +6,6 @@ fn bar() { assert_eq!(x, 6); } -fn main() { //~ ERROR tried to allocate 8 more bytes, but only 0 bytes are free of the 0 byte memory +fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory bar(); } diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index 65ef0dd0b7daa..63c51dbaa7d26 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -3,7 +3,7 @@ fn bar(i: i32) { if i < 1000 { - bar(i + 1) //~ ERROR tried to allocate 8 more bytes, but only 1 bytes are free of the 1000 byte memory + bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory //~^NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index 29355992e44c0..f6a305840c241 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 3 + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2 panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/out_of_bounds_read2.rs b/tests/compile-fail/out_of_bounds_read2.rs index 388ed8b130639..5509a8346e552 100644 --- a/tests/compile-fail/out_of_bounds_read2.rs +++ b/tests/compile-fail/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 6..7 outside bounds of allocation + let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation panic!("this should never print: {}", x); } From f8cfc387fd3eef88033c93086ef4beb9d8e4f082 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 25 Jul 2016 12:30:35 +0200 Subject: [PATCH 0458/1096] address nits --- src/memory.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 91ae90da8b6f6..d99d2c8132e32 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -168,7 +168,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 { - // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } if ptr.points_to_zst() { @@ -184,12 +183,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.undef_mask.grow(amount, false); } else if size > new_size { - // it's possible to cause miri to use arbitrary amounts of memory that aren't detectable - // through the memory_usage value, by allocating a lot and reallocating to zero self.memory_usage -= size - new_size; self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; alloc.bytes.truncate(new_size); + alloc.bytes.shrink_to_fit(); alloc.undef_mask.truncate(new_size); } From 45cf3cfde2c32f5b27c469ef1b5f04dd195fb462 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 27 Aug 2016 01:44:46 -0600 Subject: [PATCH 0459/1096] Update for changes in rustc. --- Cargo.lock | 45 +++++++++++----- src/bin/miri.rs | 12 +++-- src/interpreter/mod.rs | 68 ++++++++++------------- src/interpreter/step.rs | 16 ++++-- src/interpreter/terminator.rs | 80 +++++++++++++--------------- tests/compile-fail/cast_fn_ptr.rs | 4 +- tests/compile-fail/execute_memory.rs | 4 +- 7 files changed, 121 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0b785a061fbb..0217a26fddd24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,8 +3,8 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -33,11 +33,11 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -45,7 +45,7 @@ name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -56,7 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -77,24 +77,24 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "0.1.71" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -108,7 +108,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -126,7 +126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -134,3 +134,22 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[metadata] +"checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9" +"checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" +"checksum compiletest_rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bcddebf582c5c035cce855a89596eb686cc40b9e77da1026fba735dcca2fbd3" +"checksum env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "82dcb9ceed3868a03b335657b85a159736c961900f7e7747d3b0b97b9ccb5ccb" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" +"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" +"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" +"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +"checksum regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)" = "56b7ee9f764ecf412c6e2fff779bca4b22980517ae335a21aeaf4e32625a5df2" +"checksum regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "31040aad7470ad9d8c46302dcffba337bb4289ca5da2e3cd6e37b64109a85199" +"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" +"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" +"checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d" +"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 14a947302c65e..728266a755d64 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -33,8 +33,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let tcx = state.tcx.unwrap(); let mir_map = state.mir_map.unwrap(); - let (node_id, _) = state.session.entry_fn.borrow() + let (entry_node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); + let entry_def_id = tcx.map.local_def_id(entry_node_id); let krate = state.hir_crate.as_ref().unwrap(); let mut memory_size = 100*1024*1024; // 100MB @@ -66,9 +67,12 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } - let mut mir_map = MirMap { map: mir_map.map.clone() }; - run_mir_passes(tcx, &mut mir_map); - eval_main(tcx, &mir_map, node_id, memory_size, step_limit, stack_limit); + let mut mir_map_copy = MirMap::new(tcx.dep_graph.clone()); + for def_id in mir_map.map.keys() { + mir_map_copy.map.insert(def_id, mir_map.map.get(&def_id).unwrap().clone()); + } + run_mir_passes(tcx, &mut mir_map_copy); + eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit); state.session.abort_if_errors(); }); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c1e1fff999fd3..5ef423f21fb41 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -2,7 +2,7 @@ use rustc::middle::const_val; use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; -use rustc::traits::ProjectionMode; +use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; @@ -12,7 +12,6 @@ use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; use std::iter; -use syntax::ast; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -148,15 +147,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { - match output_ty { - ty::FnConverging(ty) => { - let size = self.type_size_with_substs(ty, substs); - let align = self.type_align_with_substs(ty, substs); - self.memory.allocate(size, align).map(Some) - } - ty::FnDiverging => Ok(None), - } + pub fn alloc_ret_ptr(&mut self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Pointer> { + let size = self.type_size_with_substs(ty, substs); + let align = self.type_align_with_substs(ty, substs); + self.memory.allocate(size, align) } pub fn memory(&self) -> &Memory<'a, 'tcx> { @@ -251,22 +245,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { - match self.tcx.map.as_local_node_id(def_id) { - Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()), - None => { - let mut mir_cache = self.mir_cache.borrow_mut(); - if let Some(mir) = mir_cache.get(&def_id) { - return CachedMir::Owned(mir.clone()); - } - - let cs = &self.tcx.sess.cstore; - let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); - }); - let cached = Rc::new(mir); - mir_cache.insert(def_id, cached.clone()); - CachedMir::Owned(cached) + if def_id.is_local() { + CachedMir::Ref(self.mir_map.map.get(&def_id).unwrap()) + } else { + let mut mir_cache = self.mir_cache.borrow_mut(); + if let Some(mir) = mir_cache.get(&def_id) { + return CachedMir::Owned(mir.clone()); } + + let cs = &self.tcx.sess.cstore; + let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { + panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); + }); + let cached = Rc::new(mir); + mir_cache.insert(def_id, cached.clone()); + CachedMir::Owned(cached) } } @@ -299,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + self.tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { // TODO(solson): Report this error properly. ty.layout(&infcx).unwrap() }) @@ -755,7 +748,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], Static(def_id) => { - let substs = self.tcx.mk_substs(subst::Substs::empty()); + let substs = subst::Substs::empty(self.tcx); let cid = ConstantId { def_id: def_id, substs: substs, @@ -846,11 +839,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx), self.substs()) + self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().operand_ty(self.tcx, operand), self.substs()) + self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { @@ -961,21 +954,19 @@ impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, - node_id: ast::NodeId, + def_id: DefId, memory_size: usize, step_limit: u64, stack_limit: usize, ) { - let mir = mir_map.map.get(&node_id).expect("no mir for main function"); - let def_id = tcx.map.local_def_id(node_id); + let mir = mir_map.map.get(&def_id).expect("no mir for main function"); let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); - let substs = tcx.mk_substs(subst::Substs::empty()); + let substs = subst::Substs::empty(tcx); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) - .expect("should at least be able to allocate space for the main function's return value") - .expect("main function should not be diverging"); + .expect("should at least be able to allocate space for the main function's return value"); ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)) - .expect("could not allocate first stack frame"); + .expect("could not allocate first stack frame"); if mir.arg_decls.len() == 2 { // start function @@ -1018,8 +1009,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| Some(tcx.lookup_item_type(self.0).generics)) + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) } } err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 5bfe85fff92fc..75dc7ee0941cc 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -66,8 +66,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { trace!("{:?}", stmt); - let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - self.eval_assignment(lvalue, rvalue)?; + + use rustc::mir::repr::StatementKind::*; + match stmt.kind { + Assign(ref lvalue, ref rvalue) => self.eval_assignment(lvalue, rvalue)?, + SetDiscriminant { .. } => unimplemented!(), + + // Miri can safely ignore these. Only translation needs them. + StorageLive(_) | StorageDead(_) => {} + } + self.frame_mut().stmt += 1; Ok(()) } @@ -110,7 +118,6 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { let mir = self.ecx.load_mir(def_id); self.try(|this| { let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; - let ptr = ptr.expect("there's no such thing as an unreachable static"); this.ecx.statics.insert(cid.clone(), ptr); this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)) }); @@ -155,7 +162,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let return_ty = mir.return_ty; self.try(|this| { let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; - let return_ptr = return_ptr.expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr)) @@ -167,7 +173,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { self.super_lvalue(lvalue, context); if let mir::Lvalue::Static(def_id) = *lvalue { - let substs = self.ecx.tcx.mk_substs(subst::Substs::empty()); + let substs = subst::Substs::empty(self.ecx.tcx); let span = self.span; self.global_item(def_id, substs, span); } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 568d8358f0874..b8f8b132088a5 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -1,9 +1,9 @@ use rustc::hir::def_id::DefId; use rustc::mir::repr as mir; -use rustc::traits::{self, ProjectionMode}; +use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; -use rustc::ty::subst::{self, Substs}; +use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use std::rc::Rc; use std::iter; @@ -150,25 +150,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { - let name = self.tcx.item_name(def_id).as_str(); - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let layout = self.type_layout(ty); - let ret = return_ptr.unwrap(); - self.call_intrinsic(&name, substs, args, ret, layout) - } - ty::FnDiverging => unimplemented!(), - } + let ty = fn_ty.sig.0.output; + let layout = self.type_layout(ty); + let ret = return_ptr.unwrap(); + self.call_intrinsic(def_id, substs, args, ret, layout) } Abi::C => { - match fn_ty.sig.0.output { - ty::FnConverging(ty) => { - let size = self.type_size(ty); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size) - } - ty::FnDiverging => unimplemented!(), - } + let ty = fn_ty.sig.0.output; + let size = self.type_size(ty); + self.call_c_abi(def_id, args, return_ptr.unwrap(), size) } Abi::Rust | Abi::RustCall => { @@ -176,11 +167,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FnMut closure via FnOnce::call_once. // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = if substs.self_ty().is_some() { - self.trait_method(def_id, substs) - } else { - (def_id, substs) - }; + let (resolved_def_id, resolved_substs) = + if let Some(trait_id) = self.tcx.trait_of_item(def_id) { + self.trait_method(trait_id, def_id, substs) + } else { + (def_id, substs) + }; let mut arg_srcs = Vec::new(); for arg in args { @@ -265,7 +257,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn call_intrinsic( &mut self, - name: &str, + def_id: DefId, substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], dest: Pointer, @@ -275,10 +267,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .map(|arg| self.eval_operand(arg)) .collect(); let args_ptrs = args_res?; - let pointer_size = self.memory.pointer_size(); - match name { + match &self.tcx.item_name(def_id).as_str()[..] { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, @@ -287,7 +278,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "assume" => {} "copy_nonoverlapping" => { - let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_ty = substs.types[0]; let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; @@ -297,7 +288,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = *substs.types.get(subst::FnSpace, 0); + let ty = substs.types[0]; let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.memory.write_uint(dest, discr_val, 8)?; @@ -308,19 +299,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_ty = substs.types[0]; let elem_align = self.type_align(elem_ty); self.memory.write_uint(dest, elem_align as u64, pointer_size)?; } "move_val_init" => { - let ty = *substs.types.get(subst::FnSpace, 0); + let ty = substs.types[0]; let ptr = self.memory.read_ptr(args_ptrs[0])?; self.move_(args_ptrs[1], ptr, ty)?; } "offset" => { - let pointee_ty = *substs.types.get(subst::FnSpace, 0); + let pointee_ty = substs.types[0]; let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args_ptrs[0]; let offset = self.memory.read_isize(args_ptrs[1])?; @@ -342,21 +333,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "overflowing_sub" => { self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; } + "overflowing_mul" => { self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; } + "overflowing_add" => { self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; } "size_of" => { - let ty = *substs.types.get(subst::FnSpace, 0); + let ty = substs.types[0]; let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; } "size_of_val" => { - let ty = *substs.types.get(subst::FnSpace, 0); + let ty = substs.types[0]; if self.type_is_sized(ty) { let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; @@ -376,7 +369,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "transmute" => { - let ty = *substs.types.get(subst::FnSpace, 0); + let ty = substs.types[0]; self.move_(args_ptrs[0], dest, ty)?; } "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, @@ -464,7 +457,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - self.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + self.tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( @@ -486,21 +479,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Trait method, which has to be resolved to an impl method. fn trait_method( &self, + trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx> ) -> (DefId, &'tcx Substs<'tcx>) { - let method_item = self.tcx.impl_or_trait_item(def_id); - let trait_id = method_item.container().id(); - let trait_ref = ty::Binder(substs.to_trait_ref(self.tcx, trait_id)); + let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); + let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); + match self.fulfill_obligation(trait_ref) { traits::VtableImpl(vtable_impl) => { let impl_did = vtable_impl.impl_def_id; let mname = self.tcx.item_name(def_id); // Create a concatenated set of substitutions which includes those from the impl // and those from the method: - let impl_substs = vtable_impl.substs.with_method_from(substs); - let substs = self.tcx.mk_substs(impl_substs); - let mth = get_impl_method(self.tcx, impl_did, substs, mname); + let mth = get_impl_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); (mth.method.def_id, mth.substs) } @@ -573,8 +565,9 @@ struct ImplMethod<'tcx> { /// Locates the applicable definition of a method, given its name. fn get_impl_method<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - impl_def_id: DefId, substs: &'tcx Substs<'tcx>, + impl_def_id: DefId, + impl_substs: &'tcx Substs<'tcx>, name: ast::Name, ) -> ImplMethod<'tcx> { assert!(!substs.types.needs_infer()); @@ -584,7 +577,8 @@ fn get_impl_method<'a, 'tcx>( match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { Some(node_item) => { - let substs = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { + let substs = tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { + let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); tcx.lift(&substs).unwrap_or_else(|| { diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index db87e9e422bd8..f35aad87270ed 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -1,7 +1,7 @@ -fn main() { //~ ERROR tried to call a function of type +fn main() { fn f() {} - let g = unsafe { + let g = unsafe { //~ ERROR tried to call a function of type std::mem::transmute::(f) }; diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs index 054f91b3cf924..c7d25a663159c 100644 --- a/tests/compile-fail/execute_memory.rs +++ b/tests/compile-fail/execute_memory.rs @@ -1,10 +1,10 @@ #![feature(box_syntax)] -// FIXME: This span is wrong. -fn main() { //~ ERROR: tried to treat a memory pointer as a function pointer +fn main() { let x = box 42; unsafe { let f = std::mem::transmute::, fn()>(x); + //~^ ERROR: tried to treat a memory pointer as a function pointer f() } } From cd42bb97f0cd6ea6d758659f4208dbabe8f4873d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Sep 2016 16:04:51 +0200 Subject: [PATCH 0460/1096] rustup to rustc 1.13.0-nightly (91f057de3 2016-09-04) --- Cargo.lock | 12 ++++++------ src/bin/miri.rs | 19 +++++++++++-------- src/interpreter/mod.rs | 10 +++++----- src/interpreter/step.rs | 22 ++++++++++++++-------- src/interpreter/terminator.rs | 23 ++++++++++++----------- 5 files changed, 48 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0217a26fddd24..d85ee67cf3e1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.75 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -82,19 +82,19 @@ dependencies = [ [[package]] name = "regex" -version = "0.1.73" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -145,8 +145,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)" = "56b7ee9f764ecf412c6e2fff779bca4b22980517ae335a21aeaf4e32625a5df2" -"checksum regex-syntax 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "31040aad7470ad9d8c46302dcffba337bb4289ca5da2e3cd6e37b64109a85199" +"checksum regex 0.1.75 (registry+https://github.com/rust-lang/crates.io-index)" = "f62414f9d3b0f53e827ac46d6f8ce2ff6a91afd724225a5986e54e81e170693c" +"checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd" "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d" diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 728266a755d64..4fc20c96d2162 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -13,7 +13,7 @@ use miri::{eval_main, run_mir_passes}; use rustc::session::Session; use rustc::mir::mir_map::MirMap; use rustc_driver::{driver, CompilerCalls, Compilation}; -use syntax::ast::MetaItemKind; +use syntax::ast::{MetaItemKind, NestedMetaItemKind}; struct MiriCompilerCalls; @@ -52,14 +52,17 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { MetaItemKind::List(ref name, _) if name != "miri" => {} MetaItemKind::List(_, ref items) => for item in items { match item.node { - MetaItemKind::NameValue(ref name, ref value) => { - match &**name { - "memory_size" => memory_size = extract_str(value).parse().expect("not a number"), - "step_limit" => step_limit = extract_str(value).parse().expect("not a number"), - "stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"), - _ => state.session.span_err(item.span, "unknown miri attribute"), + NestedMetaItemKind::MetaItem(ref inner) => match inner.node { + MetaItemKind::NameValue(ref name, ref value) => { + match &**name { + "memory_size" => memory_size = extract_str(value).parse().expect("not a number"), + "step_limit" => step_limit = extract_str(value).parse().expect("not a number"), + "stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"), + _ => state.session.span_err(item.span, "unknown miri attribute"), + } } - } + _ => state.session.span_err(inner.span, "miri attributes need to be of key = value kind"), + }, _ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"), } }, diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5ef423f21fb41..0afca11c0ac5e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -292,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - self.tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { // TODO(solson): Report this error properly. ty.layout(&infcx).unwrap() }) @@ -454,7 +454,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } General { discr, ref variants, .. } => { - if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let discr_size = discr.size().bytes() as usize; self.memory.write_uint(dest, discr_val, discr_size)?; @@ -468,7 +468,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } RawNullablePointer { nndiscr, .. } => { - if let mir::AggregateKind::Adt(_, variant, _) = *kind { + if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); let operand = &operands[0]; @@ -485,7 +485,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield } => { - if let mir::AggregateKind::Adt(_, variant, _) = *kind { + if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { if nndiscr == variant as u64 { let offsets = iter::once(0) .chain(nonnull.offset_after_field.iter().map(|s| s.bytes())); @@ -503,7 +503,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { discr, signed, .. } => { assert_eq!(operands.len(), 0); - if let mir::AggregateKind::Adt(adt_def, variant, _) = *kind { + if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let size = discr.size().bytes() as usize; diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 75dc7ee0941cc..3175d2a16a3d1 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -24,11 +24,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let block = self.frame().block; - let stmt = self.frame().stmt; + let stmt_id = self.frame().stmt; let mir = self.mir(); let basic_block = &mir.basic_blocks()[block]; - if let Some(ref stmt) = basic_block.statements.get(stmt) { + if let Some(ref stmt) = basic_block.statements.get(stmt_id) { let mut new = Ok(0); ConstantExtractor { span: stmt.source_info.span, @@ -37,7 +37,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ecx: self, mir: &mir, new_constants: &mut new, - }.visit_statement(block, stmt); + }.visit_statement(block, stmt, mir::Location { + block: block, + statement_index: stmt_id, + }); if new? == 0 { self.statement(stmt)?; } @@ -55,7 +58,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ecx: self, mir: &mir, new_constants: &mut new, - }.visit_terminator(block, terminator); + }.visit_terminator(block, terminator, mir::Location { + block: block, + statement_index: stmt_id, + }); if new? == 0 { self.terminator(terminator)?; } @@ -135,8 +141,8 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { - fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { - self.super_constant(constant); + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) { + self.super_constant(constant, location); match constant.literal { // already computed by rustc mir::Literal::Value { .. } => {} @@ -170,8 +176,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } } - fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { - self.super_lvalue(lvalue, context); + fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext, location: mir::Location) { + self.super_lvalue(lvalue, context, location); if let mir::Lvalue::Static(def_id) = *lvalue { let substs = subst::Substs::empty(self.ecx.tcx); let span = self.span; diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index b8f8b132088a5..159cfd09ed368 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -240,6 +240,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // The discriminant_value intrinsic returns 0 for non-sum types. Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | Vector { .. } => 0, + UntaggedUnion { .. } => unimplemented!(), }; Ok(discr_val) @@ -278,7 +279,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "assume" => {} "copy_nonoverlapping" => { - let elem_ty = substs.types[0]; + let elem_ty = substs.types().next().expect("should at least have one type argument"); let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; @@ -288,7 +289,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = substs.types[0]; + let ty = substs.types().next().expect("should have at least one type argument"); let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.memory.write_uint(dest, discr_val, 8)?; @@ -299,19 +300,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - let elem_ty = substs.types[0]; + let elem_ty = substs.types().next().expect("should have at least one type argument"); let elem_align = self.type_align(elem_ty); self.memory.write_uint(dest, elem_align as u64, pointer_size)?; } "move_val_init" => { - let ty = substs.types[0]; + let ty = substs.types().next().expect("should have at least one type argument"); let ptr = self.memory.read_ptr(args_ptrs[0])?; self.move_(args_ptrs[1], ptr, ty)?; } "offset" => { - let pointee_ty = substs.types[0]; + let pointee_ty = substs.types().next().expect("should have at least one type argument"); let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args_ptrs[0]; let offset = self.memory.read_isize(args_ptrs[1])?; @@ -343,13 +344,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = substs.types[0]; + let ty = substs.types().next().expect("should have at least one type argument"); let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; } "size_of_val" => { - let ty = substs.types[0]; + let ty = substs.types().next().expect("should have at least one type argument"); if self.type_is_sized(ty) { let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; @@ -369,7 +370,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "transmute" => { - let ty = substs.types[0]; + let ty = substs.types().next().expect("should have at least one type argument"); self.move_(args_ptrs[0], dest, ty)?; } "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, @@ -457,7 +458,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - self.tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( @@ -570,14 +571,14 @@ fn get_impl_method<'a, 'tcx>( impl_substs: &'tcx Substs<'tcx>, name: ast::Name, ) -> ImplMethod<'tcx> { - assert!(!substs.types.needs_infer()); + assert!(!substs.types().any(|ty| ty.needs_infer())); let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { Some(node_item) => { - let substs = tcx.normalizing_infer_ctxt(Reveal::All).enter(|infcx| { + let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); From cd91f9feee00a1ed4c1630558424ee762d219994 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Sep 2016 16:16:49 +0200 Subject: [PATCH 0461/1096] replace all `unreachable!` and `panic!` calls with `bug!` --- src/bin/miri.rs | 2 +- src/interpreter/mod.rs | 48 ++++++++++++++++------------------- src/interpreter/terminator.rs | 4 +-- src/memory.rs | 4 +-- src/primval.rs | 24 +++++++++--------- 5 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4fc20c96d2162..15a970586b806 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -44,7 +44,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { match lit.node { syntax::ast::LitKind::Str(ref s, _) => s.clone(), - _ => panic!("attribute values need to be strings"), + _ => bug!("attribute values need to be strings"), } } for attr in krate.attrs.iter() { diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0afca11c0ac5e..d463cc69914b4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -187,9 +187,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_f64(ptr, f)?; Ok(ptr) }, - Float(ConstFloat::FInfer{..}) => unreachable!(), - Integral(ConstInt::Infer(_)) => unreachable!(), - Integral(ConstInt::InferSigned(_)) => unreachable!(), + Float(ConstFloat::FInfer{..}) | + Integral(ConstInt::Infer(_)) | + Integral(ConstInt::InferSigned(_)) => bug!("uninferred constants only exist before typeck"), Integral(ConstInt::I8(i)) => i2p!(i, 1), Integral(ConstInt::U8(i)) => i2p!(i, 1), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | @@ -255,7 +255,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); + bug!("no mir for `{}`", self.tcx.item_path_str(def_id)); }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); @@ -359,7 +359,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; let tup_layout = match *dest_layout { Univariant { ref variant, .. } => variant, - _ => panic!("checked bin op returns something other than a tuple"), + _ => bug!("checked bin op returns something other than a tuple"), }; let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; @@ -446,8 +446,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, - _ => panic!("tried to assign {:?} to non-array type {:?}", - kind, dest_ty), + _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); self.assign_fields(dest, offsets, operands)?; @@ -463,7 +462,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .map(|s| s.bytes()); self.assign_fields(dest, offsets, operands)?; } else { - panic!("tried to assign {:?} to Layout::General", kind); + bug!("tried to assign {:?} to Layout::General", kind); } } @@ -480,7 +479,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_isize(dest, 0)?; } } else { - panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); + bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); } } @@ -497,7 +496,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { try!(self.memory.write_isize(dest, 0)); } } else { - panic!("tried to assign {:?} to Layout::RawNullablePointer", kind); + bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); } } @@ -513,7 +512,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_uint(dest, val, size)?; } } else { - panic!("tried to assign {:?} to Layout::CEnum", kind); + bug!("tried to assign {:?} to Layout::CEnum", kind); } } @@ -524,7 +523,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Repeat(ref operand, _) => { let (elem_size, elem_align, length) = match dest_ty.sty { ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n), - _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), + _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; let src = self.eval_operand(operand)?; @@ -542,9 +541,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) => if let LvalueExtra::Length(n) = src.extra { n } else { - panic!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); + bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); }, - _ => panic!("Rvalue::Len expected array or slice, got {:?}", ty), + _ => bug!("Rvalue::Len expected array or slice, got {:?}", ty), }; self.memory.write_usize(dest, len)?; } @@ -559,7 +558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_usize(len_ptr, len)?; } LvalueExtra::DowncastVariant(..) => - panic!("attempted to take a reference to an enum downcast lvalue"), + bug!("attempted to take a reference to an enum downcast lvalue"), } } @@ -615,7 +614,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); self.memory.write_ptr(dest, fn_ptr)?; }, - ref other => panic!("reify fn pointer on {:?}", other), + ref other => bug!("reify fn pointer on {:?}", other), }, UnsafeFnPointer => match dest_ty.sty { @@ -626,7 +625,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.create_fn_ptr(fn_def.def_id, fn_def.substs, unsafe_fn_ty); self.memory.write_ptr(dest, fn_ptr)?; }, - ref other => panic!("fn to unsafe fn cast on {:?}", other), + ref other => bug!("fn to unsafe fn cast on {:?}", other), }, } } @@ -649,10 +648,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field = &variant.fields[index]; field.ty(self.tcx, substs) } - _ => panic!( - "non-enum for StructWrappedNullablePointer: {}", - ty, - ), + _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), }; self.field_path_offset(inner_ty, path) @@ -772,7 +768,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { &variants[variant_idx] } else { - panic!("field access on enum had no variant index"); + bug!("field access on enum had no variant index"); } } RawNullablePointer { .. } => { @@ -780,7 +776,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(base); } StructWrappedNullablePointer { ref nonnull, .. } => nonnull, - _ => panic!("field access on non-product type: {:?}", base_layout), + _ => bug!("field access on non-product type: {:?}", base_layout), }; let offset = variant.field_offset(field.index()).bytes(); @@ -799,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { return Ok(base); } - _ => panic!("variant downcast on non-aggregate: {:?}", base_layout), + _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), } }, @@ -822,7 +818,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | ty::TySlice(elem_ty) => self.type_size(elem_ty), - _ => panic!("indexing expected an array or slice, got {:?}", base_ty), + _ => bug!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; let n = self.memory.read_usize(n_ptr)?; @@ -901,7 +897,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - _ => panic!("primitive read of non-primitive type: {:?}", ty), + _ => bug!("primitive read of non-primitive type: {:?}", ty), }; Ok(val) } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 159cfd09ed368..1f45c36acd4bb 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -198,7 +198,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { arg_srcs.push((src, ty)); } } - ty => panic!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } } @@ -523,7 +523,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // ty: def_ty(tcx, def_id, substs) // } } - vtable => unreachable!("resolved vtable bad vtable {:?} in trans", vtable), + vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), } } diff --git a/src/memory.rs b/src/memory.rs index d99d2c8132e32..3cf0bc97557b1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { println!("(deallocated)"); continue; }, - (Some(_), Some(_)) => unreachable!(), + (Some(_), Some(_)) => bug!("miri invariant broken: an allocation id exists that points to both a function and a memory location"), }; for i in 0..alloc.bytes.len() { @@ -481,7 +481,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 2 => Ok(self.layout.i16_align.abi() as usize), 4 => Ok(self.layout.i32_align.abi() as usize), 8 => Ok(self.layout.i64_align.abi() as usize), - _ => panic!("bad integer size"), + _ => bug!("bad integer size"), } } diff --git a/src/primval.rs b/src/primval.rs index 1ec7516817034..0c26dac9ec915 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -49,8 +49,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva BitOr => $v($l | $r), // these have already been handled - Shl => unreachable!(), - Shr => unreachable!(), + Shl => bug!("`<<` operation should already have been handled"), + Shr => bug!("`>>` operation should already have been handled"), Eq => Bool($l == $r), Ne => Bool($l != $r), @@ -72,11 +72,11 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva Rem => $v($l % $r), // invalid float ops - BitXor => unreachable!(), - BitAnd => unreachable!(), - BitOr => unreachable!(), - Shl => unreachable!(), - Shr => unreachable!(), + BitXor => bug!("`^` is not a valid operation on floats"), + BitAnd => bug!("`&` is not a valid operation on floats"), + BitOr => bug!("`|` is not a valid operation on floats"), + Shl => bug!("`<<` is not a valid operation on floats"), + Shr => bug!("`>>` is not a valid operation on floats"), Eq => Bool($l == $r), Ne => Bool($l != $r), @@ -108,7 +108,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva I16(_) | U16(_) => 16, I32(_) | U32(_) => 32, I64(_) | U64(_) => 64, - _ => unreachable!(), + _ => bug!("bad MIR: bitshift lhs is not integral"), }; assert!(type_bits.is_power_of_two()); // turn into `u32` because `overflowing_sh{l,r}` only take `u32` @@ -121,7 +121,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva U16(i) => i as u32, U32(i) => i as u32, U64(i) => i as u32, - _ => panic!("bad MIR: bitshift rhs is not integral"), + _ => bug!("bad MIR: bitshift rhs is not integral"), }; // apply mask let r = r & (type_bits - 1); @@ -130,7 +130,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva match bin_op { Shl => overflow!($v, U32, $l, overflowing_shl, $r), Shr => overflow!($v, U32, $l, overflowing_shr, $r), - _ => unreachable!(), + _ => bug!("it has already been checked that this is a shift op"), } }) } @@ -143,7 +143,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva U16(l) => shift!(U16, l, r), U32(l) => shift!(U32, l, r), U64(l) => shift!(U64, l, r), - _ => unreachable!(), + _ => bug!("bad MIR: bitshift lhs is not integral (should already have been checked)"), }; return Ok((val, false)); }, @@ -168,7 +168,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva Le => Bool(l <= r), Gt => Bool(l > r), Ge => Bool(l >= r), - _ => panic!("invalid char op: {:?}", bin_op), + _ => bug!("invalid char op: {:?}", bin_op), }, (Bool(l), Bool(r)) => { From 7b24d55eca4979afe3d9cb852ce74396efb6f856 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 10:12:15 +0200 Subject: [PATCH 0462/1096] address comments --- src/interpreter/terminator.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 159cfd09ed368..3ab33e6bd9866 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -239,8 +239,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // The discriminant_value intrinsic returns 0 for non-sum types. Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } | - Vector { .. } => 0, - UntaggedUnion { .. } => unimplemented!(), + Vector { .. } | UntaggedUnion { .. } => 0, }; Ok(discr_val) @@ -279,7 +278,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "assume" => {} "copy_nonoverlapping" => { - let elem_ty = substs.types().next().expect("should at least have one type argument"); + let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; @@ -289,7 +288,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = substs.types().next().expect("should have at least one type argument"); + let ty = substs.type_at(0); let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.memory.write_uint(dest, discr_val, 8)?; @@ -300,19 +299,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - let elem_ty = substs.types().next().expect("should have at least one type argument"); + let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty); self.memory.write_uint(dest, elem_align as u64, pointer_size)?; } "move_val_init" => { - let ty = substs.types().next().expect("should have at least one type argument"); + let ty = substs.type_at(0); let ptr = self.memory.read_ptr(args_ptrs[0])?; self.move_(args_ptrs[1], ptr, ty)?; } "offset" => { - let pointee_ty = substs.types().next().expect("should have at least one type argument"); + let pointee_ty = substs.type_at(0); let pointee_size = self.type_size(pointee_ty) as isize; let ptr_arg = args_ptrs[0]; let offset = self.memory.read_isize(args_ptrs[1])?; @@ -344,13 +343,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = substs.types().next().expect("should have at least one type argument"); + let ty = substs.type_at(0); let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; } "size_of_val" => { - let ty = substs.types().next().expect("should have at least one type argument"); + let ty = substs.type_at(0); if self.type_is_sized(ty) { let size = self.type_size(ty) as u64; self.memory.write_uint(dest, size, pointer_size)?; @@ -370,7 +369,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "transmute" => { - let ty = substs.types().next().expect("should have at least one type argument"); + let ty = substs.type_at(0); self.move_(args_ptrs[0], dest, ty)?; } "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, @@ -571,7 +570,7 @@ fn get_impl_method<'a, 'tcx>( impl_substs: &'tcx Substs<'tcx>, name: ast::Name, ) -> ImplMethod<'tcx> { - assert!(!substs.types().any(|ty| ty.needs_infer())); + assert!(!substs.needs_infer()); let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); From 35e8882553dbfa2810dfcc68b7769daf442d7c66 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 10:27:32 +0200 Subject: [PATCH 0463/1096] still use `panic!` for missing MIR, because compiletest can't test compiler bugs --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d463cc69914b4..92bac85e56eb9 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -255,7 +255,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cs = &self.tcx.sess.cstore; let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - bug!("no mir for `{}`", self.tcx.item_path_str(def_id)); + panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); }); let cached = Rc::new(mir); mir_cache.insert(def_id, cached.clone()); From f5a89d297c8dc5b36a5dcea022002cb1cd9e666a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 10:27:57 +0200 Subject: [PATCH 0464/1096] rustc now can use integer literals in attributes --- src/bin/miri.rs | 14 +++++++------- tests/compile-fail/oom.rs | 4 ++-- tests/compile-fail/oom2.rs | 4 ++-- tests/compile-fail/stack_limit.rs | 4 ++-- tests/compile-fail/timeout.rs | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 15a970586b806..cc0132e4090d9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -41,12 +41,12 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut memory_size = 100*1024*1024; // 100MB let mut step_limit = 1000_000; let mut stack_limit = 100; - fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { + let extract_int = |lit: &syntax::ast::Lit| -> u64 { match lit.node { - syntax::ast::LitKind::Str(ref s, _) => s.clone(), - _ => bug!("attribute values need to be strings"), + syntax::ast::LitKind::Int(i, _) => i, + _ => state.session.span_fatal(lit.span, "expected an integer literal"), } - } + }; for attr in krate.attrs.iter() { match attr.node.value.node { MetaItemKind::List(ref name, _) if name != "miri" => {} @@ -55,9 +55,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { NestedMetaItemKind::MetaItem(ref inner) => match inner.node { MetaItemKind::NameValue(ref name, ref value) => { match &**name { - "memory_size" => memory_size = extract_str(value).parse().expect("not a number"), - "step_limit" => step_limit = extract_str(value).parse().expect("not a number"), - "stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"), + "memory_size" => memory_size = extract_int(value) as usize, + "step_limit" => step_limit = extract_int(value), + "stack_limit" => stack_limit = extract_int(value) as usize, _ => state.session.span_err(item.span, "unknown miri attribute"), } } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index 83109a77e6201..d3911a65f2f30 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -1,5 +1,5 @@ -#![feature(custom_attribute)] -#![miri(memory_size="0")] +#![feature(custom_attribute, attr_literals)] +#![miri(memory_size=0)] fn bar() { let x = 5; diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index 63c51dbaa7d26..d0344e4faeb3a 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -1,5 +1,5 @@ -#![feature(custom_attribute)] -#![miri(memory_size="1000")] +#![feature(custom_attribute, attr_literals)] +#![miri(memory_size=1000)] fn bar(i: i32) { if i < 1000 { diff --git a/tests/compile-fail/stack_limit.rs b/tests/compile-fail/stack_limit.rs index 3b6d4186dc905..2a78fbe5398f0 100644 --- a/tests/compile-fail/stack_limit.rs +++ b/tests/compile-fail/stack_limit.rs @@ -1,5 +1,5 @@ -#![feature(custom_attribute)] -#![miri(stack_limit="2")] +#![feature(custom_attribute, attr_literals)] +#![miri(stack_limit=2)] fn bar() { foo(); diff --git a/tests/compile-fail/timeout.rs b/tests/compile-fail/timeout.rs index bcb6c993089a7..edd4c31866910 100644 --- a/tests/compile-fail/timeout.rs +++ b/tests/compile-fail/timeout.rs @@ -1,6 +1,6 @@ //error-pattern: reached the configured maximum execution time -#![feature(custom_attribute)] -#![miri(step_limit="1000")] +#![feature(custom_attribute, attr_literals)] +#![miri(step_limit=1000)] fn main() { for i in 0..1000000 { From ca703f619cc01d69f30bbe4901db9c91c51aba00 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 10:30:49 +0200 Subject: [PATCH 0465/1096] DRY --- src/primval.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/primval.rs b/src/primval.rs index 0c26dac9ec915..4fda7cd0d1962 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -49,8 +49,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva BitOr => $v($l | $r), // these have already been handled - Shl => bug!("`<<` operation should already have been handled"), - Shr => bug!("`>>` operation should already have been handled"), + Shl | Shr => bug!("`bin_op` operation should already have been handled", bin_op.to_hir_binop().as_str()), Eq => Bool($l == $r), Ne => Bool($l != $r), @@ -72,11 +71,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva Rem => $v($l % $r), // invalid float ops - BitXor => bug!("`^` is not a valid operation on floats"), - BitAnd => bug!("`&` is not a valid operation on floats"), - BitOr => bug!("`|` is not a valid operation on floats"), - Shl => bug!("`<<` is not a valid operation on floats"), - Shr => bug!("`>>` is not a valid operation on floats"), + BitXor | BitAnd | BitOr | + Shl | Shr => bug!("`{}` is not a valid operation on floats", bin_op.to_hir_binop().as_str()), Eq => Bool($l == $r), Ne => Bool($l != $r), From 7be27ecb5316d8661e4335f53d0e4398b7e3a680 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 10:43:13 +0200 Subject: [PATCH 0466/1096] forgot to insert a {} into the format string --- src/primval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primval.rs b/src/primval.rs index 4fda7cd0d1962..6b24bf7530f85 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -49,7 +49,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva BitOr => $v($l | $r), // these have already been handled - Shl | Shr => bug!("`bin_op` operation should already have been handled", bin_op.to_hir_binop().as_str()), + Shl | Shr => bug!("`{}` operation should already have been handled", bin_op.to_hir_binop().as_str()), Eq => Bool($l == $r), Ne => Bool($l != $r), From ad053d66fec4e2cdea2a93621caa303029b3a1e9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 6 Jul 2016 17:55:05 +0200 Subject: [PATCH 0467/1096] change the block and stmt position after a function call returns previously we moved to the target block *before* calling a function, so when inspecting the stack, it appeared as if we were in the first statement of the next block. --- src/interpreter/mod.rs | 12 +++++-- src/interpreter/step.rs | 6 ++-- src/interpreter/terminator.rs | 54 ++++++++++++++++------------ tests/compile-fail/cast_fn_ptr.rs | 4 +-- tests/compile-fail/execute_memory.rs | 3 +- tests/compile-fail/oom.rs | 4 +-- 6 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 92bac85e56eb9..a8d29f7875857 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -71,6 +71,9 @@ pub struct Frame<'a, 'tcx: 'a> { /// A pointer for writing the return value of the current call if it's not a diverging call. pub return_ptr: Option, + /// The block to return to when returning from the current stack frame + pub return_to_block: Option, + /// The list of locals for the current function, stored in order as /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` /// and the temporaries at `self.temp_offset`. @@ -305,6 +308,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option, + return_to_block: Option, ) -> EvalResult<'tcx, ()> { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); @@ -325,6 +329,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir: mir.clone(), block: mir::START_BLOCK, return_ptr: return_ptr, + return_to_block: return_to_block, locals: locals?, var_offset: num_args, temp_offset: num_args + num_vars, @@ -342,7 +347,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn pop_stack_frame(&mut self) { ::log_settings::settings().indentation -= 1; - let _frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); + if let Some(target) = frame.return_to_block { + self.goto_block(target); + } // TODO(solson): Deallocate local variables. } @@ -961,7 +969,7 @@ pub fn eval_main<'a, 'tcx: 'a>( let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr)) + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr), None) .expect("could not allocate first stack frame"); if mir.arg_decls.len() == 2 { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 3175d2a16a3d1..3684f525ba632 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -87,8 +87,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { - // after a terminator we go to a new block - self.frame_mut().stmt = 0; trace!("{:?}", terminator.kind); self.eval_terminator(terminator)?; if !self.stack.is_empty() { @@ -125,7 +123,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); - this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)) + this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr), None) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -170,7 +168,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); - this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr)) + this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr), None) }); } } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index bdf8a5a946bd0..36e2c05f4f3ee 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -15,6 +15,12 @@ use error::{EvalError, EvalResult}; use memory::{Pointer, FunctionDefinition}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { + + pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { + self.frame_mut().block = target; + self.frame_mut().stmt = 0; + } + pub(super) fn eval_terminator( &mut self, terminator: &mir::Terminator<'tcx>, @@ -23,14 +29,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match terminator.kind { Return => self.pop_stack_frame(), - Goto { target } => { - self.frame_mut().block = target; - }, + Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; - self.frame_mut().block = if cond_val { then_target } else { else_target }; + self.goto_block(if cond_val { then_target } else { else_target }); } SwitchInt { ref discr, ref values, ref targets, .. } => { @@ -59,7 +63,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.frame_mut().block = target_block; + self.goto_block(target_block); } Switch { ref discr, ref targets, adt_def } => { @@ -70,19 +74,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .position(|v| discr_val == v.disr_val.to_u64_unchecked()); match matching { - Some(i) => { - self.frame_mut().block = targets[i]; - }, + Some(i) => self.goto_block(targets[i]), None => return Err(EvalError::InvalidDiscriminant), } } Call { ref func, ref args, ref destination, .. } => { - let mut return_ptr = None; - if let Some((ref lv, target)) = *destination { - self.frame_mut().block = target; - return_ptr = Some(self.eval_lvalue(lv)?.to_ptr()); - } + let destination = match *destination { + Some((ref lv, target)) => Some((self.eval_lvalue(lv)?.to_ptr(), target)), + None => None, + }; let func_ty = self.operand_ty(func); match func_ty.sty { @@ -93,11 +94,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); } - self.eval_fn_call(def_id, substs, bare_fn_ty, return_ptr, args, + self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, terminator.source_info.span)? }, ty::TyFnDef(def_id, substs, fn_ty) => { - self.eval_fn_call(def_id, substs, fn_ty, return_ptr, args, + self.eval_fn_call(def_id, substs, fn_ty, destination, args, terminator.source_info.span)? } @@ -109,13 +110,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.eval_lvalue(location)?.to_ptr(); let ty = self.lvalue_ty(location); self.drop(ptr, ty)?; - self.frame_mut().block = target; + self.goto_block(target); } Assert { ref cond, expected, ref msg, target, .. } => { let cond_ptr = self.eval_operand(cond)?; if expected == self.memory.read_bool(cond_ptr)? { - self.frame_mut().block = target; + self.goto_block(target); } else { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { @@ -143,7 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy, - return_ptr: Option, + destination: Option<(Pointer, mir::BasicBlock)>, args: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx, ()> { @@ -152,14 +153,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::RustIntrinsic => { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); - let ret = return_ptr.unwrap(); - self.call_intrinsic(def_id, substs, args, ret, layout) + let (ret, target) = destination.unwrap(); + self.call_intrinsic(def_id, substs, args, ret, layout)?; + self.goto_block(target); + Ok(()) } Abi::C => { let ty = fn_ty.sig.0.output; let size = self.type_size(ty); - self.call_c_abi(def_id, args, return_ptr.unwrap(), size) + let (ret, target) = destination.unwrap(); + self.call_c_abi(def_id, args, ret, size)?; + self.goto_block(target); + Ok(()) } Abi::Rust | Abi::RustCall => { @@ -203,7 +209,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let mir = self.load_mir(resolved_def_id); - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr)?; + let (return_ptr, return_to_block) = match destination { + Some((ptr, block)) => (Some(ptr), Some(block)), + None => (None, None), + }; + self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index f35aad87270ed..e9b2536a7005a 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -1,9 +1,9 @@ fn main() { fn f() {} - let g = unsafe { //~ ERROR tried to call a function of type + let g = unsafe { std::mem::transmute::(f) }; - g(42) + g(42) //~ ERROR tried to call a function of type } diff --git a/tests/compile-fail/execute_memory.rs b/tests/compile-fail/execute_memory.rs index c7d25a663159c..8d3c9df0320ba 100644 --- a/tests/compile-fail/execute_memory.rs +++ b/tests/compile-fail/execute_memory.rs @@ -4,7 +4,6 @@ fn main() { let x = box 42; unsafe { let f = std::mem::transmute::, fn()>(x); - //~^ ERROR: tried to treat a memory pointer as a function pointer - f() + f() //~ ERROR: tried to treat a memory pointer as a function pointer } } diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index d3911a65f2f30..be56240af4767 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -6,6 +6,6 @@ fn bar() { assert_eq!(x, 6); } -fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory - bar(); +fn main() { + bar(); //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory } From 0f177fdecf07ae2a23c17452a6b00b2ba2c5b36c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 18:34:59 +0200 Subject: [PATCH 0468/1096] implement more casts --- src/error.rs | 2 +- src/interpreter/cast.rs | 96 +++++++++++++++++++++++++++++ src/interpreter/mod.rs | 113 +++++++++++++++++++++++++++------- src/interpreter/terminator.rs | 2 +- src/memory.rs | 4 +- 5 files changed, 190 insertions(+), 27 deletions(-) create mode 100644 src/interpreter/cast.rs diff --git a/src/error.rs b/src/error.rs index 5b39399070c22..726a4dcfa3fe5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,7 +28,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), - InvalidChar(u32), + InvalidChar(u64), OutOfMemory { allocation_size: usize, memory_size: usize, diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs new file mode 100644 index 0000000000000..580f957b44916 --- /dev/null +++ b/src/interpreter/cast.rs @@ -0,0 +1,96 @@ + +use super::{ + EvalContext, +}; +use error::{EvalResult, EvalError}; +use rustc::ty; +use primval::PrimVal; +use memory::Pointer; + +use rustc::ty::Ty; +use syntax::ast; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + match val { + Bool(b) => self.cast_const_int(b as u64, ty, false), + F32(f) => self.cast_const_float(f as f64, ty), + F64(f) => self.cast_const_float(f, ty), + I8(i) => self.cast_signed_int(i as i64, ty), + I16(i) => self.cast_signed_int(i as i64, ty), + I32(i) => self.cast_signed_int(i as i64, ty), + I64(i) => self.cast_signed_int(i, ty), + U8(u) => self.cast_const_int(u as u64, ty, false), + U16(u) => self.cast_const_int(u as u64, ty, false), + U32(u) => self.cast_const_int(u as u64, ty, false), + Char(c) => self.cast_const_int(c as u64, ty, false), + U64(u) | + IntegerPtr(u) => self.cast_const_int(u, ty, false), + FnPtr(ptr) | + AbstractPtr(ptr) => self.cast_ptr(ptr, ty), + } + } + + fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + match ty.sty { + ty::TyRef(..) | + ty::TyRawPtr(_) => Ok(AbstractPtr(ptr)), + ty::TyFnPtr(_) => Ok(FnPtr(ptr)), + _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), + } + } + + fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + self.cast_const_int(val as u64, ty, val < 0) + } + + fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + match ty.sty { + ty::TyBool if v == 0 => Ok(Bool(false)), + ty::TyBool if v == 1 => Ok(Bool(true)), + ty::TyBool => Err(EvalError::InvalidBool), + ty::TyInt(ast::IntTy::I8) => Ok(I8(v as i64 as i8)), + ty::TyInt(ast::IntTy::I16) => Ok(I16(v as i64 as i16)), + ty::TyInt(ast::IntTy::I32) => Ok(I32(v as i64 as i32)), + ty::TyInt(ast::IntTy::I64) => Ok(I64(v as i64)), + ty::TyInt(ast::IntTy::Is) => { + let int_ty = self.tcx.sess.target.int_type; + let ty = self.tcx.mk_mach_int(int_ty); + self.cast_const_int(v, ty, negative) + }, + ty::TyUint(ast::UintTy::U8) => Ok(U8(v as u8)), + ty::TyUint(ast::UintTy::U16) => Ok(U16(v as u16)), + ty::TyUint(ast::UintTy::U32) => Ok(U32(v as u32)), + ty::TyUint(ast::UintTy::U64) => Ok(U64(v)), + ty::TyUint(ast::UintTy::Us) => { + let uint_ty = self.tcx.sess.target.uint_type; + let ty = self.tcx.mk_mach_uint(uint_ty); + self.cast_const_int(v, ty, negative) + }, + ty::TyFloat(ast::FloatTy::F64) if negative => Ok(F64(v as i64 as f64)), + ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)), + ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)), + ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)), + ty::TyRawPtr(_) => Ok(IntegerPtr(v)), + ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)), + ty::TyChar => Err(EvalError::InvalidChar(v)), + _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), + } + } + + fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + match ty.sty { + // casting negative floats to unsigned integers yields zero + ty::TyUint(_) if val < 0.0 => self.cast_const_int(0, ty, false), + ty::TyInt(_) if val < 0.0 => self.cast_const_int(val as i64 as u64, ty, true), + ty::TyInt(_) | ty::TyUint(_) => self.cast_const_int(val as u64, ty, false), + ty::TyFloat(ast::FloatTy::F64) => Ok(F64(val)), + ty::TyFloat(ast::FloatTy::F32) => Ok(F32(val as f32)), + _ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))), + } + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a8d29f7875857..bbdde3729c9ec 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -22,6 +22,7 @@ use std::collections::HashMap; mod step; mod terminator; +mod cast; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -211,9 +212,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len(), 1)?; let ptr = self.memory.allocate(psize * 2, psize)?; + let (ptr, extra) = self.get_fat_ptr(ptr); self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; - self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; + self.memory.write_usize(extra, s.len() as u64)?; Ok(ptr) } ByteStr(ref bs) => { @@ -244,6 +246,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + // generics are weird, don't run this function on a generic + debug_assert_eq!(self.monomorphize(ty, self.substs()), ty); ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } @@ -558,12 +562,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let lv = self.eval_lvalue(lvalue)?; - self.memory.write_ptr(dest, lv.ptr)?; + let (ptr, extra) = self.get_fat_ptr(dest); + self.memory.write_ptr(ptr, lv.ptr)?; match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { - let len_ptr = dest.offset(self.memory.pointer_size() as isize); - self.memory.write_usize(len_ptr, len)?; + self.memory.write_usize(extra, len)?; } LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -583,14 +587,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Unsize => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - self.move_(src, dest, src_ty)?; + let (ptr, extra) = self.get_fat_ptr(dest); + self.move_(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let len_ptr = dest.offset(self.memory.pointer_size() as isize); - self.memory.write_usize(len_ptr, length as u64)?; + self.memory.write_usize(extra, length as u64)?; } _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), @@ -600,20 +604,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - // FIXME(solson): Wrong for almost everything. - warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); - let dest_size = self.type_size(dest_ty); - let src_size = self.type_size(src_ty); - let dest_align = self.type_align(dest_ty); - - // Hack to support fat pointer -> thin pointer casts to keep tests for - // other things passing for now. - let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); - - if dest_size == src_size || is_fat_ptr_cast { - self.memory.copy(src, dest, dest_size, dest_align)?; + if self.type_is_immediate(src_ty) { + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); + assert!(self.type_is_immediate(dest_ty)); + let src_val = self.read_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, dest_ty)?; + self.memory.write_primval(dest, dest_val)?; } else { - return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); + // Casts from a fat-ptr. + assert!(self.type_is_fat_ptr(src_ty)); + let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); + let ptr_size = self.memory.pointer_size(); + let dest_ty = self.monomorphize(dest_ty, self.substs()); + if self.type_is_fat_ptr(dest_ty) { + // FIXME: add assertion that the extra part of the src_ty and + // dest_ty is of the same type + self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?; + } else { // cast to thin-ptr + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and + // pointer-cast of that pointer to desired pointer type. + self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; + } } } @@ -644,6 +656,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + /// equivalent to rustc_trans::common::type_is_immediate + fn type_is_immediate(&self, ty: Ty<'tcx>) -> bool { + let simple = ty.is_scalar() || + ty.is_unique() || ty.is_region_ptr() || + ty.is_simd(); + if simple && !self.type_is_fat_ptr(ty) { + return true; + } + if !self.type_is_sized(ty) { + return false; + } + match ty.sty { + ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) | + ty::TyClosure(..) => { + self.type_size(ty) < self.memory.pointer_size() + } + _ => self.type_size(ty) == 0 + } + } + + fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => !self.type_is_sized(ty), + _ => false, + } + } + fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -809,11 +850,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); + self.memory.dump(base.ptr.alloc_id); let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { - let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize); - let len = self.memory.read_usize(len_ptr)?; + let (_, extra) = self.get_fat_ptr(base.ptr); + let len = self.memory.read_usize(extra)?; LvalueExtra::Length(len) } ty::TyTrait(_) => unimplemented!(), @@ -842,6 +884,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } + fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + (ptr, ptr.offset(self.memory.pointer_size() as isize)) + } + fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } @@ -865,7 +913,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::Char(ch), - None => return Err(EvalError::InvalidChar(c)), + None => return Err(EvalError::InvalidChar(c as u64)), } } (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), @@ -905,6 +953,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + (_, &ty::TyEnum(..)) => { + use rustc::ty::layout::Layout::*; + if let CEnum { discr, signed, .. } = *self.type_layout(ty) { + match (discr.size().bytes(), signed) { + (1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), + (2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), + (4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), + (8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), + (1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), + (2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), + (4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), + (8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + (size, _) => bug!("CEnum discr size {}", size), + } + } else { + bug!("primitive read of non-clike enum: {:?}", ty); + } + }, + _ => bug!("primitive read of non-primitive type: {:?}", ty), }; Ok(val) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 36e2c05f4f3ee..afba23fac5999 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { - return Err(EvalError::InvalidChar(discr_val as u32)); + return Err(EvalError::InvalidChar(discr_val as u64)); } } diff --git a/src/memory.rs b/src/memory.rs index 3cf0bc97557b1..75cbb16122f9b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -455,8 +455,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::F32(f) => self.write_f32(ptr, f), PrimVal::F64(f) => self.write_f64(ptr, f), - PrimVal::FnPtr(_p) | - PrimVal::AbstractPtr(_p) => unimplemented!(), + PrimVal::FnPtr(p) | + PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p), } } From d627cc749f9d504751e0636da3be620f01520088 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 8 Sep 2016 10:25:45 +0200 Subject: [PATCH 0469/1096] use cheap assertions instead of expensive debug assertions --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bbdde3729c9ec..ef6318a35ae69 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -5,7 +5,7 @@ use rustc::mir::repr as mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; @@ -247,7 +247,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic - debug_assert_eq!(self.monomorphize(ty, self.substs()), ty); + assert!(!ty.needs_subst()); ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } From 168d9e77456b09d062f84d808c0f42336c03acb1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 8 Sep 2016 10:26:33 +0200 Subject: [PATCH 0470/1096] don't use `type_is_immediate` for finding fat ptr casts --- src/interpreter/mod.rs | 37 +++++++------------------------------ 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ef6318a35ae69..e77ec942bbf55 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -604,16 +604,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - if self.type_is_immediate(src_ty) { - // FIXME: dest_ty should already be monomorphized - let dest_ty = self.monomorphize(dest_ty, self.substs()); - assert!(self.type_is_immediate(dest_ty)); - let src_val = self.read_primval(src, src_ty)?; - let dest_val = self.cast_primval(src_val, dest_ty)?; - self.memory.write_primval(dest, dest_val)?; - } else { - // Casts from a fat-ptr. - assert!(self.type_is_fat_ptr(src_ty)); + if self.type_is_fat_ptr(src_ty) { let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); let ptr_size = self.memory.pointer_size(); let dest_ty = self.monomorphize(dest_ty, self.substs()); @@ -626,6 +617,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // pointer-cast of that pointer to desired pointer type. self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; } + } else { + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); + let src_val = self.read_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, dest_ty)?; + self.memory.write_primval(dest, dest_val)?; } } @@ -656,26 +653,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - /// equivalent to rustc_trans::common::type_is_immediate - fn type_is_immediate(&self, ty: Ty<'tcx>) -> bool { - let simple = ty.is_scalar() || - ty.is_unique() || ty.is_region_ptr() || - ty.is_simd(); - if simple && !self.type_is_fat_ptr(ty) { - return true; - } - if !self.type_is_sized(ty) { - return false; - } - match ty.sty { - ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) | - ty::TyClosure(..) => { - self.type_size(ty) < self.memory.pointer_size() - } - _ => self.type_size(ty) == 0 - } - } - fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | From 00c551c5f063ea40f241ed50c5de6c67de8173f9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Sep 2016 12:51:14 +0200 Subject: [PATCH 0471/1096] implement calling methods through trait objects --- src/error.rs | 6 ++ src/interpreter/mod.rs | 37 +++++-- src/interpreter/terminator.rs | 67 +++++++----- src/interpreter/vtable.rs | 195 ++++++++++++++++++++++++++++++++++ src/memory.rs | 73 ++++++++++--- tests/run-pass/traits.rs | 16 +++ 6 files changed, 347 insertions(+), 47 deletions(-) create mode 100644 src/interpreter/vtable.rs create mode 100644 tests/run-pass/traits.rs diff --git a/src/error.rs b/src/error.rs index 726a4dcfa3fe5..7129fa0dfcd82 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,6 +40,8 @@ pub enum EvalError<'tcx> { required: usize, has: usize, }, + CalledClosureAsFunction, + VtableForArgumentlessMethod, } pub type EvalResult<'tcx, T> = Result>; @@ -88,6 +90,10 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the configured maximum number of stack frames", EvalError::AlignmentCheckFailed{..} => "tried to execute a misaligned read or write", + EvalError::CalledClosureAsFunction => + "tried to call a closure through a function pointer", + EvalError::VtableForArgumentlessMethod => + "tried to call a vtable function without arguments", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e77ec942bbf55..370b1efc49914 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -23,6 +23,7 @@ use std::collections::HashMap; mod step; mod terminator; mod cast; +mod vtable; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -108,7 +109,7 @@ struct Lvalue { enum LvalueExtra { None, Length(u64), - // TODO(solson): Vtable(memory::AllocId), + Vtable(Pointer), DowncastVariant(usize), } @@ -569,6 +570,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { LvalueExtra::Length(len) => { self.memory.write_usize(extra, len)?; } + LvalueExtra::Vtable(ptr) => { + self.memory.write_ptr(extra, ptr)?; + }, LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), } @@ -587,6 +591,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Unsize => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); + let dest_ty = self.monomorphize(dest_ty, self.substs()); + assert!(self.type_is_fat_ptr(dest_ty)); let (ptr, extra) = self.get_fat_ptr(dest); self.move_(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); @@ -596,8 +602,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { self.memory.write_usize(extra, length as u64)?; } - - _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), + (&ty::TyTrait(_), &ty::TyTrait(_)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + let (_, src_extra) = self.get_fat_ptr(src); + let src_extra = self.memory.read_ptr(src_extra)?; + self.memory.write_ptr(extra, src_extra)?; + }, + (_, &ty::TyTrait(ref data)) => { + let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); + let trait_ref = self.tcx.erase_regions(&trait_ref); + let vtable = self.get_vtable(trait_ref)?; + self.memory.write_ptr(extra, vtable)?; + }, + + _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), } } @@ -638,8 +658,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(unsafe_fn_ty) => { let src = self.eval_operand(operand)?; let ptr = self.memory.read_ptr(src)?; - let fn_def = self.memory.get_fn(ptr.alloc_id)?; - let fn_ptr = self.memory.create_fn_ptr(fn_def.def_id, fn_def.substs, unsafe_fn_ty); + let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; + let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); self.memory.write_ptr(dest, fn_ptr)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), @@ -827,7 +847,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - self.memory.dump(base.ptr.alloc_id); let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { @@ -835,7 +854,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let len = self.memory.read_usize(extra)?; LvalueExtra::Length(len) } - ty::TyTrait(_) => unimplemented!(), + ty::TyTrait(_) => { + let (_, extra) = self.get_fat_ptr(base.ptr); + let vtable = self.memory.read_ptr(extra)?; + LvalueExtra::Vtable(vtable) + }, _ => LvalueExtra::None, }; return Ok(Lvalue { ptr: ptr, extra: extra }); diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index afba23fac5999..2720801123c6b 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -12,7 +12,7 @@ use syntax::codemap::{DUMMY_SP, Span}; use super::{EvalContext, IntegerExt}; use error::{EvalError, EvalResult}; -use memory::{Pointer, FunctionDefinition}; +use memory::Pointer; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -90,7 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(bare_fn_ty) => { let ptr = self.eval_operand(func)?; let fn_ptr = self.memory.read_ptr(ptr)?; - let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; + let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); } @@ -172,14 +172,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Adjust the first argument when calling a Fn or // FnMut closure via FnOnce::call_once. - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = - if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs) - } else { - (def_id, substs) - }; - let mut arg_srcs = Vec::new(); for arg in args { let src = self.eval_operand(arg)?; @@ -187,6 +179,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { arg_srcs.push((src, src_ty)); } + // Only trait methods can have a Self parameter. + let (resolved_def_id, resolved_substs) = + if let Some(trait_id) = self.tcx.trait_of_item(def_id) { + self.trait_method(trait_id, def_id, substs, arg_srcs.get_mut(0))? + } else { + (def_id, substs) + }; + if fn_ty.abi == Abi::RustCall && !args.is_empty() { arg_srcs.pop(); let last_arg = args.last().unwrap(); @@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + pub (super) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { @@ -491,8 +491,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self, trait_id: DefId, def_id: DefId, - substs: &'tcx Substs<'tcx> - ) -> (DefId, &'tcx Substs<'tcx>) { + substs: &'tcx Substs<'tcx>, + first_arg: Option<&mut (Pointer, Ty<'tcx>)>, + ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -504,11 +505,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // and those from the method: let mth = get_impl_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - (mth.method.def_id, mth.substs) + Ok((mth.method.def_id, mth.substs)) } traits::VtableClosure(vtable_closure) => - (vtable_closure.closure_def_id, vtable_closure.substs.func_substs), + Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), traits::VtableFnPointer(_fn_ty) => { let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); @@ -524,14 +525,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) } - traits::VtableObject(ref _data) => { - unimplemented!() - // Callee { - // data: Virtual(traits::get_vtable_index_of_object_method( - // tcx, data, def_id)), - // ty: def_ty(tcx, def_id, substs) - // } - } + traits::VtableObject(ref data) => { + let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); + if let Some(&mut(first_arg, ref mut first_ty)) = first_arg { + self.memory.dump(first_arg.alloc_id); + println!("idx: {}", idx); + let (_, vtable) = self.get_fat_ptr(first_arg); + let vtable = self.memory.read_ptr(vtable)?; + let idx = idx + 3; + let offset = idx * self.memory.pointer_size(); + let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; + let (def_id, substs, ty) = self.memory.get_fn(fn_ptr.alloc_id)?; + // FIXME: skip_binder is wrong for HKL + *first_ty = ty.sig.skip_binder().inputs[0]; + Ok((def_id, substs)) + } else { + Err(EvalError::VtableForArgumentlessMethod) + } + }, vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), } } @@ -566,14 +577,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } #[derive(Debug)] -struct ImplMethod<'tcx> { - method: Rc>, - substs: &'tcx Substs<'tcx>, - is_provided: bool, +pub (super) struct ImplMethod<'tcx> { + pub (super) method: Rc>, + pub (super) substs: &'tcx Substs<'tcx>, + pub (super) is_provided: bool, } /// Locates the applicable definition of a method, given its name. -fn get_impl_method<'a, 'tcx>( +pub (super) fn get_impl_method<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: &'tcx Substs<'tcx>, impl_def_id: DefId, diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs new file mode 100644 index 0000000000000..76ec5f5f19430 --- /dev/null +++ b/src/interpreter/vtable.rs @@ -0,0 +1,195 @@ +use rustc::hir::def_id::DefId; +use rustc::traits::{self, Reveal, SelectionContext}; +use rustc::ty::subst::{Substs, Subst}; +use rustc::ty; + +use super::EvalContext; +use error::EvalResult; +use memory::Pointer; +use super::terminator::{get_impl_method, ImplMethod}; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + /// Creates a returns a dynamic vtable for the given type and vtable origin. + /// This is used only for objects. + /// + /// The `trait_ref` encodes the erased self type. Hence if we are + /// making an object `Foo` from a value of type `Foo`, then + /// `trait_ref` would map `T:Trait`. + pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { + let tcx = self.tcx; + + debug!("get_vtable(trait_ref={:?})", trait_ref); + + let methods: Vec<_> = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| { + match self.fulfill_obligation(trait_ref.clone()) { + // Should default trait error here? + traits::VtableDefaultImpl(_) | + traits::VtableBuiltin(_) => { + Vec::new().into_iter() + } + traits::VtableImpl( + traits::VtableImplData { + impl_def_id: id, + substs, + nested: _ }) => { + self.get_vtable_methods(id, substs) + .into_iter() + .map(|opt_mth| opt_mth.map(|mth| { + self.memory.create_fn_ptr(mth.method.def_id, mth.substs, mth.method.fty) + })) + .collect::>() + .into_iter() + } + traits::VtableClosure( + traits::VtableClosureData { + closure_def_id, + substs, + nested: _ }) => { + let closure_type = self.tcx.closure_type(closure_def_id, substs); + vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() + } + traits::VtableFnPointer( + traits::VtableFnPointerData { + fn_ty: bare_fn_ty, + nested: _ }) => { + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); + //vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter() + unimplemented!() + } + traits::VtableObject(ref data) => { + // this would imply that the Self type being erased is + // an object type; this cannot happen because we + // cannot cast an unsized type into a trait object + bug!("cannot get vtable for an object type: {:?}", + data); + } + vtable @ traits::VtableParam(..) => { + bug!("resolved vtable for {:?} to bad vtable {:?} in trans", + trait_ref, + vtable); + } + } + }).collect(); + + let size = self.type_size(trait_ref.self_ty()); + let align = self.type_align(trait_ref.self_ty()); + + let ptr_size = self.memory.pointer_size(); + let vtable = self.memory.allocate(ptr_size * (3 + methods.len()), ptr_size)?; + + // FIXME: generate a destructor for the vtable. + // trans does this with glue::get_drop_glue(ccx, trait_ref.self_ty()) + + self.memory.write_usize(vtable.offset(ptr_size as isize), size as u64)?; + self.memory.write_usize(vtable.offset((ptr_size * 2) as isize), align as u64)?; + + for (i, method) in methods.into_iter().enumerate() { + if let Some(method) = method { + self.memory.write_ptr(vtable.offset(ptr_size as isize * (3 + i as isize)), method)?; + } + } + + Ok(vtable) + } + + fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec>> { + debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); + + let trait_id = match self.tcx.impl_trait_ref(impl_id) { + Some(t_id) => t_id.def_id, + None => bug!("make_impl_vtable: don't know how to \ + make a vtable for a type impl!") + }; + + self.tcx.populate_implementations_for_trait_if_necessary(trait_id); + + let trait_item_def_ids = self.tcx.trait_item_def_ids(trait_id); + trait_item_def_ids + .iter() + + // Filter out non-method items. + .filter_map(|item_def_id| { + match *item_def_id { + ty::MethodTraitItemId(def_id) => Some(def_id), + _ => None, + } + }) + + // Now produce pointers for each remaining method. If the + // method could never be called from this object, just supply + // null. + .map(|trait_method_def_id| { + debug!("get_vtable_methods: trait_method_def_id={:?}", + trait_method_def_id); + + let trait_method_type = match self.tcx.impl_or_trait_item(trait_method_def_id) { + ty::MethodTraitItem(m) => m, + _ => bug!("should be a method, not other assoc item"), + }; + let name = trait_method_type.name; + + // Some methods cannot be called on an object; skip those. + if !self.tcx.is_vtable_safe_method(trait_id, &trait_method_type) { + debug!("get_vtable_methods: not vtable safe"); + return None; + } + + debug!("get_vtable_methods: trait_method_type={:?}", + trait_method_type); + + // the method may have some early-bound lifetimes, add + // regions for those + let method_substs = Substs::for_item(self.tcx, trait_method_def_id, + |_, _| self.tcx.mk_region(ty::ReErased), + |_, _| self.tcx.types.err); + + // The substitutions we have are on the impl, so we grab + // the method type from the impl to substitute into. + let mth = get_impl_method(self.tcx, method_substs, impl_id, substs, name); + + debug!("get_vtable_methods: mth={:?}", mth); + + // If this is a default method, it's possible that it + // relies on where clauses that do not hold for this + // particular set of type parameters. Note that this + // method could then never be called, so we do not want to + // try and trans it, in that case. Issue #23435. + if mth.is_provided { + let predicates = mth.method.predicates.predicates.subst(self.tcx, &mth.substs); + if !self.normalize_and_test_predicates(predicates) { + debug!("get_vtable_methods: predicates do not hold"); + return None; + } + } + + Some(mth) + }) + .collect() + } + + /// Normalizes the predicates and checks whether they hold. If this + /// returns false, then either normalize encountered an error or one + /// of the predicates did not hold. Used when creating vtables to + /// check for unsatisfiable methods. + fn normalize_and_test_predicates(&mut self, predicates: Vec>) -> bool { + debug!("normalize_and_test_predicates(predicates={:?})", + predicates); + + self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + let mut fulfill_cx = traits::FulfillmentContext::new(); + let cause = traits::ObligationCause::dummy(); + let traits::Normalized { value: predicates, obligations } = + traits::normalize(&mut selcx, cause.clone(), &predicates); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + for predicate in predicates { + let obligation = traits::Obligation::new(cause.clone(), predicate); + fulfill_cx.register_predicate_obligation(&infcx, obligation); + } + + fulfill_cx.select_all_or_error(&infcx).is_ok() + }) + } +} diff --git a/src/memory.rs b/src/memory.rs index 75cbb16122f9b..25ae6b3d995ba 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,7 +4,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr}; use rustc::hir::def_id::DefId; -use rustc::ty::BareFnTy; +use rustc::ty::{BareFnTy, ClosureTy, ClosureSubsts}; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; @@ -53,11 +53,22 @@ impl Pointer { } } -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] -pub struct FunctionDefinition<'tcx> { +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +struct FunctionDefinition<'tcx> { pub def_id: DefId, - pub substs: &'tcx Substs<'tcx>, - pub fn_ty: &'tcx BareFnTy<'tcx>, + pub kind: FunctionKind<'tcx>, +} + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +enum FunctionKind<'tcx> { + Closure { + substs: ClosureSubsts<'tcx>, + ty: ClosureTy<'tcx>, + }, + Function { + substs: &'tcx Substs<'tcx>, + ty: &'tcx BareFnTy<'tcx>, + } } //////////////////////////////////////////////////////////////////////////////// @@ -112,12 +123,27 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } + pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + self.create_fn_alloc(FunctionDefinition { + def_id: def_id, + kind: FunctionKind::Closure { + substs: substs, + ty: fn_ty, + } + }) + } + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { - let def = FunctionDefinition { + self.create_fn_alloc(FunctionDefinition { def_id: def_id, - substs: substs, - fn_ty: fn_ty, - }; + kind: FunctionKind::Function { + substs: substs, + ty: fn_ty, + } + }) + } + + fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { return Pointer { alloc_id: alloc_id, @@ -127,7 +153,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; - self.functions.insert(id, def); + self.functions.insert(id, def.clone()); self.function_alloc_cache.insert(def, id); Pointer { alloc_id: id, @@ -269,10 +295,33 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { + pub fn get_closure(&self, id: AllocId) -> EvalResult<'tcx, (DefId, ClosureSubsts<'tcx>, ClosureTy<'tcx>)> { + debug!("reading closure fn ptr: {}", id); + match self.functions.get(&id) { + Some(&FunctionDefinition { + def_id, + kind: FunctionKind::Closure { ref substs, ref ty } + }) => Ok((def_id, substs.clone(), ty.clone())), + Some(&FunctionDefinition { + kind: FunctionKind::Function { .. }, .. + }) => Err(EvalError::CalledClosureAsFunction), + None => match self.alloc_map.get(&id) { + Some(_) => Err(EvalError::ExecuteMemory), + None => Err(EvalError::InvalidFunctionPointer), + } + } + } + + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, &'tcx BareFnTy<'tcx>)> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { - Some(&fn_id) => Ok(fn_id), + Some(&FunctionDefinition { + def_id, + kind: FunctionKind::Function { substs, ty } + }) => Ok((def_id, substs, ty)), + Some(&FunctionDefinition { + kind: FunctionKind::Closure { .. }, .. + }) => Err(EvalError::CalledClosureAsFunction), None => match self.alloc_map.get(&id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), diff --git a/tests/run-pass/traits.rs b/tests/run-pass/traits.rs new file mode 100644 index 0000000000000..5f9668d1303a8 --- /dev/null +++ b/tests/run-pass/traits.rs @@ -0,0 +1,16 @@ +struct Struct(i32); + +trait Trait { + fn method(&self); +} + +impl Trait for Struct { + fn method(&self) { + assert_eq!(self.0, 42); + } +} + +fn main() { + let y: &Trait = &Struct(42); + y.method(); +} From bcda724c432cd62c55e73948b1b201e72613182b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Sep 2016 15:44:35 +0200 Subject: [PATCH 0472/1096] closures don't work yet --- src/interpreter/terminator.rs | 2 -- src/interpreter/vtable.rs | 9 ++++++++- tests/run-pass/traits.rs | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 2720801123c6b..a2686c0dbc563 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -528,8 +528,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); if let Some(&mut(first_arg, ref mut first_ty)) = first_arg { - self.memory.dump(first_arg.alloc_id); - println!("idx: {}", idx); let (_, vtable) = self.get_fat_ptr(first_arg); let vtable = self.memory.read_ptr(vtable)?; let idx = idx + 3; diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 76ec5f5f19430..800ded8177cb9 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -46,7 +46,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs, nested: _ }) => { let closure_type = self.tcx.closure_type(closure_def_id, substs); - vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() + let fn_ty = ty::BareFnTy { + unsafety: closure_type.unsafety, + abi: closure_type.abi, + sig: closure_type.sig, + }; + let fn_ty = self.tcx.mk_bare_fn(fn_ty); + unimplemented!() + //vec![Some(self.memory.create_fn_ptr(closure_def_id, substs.func_substs, fn_ty))].into_iter() } traits::VtableFnPointer( traits::VtableFnPointerData { diff --git a/tests/run-pass/traits.rs b/tests/run-pass/traits.rs index 5f9668d1303a8..acef02d2bc886 100644 --- a/tests/run-pass/traits.rs +++ b/tests/run-pass/traits.rs @@ -13,4 +13,14 @@ impl Trait for Struct { fn main() { let y: &Trait = &Struct(42); y.method(); + /* + let x: Box i32> = Box::new(|x| x * 2); + assert_eq!(x(21), 42); + let mut i = 5; + { + let mut x: Box = Box::new(|| i *= 2); + x(); x(); + } + assert_eq!(i, 20); + */ } From 3562118948c8414ff50ca717086edc04729749ab Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 10 Sep 2016 15:14:49 +0200 Subject: [PATCH 0473/1096] use canonical formatting of `pub (super)` --- src/interpreter/terminator.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index a2686c0dbc563..89a0f820ed6e8 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub (super) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + pub(super) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { @@ -575,14 +575,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } #[derive(Debug)] -pub (super) struct ImplMethod<'tcx> { - pub (super) method: Rc>, - pub (super) substs: &'tcx Substs<'tcx>, - pub (super) is_provided: bool, +pub(super) struct ImplMethod<'tcx> { + pub(super) method: Rc>, + pub(super) substs: &'tcx Substs<'tcx>, + pub(super) is_provided: bool, } /// Locates the applicable definition of a method, given its name. -pub (super) fn get_impl_method<'a, 'tcx>( +pub(super) fn get_impl_method<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: &'tcx Substs<'tcx>, impl_def_id: DefId, From 5ac138c61fd9acc86b42820970d98b2d56e5eebc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 10 Sep 2016 20:59:23 -0600 Subject: [PATCH 0474/1096] Update for changes in rustc. --- src/interpreter/mod.rs | 6 +++--- src/interpreter/vtable.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 370b1efc49914..b638ea564dcd0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -688,7 +688,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Handle the field index for the outer non-null variant. let inner_ty = match ty.sty { - ty::TyEnum(adt_def, substs) => { + ty::TyAdt(adt_def, substs) => { let variant = &adt_def.variants[nndiscr as usize]; let index = path.next().unwrap(); let field = &variant.fields[index]; @@ -715,7 +715,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match ty.sty { - ty::TyStruct(adt_def, substs) => { + ty::TyAdt(adt_def, substs) => { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } @@ -953,7 +953,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - (_, &ty::TyEnum(..)) => { + (_, &ty::TyAdt(..)) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty) { match (discr.size().bytes(), signed) { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 800ded8177cb9..154a9ee8c5a65 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -51,15 +51,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { abi: closure_type.abi, sig: closure_type.sig, }; - let fn_ty = self.tcx.mk_bare_fn(fn_ty); + let _fn_ty = self.tcx.mk_bare_fn(fn_ty); unimplemented!() //vec![Some(self.memory.create_fn_ptr(closure_def_id, substs.func_substs, fn_ty))].into_iter() } traits::VtableFnPointer( traits::VtableFnPointerData { - fn_ty: bare_fn_ty, + fn_ty: _bare_fn_ty, nested: _ }) => { - let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); + let _trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); //vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter() unimplemented!() } From 78bef956c9af7b98114229ce2925dd5b77a56675 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Sep 2016 16:57:09 +0200 Subject: [PATCH 0475/1096] don't load memory as mutable if not necessary --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 25ae6b3d995ba..38f47c9f0d848 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -433,7 +433,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; - let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); + let src_bytes = self.get_bytes_unchecked(src, size)?.as_ptr(); let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes From 0e58c2a31bc0c6e048d039e61f388b64c1b49793 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Sep 2016 17:11:43 +0200 Subject: [PATCH 0476/1096] document the fields of `Allocation` --- src/memory.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 38f47c9f0d848..625ec329c1b94 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -26,9 +26,15 @@ impl fmt::Display for AllocId { #[derive(Debug)] pub struct Allocation { + /// The actual bytes of the allocation. + /// Note that the bytes of a pointer represent the offset of the pointer pub bytes: Vec, + /// Maps from byte addresses to allocations. + /// Only the first byte of a pointer is inserted into the map. pub relocations: BTreeMap, + /// Denotes undefined memory. Reading from undefined memory is forbidden in miri pub undef_mask: UndefMask, + /// The alignment of the allocation to detect unaligned reads. pub align: usize, } From 5c47e3dbd893e22727058f9bf591f4450dfead21 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 9 Sep 2016 17:44:04 +0200 Subject: [PATCH 0477/1096] only allow the modification of `static mut` or statics with interior mutability --- src/error.rs | 3 ++ src/interpreter/mod.rs | 29 +++++++++++++---- src/interpreter/step.rs | 32 ++++++++++++++++--- src/interpreter/terminator.rs | 8 ++--- src/memory.rs | 13 ++++++++ .../static_memory_modification.rs | 9 ++++++ .../{bug.rs => static_memory_modification.rs} | 0 7 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 tests/compile-fail/static_memory_modification.rs rename tests/run-pass/{bug.rs => static_memory_modification.rs} (100%) diff --git a/src/error.rs b/src/error.rs index 7129fa0dfcd82..e9312a1ef3e64 100644 --- a/src/error.rs +++ b/src/error.rs @@ -42,6 +42,7 @@ pub enum EvalError<'tcx> { }, CalledClosureAsFunction, VtableForArgumentlessMethod, + ModifiedConstantMemory, } pub type EvalResult<'tcx, T> = Result>; @@ -94,6 +95,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to call a closure through a function pointer", EvalError::VtableForArgumentlessMethod => "tried to call a vtable function without arguments", + EvalError::ModifiedConstantMemory => + "tried to modify constant memory", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b638ea564dcd0..6ba465cb5990c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -15,7 +15,7 @@ use std::iter; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{Memory, Pointer}; +use memory::{Memory, Pointer, AllocId}; use primval::{self, PrimVal}; use std::collections::HashMap; @@ -74,7 +74,7 @@ pub struct Frame<'a, 'tcx: 'a> { pub return_ptr: Option, /// The block to return to when returning from the current stack frame - pub return_to_block: Option, + pub return_to_block: StackPopCleanup, /// The list of locals for the current function, stored in order as /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` @@ -139,6 +139,18 @@ enum ConstantKind { Global, } +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum StackPopCleanup { + /// The stackframe existed to compute the initial value of a static/constant, make sure the + /// static isn't modifyable afterwards + Freeze(AllocId), + /// A regular stackframe added due to a function call will need to get forwarded to the next + /// block + Goto(mir::BasicBlock), + /// The main function and diverging functions have nowhere to return to + None, +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self { EvalContext { @@ -313,7 +325,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option, - return_to_block: Option, + return_to_block: StackPopCleanup, ) -> EvalResult<'tcx, ()> { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let var_tys = mir.var_decls.iter().map(|v| v.ty); @@ -350,13 +362,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn pop_stack_frame(&mut self) { + fn pop_stack_frame(&mut self) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); - if let Some(target) = frame.return_to_block { - self.goto_block(target); + match frame.return_to_block { + StackPopCleanup::Freeze(alloc_id) => self.memory.freeze(alloc_id)?, + StackPopCleanup::Goto(target) => self.goto_block(target), + StackPopCleanup::None => {}, } // TODO(solson): Deallocate local variables. + Ok(()) } /// Applies the binary operation `op` to the two operands and writes a tuple of the result @@ -1036,7 +1051,7 @@ pub fn eval_main<'a, 'tcx: 'a>( let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr), None) + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr), StackPopCleanup::None) .expect("could not allocate first stack frame"); if mir.arg_decls.len() == 2 { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 3684f525ba632..2509aba090003 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -7,11 +7,13 @@ use super::{ ConstantId, EvalContext, ConstantKind, + StackPopCleanup, }; use error::EvalResult; use rustc::mir::repr as mir; use rustc::ty::{subst, self}; use rustc::hir::def_id::DefId; +use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; @@ -110,7 +112,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { } impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { - fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { + fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) { let cid = ConstantId { def_id: def_id, substs: substs, @@ -123,7 +125,12 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); - this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr), None) + let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { + StackPopCleanup::Freeze(ptr.alloc_id) + } else { + StackPopCleanup::None + }; + this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr), cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -150,7 +157,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { // because the type is the actual function, not the signature of the function. // Thus we can simply create a zero sized allocation in `evaluate_operand` } else { - self.global_item(def_id, substs, constant.span); + self.global_item(def_id, substs, constant.span, true); } }, mir::Literal::Promoted { index } => { @@ -168,7 +175,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); - this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr), None) + this.ecx.push_stack_frame(this.def_id, + constant.span, + mir, + this.substs, + Some(return_ptr), + StackPopCleanup::Freeze(return_ptr.alloc_id)) }); } } @@ -179,7 +191,17 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if let mir::Lvalue::Static(def_id) = *lvalue { let substs = subst::Substs::empty(self.ecx.tcx); let span = self.span; - self.global_item(def_id, substs, span); + if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = self.ecx.tcx.map.get_if_local(def_id).expect("static not found") { + if let hir::ItemStatic(_, m, _) = *node { + self.global_item(def_id, substs, span, m == hir::MutImmutable); + return; + } else { + bug!("static def id doesn't point to static"); + } + } else { + bug!("static def id doesn't point to item"); + } + self.global_item(def_id, substs, span, false); } } } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 89a0f820ed6e8..9984e1f14eaf1 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -10,7 +10,7 @@ use std::iter; use syntax::{ast, attr}; use syntax::codemap::{DUMMY_SP, Span}; -use super::{EvalContext, IntegerExt}; +use super::{EvalContext, IntegerExt, StackPopCleanup}; use error::{EvalError, EvalResult}; use memory::Pointer; @@ -27,7 +27,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { use rustc::mir::repr::TerminatorKind::*; match terminator.kind { - Return => self.pop_stack_frame(), + Return => self.pop_stack_frame()?, Goto { target } => self.goto_block(target), @@ -210,8 +210,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = self.load_mir(resolved_def_id); let (return_ptr, return_to_block) = match destination { - Some((ptr, block)) => (Some(ptr), Some(block)), - None => (None, None), + Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), + None => (None, StackPopCleanup::None), }; self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; diff --git a/src/memory.rs b/src/memory.rs index 625ec329c1b94..c02ea22c1d997 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -36,6 +36,10 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: usize, + /// Whether the allocation may be modified. + /// Use the `freeze` method of `Memory` to ensure that an error occurs, if the memory of this + /// allocation is modified in the future. + pub immutable: bool, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -117,6 +121,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), align: 1, + immutable: false, // must be mutable, because sometimes we "move out" of a ZST }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work @@ -185,6 +190,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align: align, + immutable: false, }; let id = self.next_id; self.next_id.0 += 1; @@ -293,6 +299,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { match self.alloc_map.get_mut(&id) { + Some(ref alloc) if alloc.immutable => Err(EvalError::ModifiedConstantMemory), Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), @@ -436,6 +443,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { + + pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { + self.get_mut(alloc_id)?.immutable = true; + Ok(()) + } + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; diff --git a/tests/compile-fail/static_memory_modification.rs b/tests/compile-fail/static_memory_modification.rs new file mode 100644 index 0000000000000..11961becb246a --- /dev/null +++ b/tests/compile-fail/static_memory_modification.rs @@ -0,0 +1,9 @@ +static X: usize = 5; + +#[allow(mutable_transmutes)] +fn main() { + unsafe { + *std::mem::transmute::<&usize, &mut usize>(&X) = 6; //~ ERROR: tried to modify constant memory + assert_eq!(X, 6); + } +} diff --git a/tests/run-pass/bug.rs b/tests/run-pass/static_memory_modification.rs similarity index 100% rename from tests/run-pass/bug.rs rename to tests/run-pass/static_memory_modification.rs From 3c5f595d45917152bbb8e5217ddef94930deb5b6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 10 Sep 2016 15:17:08 +0200 Subject: [PATCH 0478/1096] prevent the modification of vtables --- src/interpreter/vtable.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 154a9ee8c5a65..d4b347af0671e 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -96,6 +96,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + self.memory.freeze(vtable.alloc_id)?; + Ok(vtable) } From db7d842fb3b9ae51f1f4e155b5779503ca3a2bb4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 11 Sep 2016 03:06:44 -0600 Subject: [PATCH 0479/1096] Fix comment wording. --- src/interpreter/vtable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 154a9ee8c5a65..338dc8fdf7b10 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -9,8 +9,8 @@ use memory::Pointer; use super::terminator::{get_impl_method, ImplMethod}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Creates a returns a dynamic vtable for the given type and vtable origin. - /// This is used only for objects. + /// Creates a dynamic vtable for the given type and vtable origin. This is used only for + /// objects. /// /// The `trait_ref` encodes the erased self type. Hence if we are /// making an object `Foo` from a value of type `Foo`, then From 903bb97c17ebe49204a8118ee742f87e74c8dae9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 13 Sep 2016 13:03:42 +0200 Subject: [PATCH 0480/1096] needless references --- src/interpreter/step.rs | 2 +- src/interpreter/vtable.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 2509aba090003..7eaf92c9b5ace 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -30,7 +30,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = self.mir(); let basic_block = &mir.basic_blocks()[block]; - if let Some(ref stmt) = basic_block.statements.get(stmt_id) { + if let Some(stmt) = basic_block.statements.get(stmt_id) { let mut new = Ok(0); ConstantExtractor { span: stmt.source_info.span, diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index edcdccf346cf6..6a3de7aae808b 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -164,7 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // method could then never be called, so we do not want to // try and trans it, in that case. Issue #23435. if mth.is_provided { - let predicates = mth.method.predicates.predicates.subst(self.tcx, &mth.substs); + let predicates = mth.method.predicates.predicates.subst(self.tcx, mth.substs); if !self.normalize_and_test_predicates(predicates) { debug!("get_vtable_methods: predicates do not hold"); return None; From c57233abca7c2f89ea656bfc05e80d745b07369b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 13 Sep 2016 13:03:54 +0200 Subject: [PATCH 0481/1096] needless clones --- src/interpreter/vtable.rs | 4 ++-- src/memory.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 6a3de7aae808b..726c6ce3913a3 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -20,8 +20,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { debug!("get_vtable(trait_ref={:?})", trait_ref); - let methods: Vec<_> = traits::supertraits(tcx, trait_ref.clone()).flat_map(|trait_ref| { - match self.fulfill_obligation(trait_ref.clone()) { + let methods: Vec<_> = traits::supertraits(tcx, trait_ref).flat_map(|trait_ref| { + match self.fulfill_obligation(trait_ref) { // Should default trait error here? traits::VtableDefaultImpl(_) | traits::VtableBuiltin(_) => { diff --git a/src/memory.rs b/src/memory.rs index c02ea22c1d997..79b4b8b64d61a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -314,7 +314,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(&FunctionDefinition { def_id, kind: FunctionKind::Closure { ref substs, ref ty } - }) => Ok((def_id, substs.clone(), ty.clone())), + }) => Ok((def_id, *substs, ty.clone())), Some(&FunctionDefinition { kind: FunctionKind::Function { .. }, .. }) => Err(EvalError::CalledClosureAsFunction), From 23eb8a5cf2053f65d629e0ea6a65aa9560a2f708 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 13 Sep 2016 13:08:57 +0200 Subject: [PATCH 0482/1096] error on failed assumptions --- src/error.rs | 3 +++ src/interpreter/terminator.rs | 7 +++++-- tests/compile-fail/assume.rs | 10 ++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/compile-fail/assume.rs diff --git a/src/error.rs b/src/error.rs index e9312a1ef3e64..5624734e888ab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,6 +43,7 @@ pub enum EvalError<'tcx> { CalledClosureAsFunction, VtableForArgumentlessMethod, ModifiedConstantMemory, + AssumptionNotHeld, } pub type EvalResult<'tcx, T> = Result>; @@ -97,6 +98,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to call a vtable function without arguments", EvalError::ModifiedConstantMemory => "tried to modify constant memory", + EvalError::AssumptionNotHeld => + "`assume` argument was false" } } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 9984e1f14eaf1..23396e0694e1b 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -284,8 +284,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, - // FIXME: turn into an assertion to catch wrong `assume` that would cause UB in llvm - "assume" => {} + "assume" => { + if !self.memory.read_bool(args_ptrs[0])? { + return Err(EvalError::AssumptionNotHeld); + } + } "copy_nonoverlapping" => { let elem_ty = substs.type_at(0); diff --git a/tests/compile-fail/assume.rs b/tests/compile-fail/assume.rs new file mode 100644 index 0000000000000..69758a5d7fe8c --- /dev/null +++ b/tests/compile-fail/assume.rs @@ -0,0 +1,10 @@ +#![feature(core_intrinsics)] + +fn main() { + let x = 5; + unsafe { + std::intrinsics::assume(x < 10); + std::intrinsics::assume(x > 1); + std::intrinsics::assume(x > 42); //~ ERROR: `assume` argument was false + } +} From 366c793306609f4a80e7977be766cbc7e9c2b3be Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 13 Sep 2016 20:13:30 -0600 Subject: [PATCH 0483/1096] Fix tests broken by std::vec::SetLenOnDrop. --- .../static_memory_modification.rs | 9 ------- tests/run-pass/heap.rs | 27 +++++++++++-------- tests/run-pass/vecs.rs | 18 ++++++++++--- 3 files changed, 30 insertions(+), 24 deletions(-) delete mode 100644 tests/compile-fail/static_memory_modification.rs diff --git a/tests/compile-fail/static_memory_modification.rs b/tests/compile-fail/static_memory_modification.rs deleted file mode 100644 index 11961becb246a..0000000000000 --- a/tests/compile-fail/static_memory_modification.rs +++ /dev/null @@ -1,9 +0,0 @@ -static X: usize = 5; - -#[allow(mutable_transmutes)] -fn main() { - unsafe { - *std::mem::transmute::<&usize, &mut usize>(&X) = 6; //~ ERROR: tried to modify constant memory - assert_eq!(X, 6); - } -} diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index b533f91646988..4bf6a085e35fc 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,20 +11,25 @@ fn make_box_syntax() -> Box<(i16, i16)> { fn allocate_reallocate() { let mut s = String::new(); - // 6 byte heap alloc (__rust_allocate) - s.push_str("foobar"); - assert_eq!(s.len(), 6); - assert_eq!(s.capacity(), 6); + // 4 byte heap alloc (__rust_allocate) + s.push('f'); + assert_eq!(s.len(), 1); + assert_eq!(s.capacity(), 4); - // heap size doubled to 12 (__rust_reallocate) - s.push_str("baz"); - assert_eq!(s.len(), 9); - assert_eq!(s.capacity(), 12); + // heap size doubled to 8 (__rust_reallocate) + // FIXME: String::push_str is broken because it hits the std::vec::SetLenOnDrop code and we + // don't call destructors in miri yet. + s.push('o'); + s.push('o'); + s.push('o'); + s.push('o'); + assert_eq!(s.len(), 5); + assert_eq!(s.capacity(), 8); - // heap size reduced to 9 (__rust_reallocate) + // heap size reduced to 5 (__rust_reallocate) s.shrink_to_fit(); - assert_eq!(s.len(), 9); - assert_eq!(s.capacity(), 9); + assert_eq!(s.len(), 5); + assert_eq!(s.capacity(), 5); } fn main() { diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index b3a88014e6f9a..0ad7a371762bc 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -1,3 +1,13 @@ +// FIXME: The normal `vec!` macro is currently broken in Miri because it hits the +// std::vec::SetLenOnDrop code and Miri doesn't call destructors yet. +macro_rules! miri_vec { + ($($e:expr),*) => ({ + let mut v = Vec::new(); + $(v.push($e);)* + v + }); +} + fn make_vec() -> Vec { let mut v = Vec::with_capacity(4); v.push(1); @@ -6,22 +16,22 @@ fn make_vec() -> Vec { } fn make_vec_macro() -> Vec { - vec![1, 2] + miri_vec![1, 2] } fn make_vec_macro_repeat() -> Vec { - vec![42; 5] + miri_vec![42, 42, 42, 42, 42] } fn vec_into_iter() -> u8 { - vec![1, 2, 3, 4] + miri_vec![1, 2, 3, 4] .into_iter() .map(|x| x * x) .fold(0, |x, y| x + y) } fn vec_reallocate() -> Vec { - let mut v = vec![1, 2]; + let mut v = miri_vec![1, 2]; v.push(3); v.push(4); v.push(5); From 2e70fcdca8d8a7a8f584944d9320bee3a76c7bc2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 13 Sep 2016 20:17:52 -0600 Subject: [PATCH 0484/1096] Undo accidental test deletion in previous commit. --- tests/compile-fail/static_memory_modification.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/compile-fail/static_memory_modification.rs diff --git a/tests/compile-fail/static_memory_modification.rs b/tests/compile-fail/static_memory_modification.rs new file mode 100644 index 0000000000000..11961becb246a --- /dev/null +++ b/tests/compile-fail/static_memory_modification.rs @@ -0,0 +1,9 @@ +static X: usize = 5; + +#[allow(mutable_transmutes)] +fn main() { + unsafe { + *std::mem::transmute::<&usize, &mut usize>(&X) = 6; //~ ERROR: tried to modify constant memory + assert_eq!(X, 6); + } +} From f731766805a32eb2cb23766057271c842fdf1724 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 13 Sep 2016 21:31:12 -0600 Subject: [PATCH 0485/1096] Fix allocation of fn items by allowing ZST alignment to be 0. --- src/memory.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index 79b4b8b64d61a..0565cb8176988 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -173,10 +173,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { - assert!(align != 0); if size == 0 { return Ok(Pointer::zst_ptr()); } + assert!(align != 0); + if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { allocation_size: size, From 1b94e06a1af1b5248c5e3335c0877a659487daf6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:10:58 +0200 Subject: [PATCH 0486/1096] ppaux::parameterized $sometimes panics, let's catch that. --- src/interpreter/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6ba465cb5990c..7bc70b08cc76e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1093,12 +1093,20 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { use rustc::util::ppaux; use std::fmt; struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); + impl<'tcx> ::std::panic::UnwindSafe for Instance<'tcx> {} + impl<'tcx> ::std::panic::RefUnwindSafe for Instance<'tcx> {} impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) } } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + let inst = Instance(def_id, substs); + match ::std::panic::catch_unwind(|| { + format!("inside call to {}", inst) + }) { + Ok(msg) => err.span_note(span, &msg), + Err(_) => err.span_note(span, &format!("ppaux::parameterized failed: {:?}, {:?}", def_id, substs)), + }; } err.emit(); } From 00bd255fe0df118c9fb43426654a7d5deec1c237 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:11:57 +0200 Subject: [PATCH 0487/1096] add ctpop and ctlz intrinsic --- src/interpreter/terminator.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 23396e0694e1b..0b16dfc15bf21 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -300,6 +300,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } + "ctpop" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "ctlz" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + "discriminant_value" => { let ty = substs.type_at(0); let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; From eb594da409e3a6dfd68b99133064b2dc43c4d1ff Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:12:36 +0200 Subject: [PATCH 0488/1096] forbid warnings only in the actual run-pass tests, not in the miri-pass tests --- tests/compiletest.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index a401257c6ae7e..00c7e7c454bf5 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -22,6 +22,8 @@ fn run_pass() { let mut config = compiletest::default_config(); config.mode = "run-pass".parse().expect("Invalid mode"); config.src_base = PathBuf::from("tests/run-pass".to_string()); + config.target_rustcflags = Some("-Dwarnings".to_string()); + config.host_rustcflags = Some("-Dwarnings".to_string()); compiletest::run_tests(&config); } @@ -67,7 +69,6 @@ fn compile_test() { write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); let mut cmd = std::process::Command::new("target/debug/miri"); cmd.arg(path); - cmd.arg("-Dwarnings"); cmd.arg(format!("--target={}", target)); let libs = Path::new(&sysroot).join("lib"); let sysroot = libs.join("rustlib").join(&target).join("lib"); From 0d2a403a518704258211889e76088341d845ed09 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:13:54 +0200 Subject: [PATCH 0489/1096] run all tests found in folder given by MIRI_RUSTC_TEST env var --- tests/compiletest.rs | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 00c7e7c454bf5..bc112f4068546 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -57,7 +57,17 @@ fn compile_test() { compile_fail(&sysroot); run_pass(); for_all_targets(&sysroot, |target| { - for file in std::fs::read_dir("tests/run-pass").unwrap() { + let files = std::fs::read_dir("tests/run-pass").unwrap(); + let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + Box::new(files.chain(std::fs::read_dir(path).unwrap())) + } else { + Box::new(files) + }; + let mut mir_not_found = 0; + let mut crate_not_found = 0; + let mut success = 0; + let mut failed = 0; + for file in files { let file = file.unwrap(); let path = file.path(); @@ -76,20 +86,34 @@ fn compile_test() { cmd.env(compiletest::procsrv::dylib_env_var(), paths); match cmd.output() { - Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), + Ok(ref output) if output.status.success() => { + success += 1; + writeln!(stderr.lock(), "ok").unwrap() + }, Ok(output) => { - writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); - writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); - writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); - panic!("some tests failed"); + let output_err = std::str::from_utf8(&output.stderr).unwrap(); + if let Some(text) = output_err.splitn(2, "thread 'main' panicked at 'no mir for `").nth(1) { + mir_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); + } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { + crate_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); + } else { + failed += 1; + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); + writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + } } Err(e) => { writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); - panic!("some tests failed"); + panic!("failed to execute miri"); }, } } let stderr = std::io::stderr(); - writeln!(stderr.lock(), "").unwrap(); + writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); }); } From 092f9d52d15198af3baaecb038a4e4ba130bcbfb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:14:53 +0200 Subject: [PATCH 0490/1096] hackily fix calling function pointers through a Fn static dispatch --- src/interpreter/terminator.rs | 55 +++++++++++++++-------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 0b16dfc15bf21..f4c7097ea2539 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -182,29 +182,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Only trait methods can have a Self parameter. let (resolved_def_id, resolved_substs) = if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs, arg_srcs.get_mut(0))? + self.trait_method(trait_id, def_id, substs, &mut arg_srcs)? } else { (def_id, substs) }; - if fn_ty.abi == Abi::RustCall && !args.is_empty() { - arg_srcs.pop(); - let last_arg = args.last().unwrap(); - let last = self.eval_operand(last_arg)?; - let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); + if fn_ty.abi == Abi::RustCall { + if let Some((last, last_ty)) = arg_srcs.pop() { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + arg_srcs.push((src, ty)); + } } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } } @@ -509,7 +507,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx>, - first_arg: Option<&mut (Pointer, Ty<'tcx>)>, + args: &mut Vec<(Pointer, Ty<'tcx>)>, ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -528,23 +526,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableClosure(vtable_closure) => Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), - traits::VtableFnPointer(_fn_ty) => { - let _trait_closure_kind = self.tcx.lang_items.fn_trait_kind(trait_id).unwrap(); - unimplemented!() - // let llfn = trans_fn_pointer_shim(ccx, trait_closure_kind, fn_ty); - - // let method_ty = def_ty(tcx, def_id, substs); - // let fn_ptr_ty = match method_ty.sty { - // ty::TyFnDef(_, _, fty) => tcx.mk_ty(ty::TyFnPtr(fty)), - // _ => unreachable!("expected fn item type, found {}", - // method_ty) - // }; - // Callee::ptr(immediate_rvalue(llfn, fn_ptr_ty)) + traits::VtableFnPointer(fn_ty) => { + if let ty::TyFnDef(did, ref substs, _) = fn_ty.fn_ty.sty { + args.remove(0); + Ok((did, substs)) + } else { + bug!("VtableFnPointer did not contain a concrete function: {:?}", fn_ty) + } } traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); - if let Some(&mut(first_arg, ref mut first_ty)) = first_arg { + if let Some(&mut(first_arg, ref mut first_ty)) = args.get_mut(0) { let (_, vtable) = self.get_fat_ptr(first_arg); let vtable = self.memory.read_ptr(vtable)?; let idx = idx + 3; From 562c64d86aeea35890841d1c09a731ff13815f95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Sep 2016 16:32:30 +0200 Subject: [PATCH 0491/1096] add some sanity tests --- tests/run-pass/function_pointers.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index 2e75a5a3ea2a5..e7368004069f6 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -2,6 +2,10 @@ fn f() -> i32 { 42 } +fn g(i: i32) -> i32 { + i*42 +} + fn return_fn_ptr() -> fn() -> i32 { f } @@ -10,8 +14,22 @@ fn call_fn_ptr() -> i32 { return_fn_ptr()() } +fn indirect i32>(f: F) -> i32 { f() } +fn indirect_mut i32>(mut f: F) -> i32 { f() } +fn indirect_once i32>(f: F) -> i32 { f() } + +fn indirect2 i32>(f: F) -> i32 { f(10) } +fn indirect_mut2 i32>(mut f: F) -> i32 { f(10) } +fn indirect_once2 i32>(f: F) -> i32 { f(10) } + fn main() { assert_eq!(call_fn_ptr(), 42); + assert_eq!(indirect(f), 42); + assert_eq!(indirect_mut(f), 42); + assert_eq!(indirect_once(f), 42); + assert_eq!(indirect2(g), 420); + assert_eq!(indirect_mut2(g), 420); + assert_eq!(indirect_once2(g), 420); assert!(return_fn_ptr() == f); assert!(return_fn_ptr() as unsafe fn() -> i32 == f as fn() -> i32 as unsafe fn() -> i32); } From a670f43886accd56d33a93c2c6eba6e218d37f67 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 16 Sep 2016 10:23:04 +0200 Subject: [PATCH 0492/1096] proper binding naming --- src/interpreter/terminator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index f4c7097ea2539..4e9d0186b7cf8 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -526,8 +526,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableClosure(vtable_closure) => Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), - traits::VtableFnPointer(fn_ty) => { - if let ty::TyFnDef(did, ref substs, _) = fn_ty.fn_ty.sty { + traits::VtableFnPointer(vtable_fn_ptr) => { + if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); Ok((did, substs)) } else { From 31bbeb9eff7b11554a87f0647fa9e542cd77597a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 16 Sep 2016 10:28:43 +0200 Subject: [PATCH 0493/1096] fix binding renaming in previous commit --- src/interpreter/terminator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 4e9d0186b7cf8..d8edad79f1ebf 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -531,7 +531,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.remove(0); Ok((did, substs)) } else { - bug!("VtableFnPointer did not contain a concrete function: {:?}", fn_ty) + bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) } } From f3589d68354f42dc98e96aa47827405f364289dd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Sep 2016 14:50:56 -0600 Subject: [PATCH 0494/1096] Remove unused extern crate rustc_trans. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 46bca86e5c532..dba1b81b70f15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ extern crate rustc_borrowck; extern crate rustc_data_structures; extern crate rustc_mir; -extern crate rustc_trans; extern crate rustc_const_math; extern crate syntax; #[macro_use] extern crate log; From 814efe3b05c4b8849cd04a8c680f53dc3845aa69 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 02:16:51 -0600 Subject: [PATCH 0495/1096] option_eq test passes now since casts are implemented. --- tests/compile-fail/option_eq.rs | 5 ----- tests/run-pass/option_eq.rs | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 tests/compile-fail/option_eq.rs create mode 100644 tests/run-pass/option_eq.rs diff --git a/tests/compile-fail/option_eq.rs b/tests/compile-fail/option_eq.rs deleted file mode 100644 index 8315ae7af84a7..0000000000000 --- a/tests/compile-fail/option_eq.rs +++ /dev/null @@ -1,5 +0,0 @@ -//error-pattern: can't handle cast: tmp0 as isize (Misc) -// no-ignore-x86 ignore-x86_64 -fn main() { - assert_eq!(std::char::from_u32('x' as u32), Some('x')); -} diff --git a/tests/run-pass/option_eq.rs b/tests/run-pass/option_eq.rs new file mode 100644 index 0000000000000..e698f8767746c --- /dev/null +++ b/tests/run-pass/option_eq.rs @@ -0,0 +1,3 @@ +fn main() { + assert_eq!(std::char::from_u32('x' as u32), Some('x')); +} From 20ced4a720d30568ea1eff0d7ce43941e388df13 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 02:19:31 -0600 Subject: [PATCH 0496/1096] Replace const_to_ptr with const_to_value. This reduces the number of allocations Miri makes drastically. The `const_to_ptr` function was a lame hack that allocated for every since simple constant, and all of those are avoided now, except for one extra allocation each for string and bytestring literals which will be fixed in a followup commit. There are a number of hacks such as `eval_operand_to_ptr` left over from this commit, which will also be fixed in followup commits. --- src/interpreter/mod.rs | 228 ++++++++++++++++++++-------------- src/interpreter/step.rs | 2 +- src/interpreter/terminator.rs | 38 +++--- src/primval.rs | 9 ++ tests/compile-fail/oom2.rs | 7 ++ 5 files changed, 171 insertions(+), 113 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7bc70b08cc76e..3cae24f34d8c8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,4 +1,4 @@ -use rustc::middle::const_val; +use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; @@ -99,6 +99,12 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } +#[derive(Clone, Copy, Debug, PartialEq)] +enum Value { + Ptr(Pointer), + Prim(PrimVal), +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Lvalue { ptr: Pointer, @@ -182,45 +188,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - // TODO(solson): Try making const_to_primval instead. - fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { + fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; - macro_rules! i2p { - ($i:ident, $n:expr) => {{ - let ptr = self.memory.allocate($n, $n)?; - self.memory.write_int(ptr, $i as i64, $n)?; - Ok(ptr) - }} - } - match *const_val { - Float(ConstFloat::F32(f)) => { - let ptr = self.memory.allocate(4, 4)?; - self.memory.write_f32(ptr, f)?; - Ok(ptr) - }, - Float(ConstFloat::F64(f)) => { - let ptr = self.memory.allocate(8, 8)?; - self.memory.write_f64(ptr, f)?; - Ok(ptr) - }, - Float(ConstFloat::FInfer{..}) | - Integral(ConstInt::Infer(_)) | - Integral(ConstInt::InferSigned(_)) => bug!("uninferred constants only exist before typeck"), - Integral(ConstInt::I8(i)) => i2p!(i, 1), - Integral(ConstInt::U8(i)) => i2p!(i, 1), + + let primval = match *const_val { + Integral(ConstInt::I8(i)) => Value::Prim(PrimVal::I8(i)), + Integral(ConstInt::U8(i)) => Value::Prim(PrimVal::U8(i)), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => i2p!(i, 2), + Integral(ConstInt::I16(i)) => Value::Prim(PrimVal::I16(i)), Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => i2p!(i, 2), + Integral(ConstInt::U16(i)) => Value::Prim(PrimVal::U16(i)), Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => i2p!(i, 4), + Integral(ConstInt::I32(i)) => Value::Prim(PrimVal::I32(i)), Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => i2p!(i, 4), + Integral(ConstInt::U32(i)) => Value::Prim(PrimVal::U32(i)), Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => i2p!(i, 8), + Integral(ConstInt::I64(i)) => Value::Prim(PrimVal::I64(i)), Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => i2p!(i, 8), + Integral(ConstInt::U64(i)) => Value::Prim(PrimVal::U64(i)), + Float(ConstFloat::F32(f)) => Value::Prim(PrimVal::F32(f)), + Float(ConstFloat::F64(f)) => Value::Prim(PrimVal::F64(f)), + Bool(b) => Value::Prim(PrimVal::Bool(b)), + Char(c) => Value::Prim(PrimVal::Char(c)), + Str(ref s) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len(), 1)?; @@ -229,33 +220,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(extra, s.len() as u64)?; - Ok(ptr) + Value::Ptr(ptr) } + ByteStr(ref bs) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(bs.len(), 1)?; let ptr = self.memory.allocate(psize, psize)?; self.memory.write_bytes(static_ptr, bs)?; self.memory.write_ptr(ptr, static_ptr)?; - Ok(ptr) + Value::Ptr(ptr) } - Bool(b) => { - let ptr = self.memory.allocate(1, 1)?; - self.memory.write_bool(ptr, b)?; - Ok(ptr) - } - Char(c) => { - let ptr = self.memory.allocate(4, 4)?; - self.memory.write_uint(ptr, c as u64, 4)?; - Ok(ptr) - }, - Struct(_node_id) => unimplemented!(), - Tuple(_node_id) => unimplemented!(), - Function(_def_id) => unimplemented!(), - Array(_, _) => unimplemented!(), - Repeat(_, _) => unimplemented!(), - Dummy => unimplemented!(), - } + + Struct(_) => unimplemented!(), + Tuple(_) => unimplemented!(), + Function(_) => unimplemented!(), + Array(_, _) => unimplemented!(), + Repeat(_, _) => unimplemented!(), + Dummy => unimplemented!(), + + Float(ConstFloat::FInfer{..}) | + Integral(ConstInt::Infer(_)) | + Integral(ConstInt::InferSigned(_)) => + bug!("uninferred constants only exist before typeck"), + }; + + Ok(primval) } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { @@ -404,15 +394,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right: &mir::Operand<'tcx>, dest: Pointer, ) -> EvalResult<'tcx, bool> { - let left_ptr = self.eval_operand(left)?; - let left_ty = self.operand_ty(left); - let left_val = self.read_primval(left_ptr, left_ty)?; - - let right_ptr = self.eval_operand(right)?; - let right_ty = self.operand_ty(right); - let right_val = self.read_primval(right_ptr, right_ty)?; - - let (val, overflow) = primval::binary_op(op, left_val, right_val)?; + let left_primval = self.eval_operand_to_primval(left)?; + let right_primval = self.eval_operand_to_primval(right)?; + let (val, overflow) = primval::binary_op(op, left_primval, right_primval)?; self.memory.write_primval(dest, val)?; Ok(overflow) } @@ -424,17 +408,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { operands: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, ()> { for (offset, operand) in offsets.into_iter().zip(operands) { - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); let field_dest = dest.offset(offset as isize); - self.move_(src, field_dest, src_ty)?; + self.write_value(value, field_dest, value_ty)?; } Ok(()) } - fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) - -> EvalResult<'tcx, ()> - { + /// Evaluate an assignment statement. + /// + /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue + /// type writes its results directly into the memory specified by the lvalue. + fn eval_rvalue_into_lvalue( + &mut self, + rvalue: &mir::Rvalue<'tcx>, + lvalue: &mir::Lvalue<'tcx>, + ) -> EvalResult<'tcx, ()> { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty); @@ -442,8 +432,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { - let src = self.eval_operand(operand)?; - self.move_(src, dest, dest_ty)?; + let value = self.eval_operand(operand)?; + self.write_value(value, dest, dest_ty)?; } BinaryOp(bin_op, ref left, ref right) => { @@ -456,9 +446,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } UnaryOp(un_op, ref operand) => { - let ptr = self.eval_operand(operand)?; - let ty = self.operand_ty(operand); - let val = self.read_primval(ptr, ty)?; + let val = self.eval_operand_to_primval(operand)?; self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?; } @@ -499,9 +487,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); let operand = &operands[0]; - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); - self.move_(src, dest, src_ty)?; + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); + self.write_value(value, dest, value_ty)?; } else { assert_eq!(operands.len(), 0); self.memory.write_isize(dest, 0)?; @@ -549,15 +537,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Repeat(ref operand, _) => { - let (elem_size, elem_align, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n), + let (elem_ty, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (elem_ty, n), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - - let src = self.eval_operand(operand)?; + let elem_size = self.type_size(elem_ty); + let value = self.eval_operand(operand)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.memory.copy(src, elem_dest, elem_size, elem_align)?; + self.write_value(value, elem_dest, elem_ty)?; } } @@ -604,7 +592,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::CastKind::*; match kind { Unsize => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let src_ty = self.operand_ty(operand); let dest_ty = self.monomorphize(dest_ty, self.substs()); assert!(self.type_is_fat_ptr(dest_ty)); @@ -637,7 +625,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Misc => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let src_ty = self.operand_ty(operand); if self.type_is_fat_ptr(src_ty) { let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); @@ -671,7 +659,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let ptr = self.memory.read_ptr(src)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); @@ -761,36 +749,68 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { + // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can + // remove it as soon as PrimVal can represent fat pointers. + fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { + let value = self.eval_operand(op)?; + match value { + Value::Ptr(ptr) => Ok(ptr), + Value::Prim(primval) => { + let ty = self.operand_ty(op); + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + self.memory.write_primval(ptr, primval)?; + Ok(ptr) + } + } + } + + fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { + let value = self.eval_operand(op)?; + let ty = self.operand_ty(op); + self.value_to_primval(value, ty) + } + + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()), + Consume(ref lvalue) => Ok(Value::Ptr(self.eval_lvalue(lvalue)?.to_ptr())), + Constant(mir::Constant { ref literal, ty, .. }) => { - use rustc::mir::repr::Literal::*; - match *literal { - Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { def_id, substs } => { + use rustc::mir::repr::Literal; + let value = match *literal { + Literal::Value { ref value } => self.const_to_value(value)?, + + Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Ok(self.memory.allocate(0, 0)?) + Value::Ptr(self.memory.allocate(0, 0)?) } else { let cid = ConstantId { def_id: def_id, substs: substs, kind: ConstantKind::Global, }; - Ok(*self.statics.get(&cid).expect("static should have been cached (rvalue)")) + let static_ptr = *self.statics.get(&cid) + .expect("static should have been cached (rvalue)"); + Value::Ptr(static_ptr) } - }, - Promoted { index } => { + } + + Literal::Promoted { index } => { let cid = ConstantId { def_id: self.frame().def_id, substs: self.substs(), kind: ConstantKind::Promoted(index), }; - Ok(*self.statics.get(&cid).expect("a promoted constant hasn't been precomputed")) - }, - } + let static_ptr = *self.statics.get(&cid) + .expect("a promoted constant hasn't been precomputed"); + Value::Ptr(static_ptr) + } + }; + + Ok(value) } } } @@ -885,7 +905,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => bug!("indexing expected an array or slice, got {:?}", base_ty), }; - let n_ptr = self.eval_operand(operand)?; + let n_ptr = self.eval_operand_to_ptr(operand)?; let n = self.memory.read_usize(n_ptr)?; base.ptr.offset(n as isize * elem_size as isize) } @@ -920,6 +940,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + match value { + Value::Ptr(ptr) => self.read_primval(ptr, ty), + + // TODO(solson): Sanity-check the primval type against the input type. + Value::Prim(primval) => Ok(primval), + } + } + + fn write_value(&mut self, value: Value, dest: Pointer, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + match value { + Value::Ptr(ptr) => self.move_(ptr, dest, dest_ty), + Value::Prim(primval) => self.memory.write_primval(dest, primval), + } + } + pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match (self.memory.pointer_size(), &ty.sty) { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 7eaf92c9b5ace..6f8a297a6d8f8 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -77,7 +77,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::StatementKind::*; match stmt.kind { - Assign(ref lvalue, ref rvalue) => self.eval_assignment(lvalue, rvalue)?, + Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, SetDiscriminant { .. } => unimplemented!(), // Miri can safely ignore these. Only translation needs them. diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index d8edad79f1ebf..9914fcba4670d 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -1,18 +1,19 @@ use rustc::hir::def_id::DefId; +use rustc::middle::const_val::ConstVal; use rustc::mir::repr as mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use std::rc::Rc; use std::iter; -use syntax::{ast, attr}; +use std::rc::Rc; use syntax::codemap::{DUMMY_SP, Span}; +use syntax::{ast, attr}; -use super::{EvalContext, IntegerExt, StackPopCleanup}; use error::{EvalError, EvalResult}; use memory::Pointer; +use super::{EvalContext, IntegerExt, StackPopCleanup}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -32,8 +33,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { - let cond_ptr = self.eval_operand(cond)?; - let cond_val = self.memory.read_bool(cond_ptr)?; + let cond_val = self.eval_operand_to_primval(cond)? + .expect_bool("TerminatorKind::If condition constant was not a bool"); self.goto_block(if cond_val { then_target } else { else_target }); } @@ -54,9 +55,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, val_const) in values.iter().enumerate() { - let ptr = self.const_to_ptr(val_const)?; - let val = self.memory.read_uint(ptr, discr_size)?; + for (index, const_val) in values.iter().enumerate() { + let val = match const_val { + &ConstVal::Integral(i) => i.to_u64_unchecked(), + _ => bug!("TerminatorKind::SwitchInt branch constant was not an integer"), + }; if discr_val == val { target_block = targets[index]; break; @@ -88,7 +91,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let ptr = self.eval_operand(func)?; + let ptr = self.eval_operand_to_ptr(func)?; let fn_ptr = self.memory.read_ptr(ptr)?; let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { @@ -114,15 +117,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Assert { ref cond, expected, ref msg, target, .. } => { - let cond_ptr = self.eval_operand(cond)?; - if expected == self.memory.read_bool(cond_ptr)? { + let cond_val = self.eval_operand_to_primval(cond)? + .expect_bool("TerminatorKind::Assert condition constant was not a bool"); + if expected == cond_val { self.goto_block(target); } else { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.eval_operand(len).expect("can't eval len"); + let len = self.eval_operand_to_ptr(len).expect("can't eval len"); let len = self.memory.read_usize(len).expect("can't read len"); - let index = self.eval_operand(index).expect("can't eval index"); + let index = self.eval_operand_to_ptr(index).expect("can't eval index"); let index = self.memory.read_usize(index).expect("can't read index"); Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) }, @@ -174,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut arg_srcs = Vec::new(); for arg in args { - let src = self.eval_operand(arg)?; + let src = self.eval_operand_to_ptr(arg)?; let src_ty = self.operand_ty(arg); arg_srcs.push((src, src_ty)); } @@ -271,8 +275,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Pointer, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { + // TODO(solson): We can probably remove this _to_ptr easily. let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) + .map(|arg| self.eval_operand_to_ptr(arg)) .collect(); let args_ptrs = args_res?; let pointer_size = self.memory.pointer_size(); @@ -422,8 +427,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { None => name.as_str(), }; + // TODO(solson): We can probably remove this _to_ptr easily. let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) + .map(|arg| self.eval_operand_to_ptr(arg)) .collect(); let args = args_res?; diff --git a/src/primval.rs b/src/primval.rs index 6b24bf7530f85..267922204af6a 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -20,6 +20,15 @@ pub enum PrimVal { F32(f32), F64(f64), } +impl PrimVal { + pub fn expect_bool(self, error_msg: &str) -> bool { + match self { + PrimVal::Bool(b) => b, + _ => bug!("{}", error_msg), + } + } +} + /// returns the result of the operation and whether the operation overflowed pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::repr::BinOp::*; diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index d0344e4faeb3a..ac4b3a6674484 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -30,6 +30,13 @@ fn bar(i: i32) { //~|NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar } } From 6c306f22544f254108edc4d6dbf782f113a19ebb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 03:35:35 -0600 Subject: [PATCH 0497/1096] Rename Value variants and simplify ByteStr consts. The ByteStr change will make one less allocation for every byte string literal. --- src/interpreter/mod.rs | 70 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3cae24f34d8c8..de05140377b34 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -99,10 +99,14 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } +/// A `Value` represents a single self-contained Rust value. +/// +/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve +/// value held directly, outside of any allocation (`ByVal`). #[derive(Clone, Copy, Debug, PartialEq)] enum Value { - Ptr(Pointer), - Prim(PrimVal), + ByRef(Pointer), + ByVal(PrimVal), } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -193,24 +197,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; let primval = match *const_val { - Integral(ConstInt::I8(i)) => Value::Prim(PrimVal::I8(i)), - Integral(ConstInt::U8(i)) => Value::Prim(PrimVal::U8(i)), + Integral(ConstInt::I8(i)) => Value::ByVal(PrimVal::I8(i)), + Integral(ConstInt::U8(i)) => Value::ByVal(PrimVal::U8(i)), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => Value::Prim(PrimVal::I16(i)), + Integral(ConstInt::I16(i)) => Value::ByVal(PrimVal::I16(i)), Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => Value::Prim(PrimVal::U16(i)), + Integral(ConstInt::U16(i)) => Value::ByVal(PrimVal::U16(i)), Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => Value::Prim(PrimVal::I32(i)), + Integral(ConstInt::I32(i)) => Value::ByVal(PrimVal::I32(i)), Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => Value::Prim(PrimVal::U32(i)), + Integral(ConstInt::U32(i)) => Value::ByVal(PrimVal::U32(i)), Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => Value::Prim(PrimVal::I64(i)), + Integral(ConstInt::I64(i)) => Value::ByVal(PrimVal::I64(i)), Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => Value::Prim(PrimVal::U64(i)), - Float(ConstFloat::F32(f)) => Value::Prim(PrimVal::F32(f)), - Float(ConstFloat::F64(f)) => Value::Prim(PrimVal::F64(f)), - Bool(b) => Value::Prim(PrimVal::Bool(b)), - Char(c) => Value::Prim(PrimVal::Char(c)), + Integral(ConstInt::U64(i)) => Value::ByVal(PrimVal::U64(i)), + Float(ConstFloat::F32(f)) => Value::ByVal(PrimVal::F32(f)), + Float(ConstFloat::F64(f)) => Value::ByVal(PrimVal::F64(f)), + Bool(b) => Value::ByVal(PrimVal::Bool(b)), + Char(c) => Value::ByVal(PrimVal::Char(c)), Str(ref s) => { let psize = self.memory.pointer_size(); @@ -220,16 +224,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(extra, s.len() as u64)?; - Value::Ptr(ptr) + Value::ByRef(ptr) } ByteStr(ref bs) => { - let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(bs.len(), 1)?; - let ptr = self.memory.allocate(psize, psize)?; - self.memory.write_bytes(static_ptr, bs)?; - self.memory.write_ptr(ptr, static_ptr)?; - Value::Ptr(ptr) + let ptr = self.memory.allocate(bs.len(), 1)?; + self.memory.write_bytes(ptr, bs)?; + Value::ByVal(PrimVal::AbstractPtr(ptr)) } Struct(_) => unimplemented!(), @@ -754,8 +755,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { let value = self.eval_operand(op)?; match value { - Value::Ptr(ptr) => Ok(ptr), - Value::Prim(primval) => { + Value::ByRef(ptr) => Ok(ptr), + Value::ByVal(primval) => { let ty = self.operand_ty(op); let size = self.type_size(ty); let align = self.type_align(ty); @@ -775,7 +776,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(Value::Ptr(self.eval_lvalue(lvalue)?.to_ptr())), + Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; @@ -785,7 +786,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Value::Ptr(self.memory.allocate(0, 0)?) + Value::ByRef(self.memory.allocate(0, 0)?) } else { let cid = ConstantId { def_id: def_id, @@ -794,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let static_ptr = *self.statics.get(&cid) .expect("static should have been cached (rvalue)"); - Value::Ptr(static_ptr) + Value::ByRef(static_ptr) } } @@ -806,7 +807,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let static_ptr = *self.statics.get(&cid) .expect("a promoted constant hasn't been precomputed"); - Value::Ptr(static_ptr) + Value::ByRef(static_ptr) } }; @@ -942,17 +943,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match value { - Value::Ptr(ptr) => self.read_primval(ptr, ty), + Value::ByRef(ptr) => self.read_primval(ptr, ty), // TODO(solson): Sanity-check the primval type against the input type. - Value::Prim(primval) => Ok(primval), + Value::ByVal(primval) => Ok(primval), } } - fn write_value(&mut self, value: Value, dest: Pointer, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn write_value( + &mut self, + value: Value, + dest: Pointer, + dest_ty: Ty<'tcx> + ) -> EvalResult<'tcx, ()> { match value { - Value::Ptr(ptr) => self.move_(ptr, dest, dest_ty), - Value::Prim(primval) => self.memory.write_primval(dest, primval), + Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), + Value::ByVal(primval) => self.memory.write_primval(dest, primval), } } From 85cba42a7bb154361519aafc4a3ec5121eb6691c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 03:36:41 -0600 Subject: [PATCH 0498/1096] There will never be a PrimVal for fat pointers. Instead, there will be a `Value::ByValPair` variant for holding fat pointers (among other things) modelled after `OperandValue::Pair` in rustc's trans. --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index de05140377b34..0df25464bf0be 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1006,7 +1006,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(e) => return Err(e), } } else { - return Err(EvalError::Unimplemented(format!("unimplemented: primitive read of fat pointer type: {:?}", ty))); + bug!("primitive read of fat pointer type: {:?}", ty); } } From c679c71defc386fc1ea699d57630b07a001d0cc1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 04:10:18 -0600 Subject: [PATCH 0499/1096] Freeze static memory of string constants. --- src/interpreter/mod.rs | 10 ++++++++-- src/memory.rs | 7 +++++-- tests/compile-fail/static_memory_modification2.rs | 9 +++++++++ tests/compile-fail/static_memory_modification3.rs | 9 +++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/compile-fail/static_memory_modification2.rs create mode 100644 tests/compile-fail/static_memory_modification3.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0df25464bf0be..5664a3b9c08b5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -217,11 +217,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Char(c) => Value::ByVal(PrimVal::Char(c)), Str(ref s) => { - let psize = self.memory.pointer_size(); + // Create and freeze the allocation holding the characters. let static_ptr = self.memory.allocate(s.len(), 1)?; + self.memory.write_bytes(static_ptr, s.as_bytes())?; + self.memory.freeze(static_ptr.alloc_id)?; + + // Create an allocation to hold the fat pointer to the above char allocation. + // FIXME(solson): Introduce Value::ByValPair to remove this allocation. + let psize = self.memory.pointer_size(); let ptr = self.memory.allocate(psize * 2, psize)?; let (ptr, extra) = self.get_fat_ptr(ptr); - self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(extra, s.len() as u64)?; Value::ByRef(ptr) @@ -230,6 +235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len(), 1)?; self.memory.write_bytes(ptr, bs)?; + self.memory.freeze(ptr.alloc_id)?; Value::ByVal(PrimVal::AbstractPtr(ptr)) } diff --git a/src/memory.rs b/src/memory.rs index 0565cb8176988..ede76b4728e0e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -444,9 +444,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { - self.get_mut(alloc_id)?.immutable = true; + // Never freeze the zero-sized allocation. If you do that, then getting a mutable handle to + // _any_ ZST becomes an error, since they all share the same allocation. + if alloc_id != ZST_ALLOC_ID { + self.get_mut(alloc_id)?.immutable = true; + } Ok(()) } diff --git a/tests/compile-fail/static_memory_modification2.rs b/tests/compile-fail/static_memory_modification2.rs new file mode 100644 index 0000000000000..89d69cf7d7f4b --- /dev/null +++ b/tests/compile-fail/static_memory_modification2.rs @@ -0,0 +1,9 @@ +use std::mem::transmute; + +#[allow(mutable_transmutes)] +fn main() { + unsafe { + let s = "this is a test"; + transmute::<&[u8], &mut [u8]>(s.as_bytes())[4] = 42; //~ ERROR: tried to modify constant memory + } +} diff --git a/tests/compile-fail/static_memory_modification3.rs b/tests/compile-fail/static_memory_modification3.rs new file mode 100644 index 0000000000000..743fbe60efff6 --- /dev/null +++ b/tests/compile-fail/static_memory_modification3.rs @@ -0,0 +1,9 @@ +use std::mem::transmute; + +#[allow(mutable_transmutes)] +fn main() { + unsafe { + let bs = b"this is a test"; + transmute::<&[u8], &mut [u8]>(bs)[4] = 42; //~ ERROR: tried to modify constant memory + } +} From 678b9ca3285f08dd1ffb24cea0f2ec023023ffad Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 04:10:51 -0600 Subject: [PATCH 0500/1096] Print "(immutable)" when dumping allocations. --- src/memory.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index ede76b4728e0e..6042181cdf2e6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -382,7 +382,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { print!("__ "); } } - println!("({} bytes)", alloc.bytes.len()); + + let immutable = if alloc.immutable { " (immutable)" } else { "" }; + println!("({} bytes){}", alloc.bytes.len(), immutable); if !relocations.is_empty() { print!("{:1$}", "", prefix.len()); // Print spaces. From 6e5bdbe57703708e91cd37b707c75a20498ef4b8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 04:38:51 -0600 Subject: [PATCH 0501/1096] Add inital implementation of ByValPair. There are still hacks left to clean up. --- src/interpreter/mod.rs | 124 ++++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 39 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5664a3b9c08b5..391ca5bdf985d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -103,10 +103,15 @@ pub struct Frame<'a, 'tcx: 'a> { /// /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve /// value held directly, outside of any allocation (`ByVal`). +/// +/// For optimization of a few very common cases, there is also a representation for a pair of +/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for check binary +/// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug, PartialEq)] enum Value { ByRef(Pointer), ByVal(PrimVal), + ByValPair(PrimVal, PrimVal), } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -192,6 +197,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } + fn target_isize_primval(&self, n: i64) -> PrimVal { + match self.memory.pointer_size() { + 1 => PrimVal::I8(n as i8), + 2 => PrimVal::I16(n as i16), + 4 => PrimVal::I32(n as i32), + 8 => PrimVal::I64(n as i64), + p => bug!("unsupported target pointer size: {}", p), + } + } + + fn target_usize_primval(&self, n: u64) -> PrimVal { + match self.memory.pointer_size() { + 1 => PrimVal::U8(n as u8), + 2 => PrimVal::U16(n as u16), + 4 => PrimVal::U32(n as u32), + 8 => PrimVal::U64(n as u64), + p => bug!("unsupported target pointer size: {}", p), + } + } + fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; @@ -217,19 +242,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Char(c) => Value::ByVal(PrimVal::Char(c)), Str(ref s) => { - // Create and freeze the allocation holding the characters. - let static_ptr = self.memory.allocate(s.len(), 1)?; - self.memory.write_bytes(static_ptr, s.as_bytes())?; - self.memory.freeze(static_ptr.alloc_id)?; - - // Create an allocation to hold the fat pointer to the above char allocation. - // FIXME(solson): Introduce Value::ByValPair to remove this allocation. - let psize = self.memory.pointer_size(); - let ptr = self.memory.allocate(psize * 2, psize)?; - let (ptr, extra) = self.get_fat_ptr(ptr); - self.memory.write_ptr(ptr, static_ptr)?; - self.memory.write_usize(extra, s.len() as u64)?; - Value::ByRef(ptr) + let ptr = self.memory.allocate(s.len(), 1)?; + self.memory.write_bytes(ptr, s.as_bytes())?; + self.memory.freeze(ptr.alloc_id)?; + Value::ByValPair( + PrimVal::AbstractPtr(ptr), + self.target_usize_primval(s.len() as u64) + ) } ByteStr(ref bs) => { @@ -762,6 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(op)?; match value { Value::ByRef(ptr) => Ok(ptr), + Value::ByVal(primval) => { let ty = self.operand_ty(op); let size = self.type_size(ty); @@ -770,6 +790,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(ptr, primval)?; Ok(ptr) } + + Value::ByValPair(primval1, primval2) => { + let ty = self.operand_ty(op); + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + + // FIXME(solson): Major dangerous assumptions here. Ideally obliterate this + // function. + self.memory.write_primval(ptr, primval1)?; + self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?; + Ok(ptr) + } } } @@ -953,6 +986,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Sanity-check the primval type against the input type. Value::ByVal(primval) => Ok(primval), + Value::ByValPair(..) => bug!("can't turn a ByValPair into a single PrimVal"), } } @@ -965,44 +999,56 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), + Value::ByValPair(primval1, primval2) => { + let size = self.type_size(dest_ty); + + // FIXME(solson): Major dangerous assumptions here. + self.memory.write_primval(dest, primval1)?; + self.memory.write_primval(dest.offset((size / 2) as isize), primval2)?; + Ok(()) + } } } pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy, FloatTy}; - let val = match (self.memory.pointer_size(), &ty.sty) { - (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), - (_, &ty::TyChar) => { + let val = match &ty.sty { + &ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), + &ty::TyChar => { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::Char(ch), None => return Err(EvalError::InvalidChar(c as u64)), } } - (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), - (2, &ty::TyInt(IntTy::Is)) | - (_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), - (4, &ty::TyInt(IntTy::Is)) | - (_, &ty::TyInt(IntTy::I32)) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), - (8, &ty::TyInt(IntTy::Is)) | - (_, &ty::TyInt(IntTy::I64)) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), - (_, &ty::TyUint(UintTy::U8)) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), - (2, &ty::TyUint(UintTy::Us)) | - (_, &ty::TyUint(UintTy::U16)) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), - (4, &ty::TyUint(UintTy::Us)) | - (_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), - (8, &ty::TyUint(UintTy::Us)) | - (_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - - (_, &ty::TyFloat(FloatTy::F32)) => PrimVal::F32(self.memory.read_f32(ptr)?), - (_, &ty::TyFloat(FloatTy::F64)) => PrimVal::F64(self.memory.read_f64(ptr)?), - - (_, &ty::TyFnDef(def_id, substs, fn_ty)) => { + &ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), + &ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), + &ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), + &ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), + &ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), + &ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), + &ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), + &ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + + &ty::TyInt(IntTy::Is) => { + let psize = self.memory.pointer_size(); + self.target_isize_primval(self.memory.read_int(ptr, psize)?) + } + + &ty::TyUint(UintTy::Us) => { + let psize = self.memory.pointer_size(); + self.target_usize_primval(self.memory.read_uint(ptr, psize)?) + } + + &ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), + &ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), + + &ty::TyFnDef(def_id, substs, fn_ty) => { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, - (_, &ty::TyFnPtr(_)) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, - (_, &ty::TyRef(_, ty::TypeAndMut { ty, .. })) | - (_, &ty::TyRawPtr(ty::TypeAndMut { ty, .. })) => { + &ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, + &ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { if self.type_is_sized(ty) { match self.memory.read_ptr(ptr) { Ok(p) => PrimVal::AbstractPtr(p), @@ -1016,7 +1062,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - (_, &ty::TyAdt(..)) => { + &ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty) { match (discr.size().bytes(), signed) { From 689bccbed156a60fbbaa41d46db82bb69193584a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 04:49:21 -0600 Subject: [PATCH 0502/1096] Fix comment typo. --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 391ca5bdf985d..179c2fc608bfe 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -105,7 +105,7 @@ pub struct Frame<'a, 'tcx: 'a> { /// value held directly, outside of any allocation (`ByVal`). /// /// For optimization of a few very common cases, there is also a representation for a pair of -/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for check binary +/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug, PartialEq)] enum Value { From 63100401db156eb9cb001263cab65711de317d9b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 04:56:09 -0600 Subject: [PATCH 0503/1096] Simplify read_primval of {i,u}size. --- src/interpreter/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 179c2fc608bfe..b097acbfe3aa5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1021,6 +1021,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { None => return Err(EvalError::InvalidChar(c as u64)), } } + &ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), &ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), &ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), @@ -1030,15 +1031,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), &ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - &ty::TyInt(IntTy::Is) => { - let psize = self.memory.pointer_size(); - self.target_isize_primval(self.memory.read_int(ptr, psize)?) - } - - &ty::TyUint(UintTy::Us) => { - let psize = self.memory.pointer_size(); - self.target_usize_primval(self.memory.read_uint(ptr, psize)?) - } + &ty::TyInt(IntTy::Is) => self.target_isize_primval(self.memory.read_isize(ptr)?), + &ty::TyUint(UintTy::Us) => self.target_usize_primval(self.memory.read_usize(ptr)?), &ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), &ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), From c1ae916a64395755a937fa4374f74093b1549221 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 19:01:28 -0600 Subject: [PATCH 0504/1096] Remove a few instances of eval_operand_to_ptr. --- src/interpreter/mod.rs | 2 +- src/interpreter/terminator.rs | 19 +++++++++++-------- src/primval.rs | 22 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b097acbfe3aa5..f15755533034f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -107,7 +107,7 @@ pub struct Frame<'a, 'tcx: 'a> { /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug)] enum Value { ByRef(Pointer), ByVal(PrimVal), diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 9914fcba4670d..6636e2b854fe9 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -91,8 +91,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let ptr = self.eval_operand_to_ptr(func)?; - let fn_ptr = self.memory.read_ptr(ptr)?; + let fn_ptr = self.eval_operand_to_primval(func)? + .expect_fn_ptr("TyFnPtr callee did not evaluate to PrimVal::FnPtr"); let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); @@ -124,13 +124,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.eval_operand_to_ptr(len).expect("can't eval len"); - let len = self.memory.read_usize(len).expect("can't read len"); - let index = self.eval_operand_to_ptr(index).expect("can't eval index"); - let index = self.memory.read_usize(index).expect("can't read index"); - Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) + let span = terminator.source_info.span; + let len = self.eval_operand_to_primval(len).expect("can't eval len") + .expect_uint("BoundsCheck len wasn't a uint"); + let index = self.eval_operand_to_primval(index) + .expect("can't eval index") + .expect_uint("BoundsCheck index wasn't a uint"); + Err(EvalError::ArrayIndexOutOfBounds(span, len, index)) }, - mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())), + mir::AssertMessage::Math(ref err) => + Err(EvalError::Math(terminator.source_info.span, err.clone())), } } }, diff --git a/src/primval.rs b/src/primval.rs index 267922204af6a..d75b7879c375c 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -20,10 +20,28 @@ pub enum PrimVal { F32(f32), F64(f64), } +macro_rules! declare_expect_fn { + ($name:ident, $variant:ident, $t:ty) => ( + pub fn $name(self, error_msg: &str) -> $t { + match self { + PrimVal::$variant(x) => x, + _ => bug!("{}", error_msg), + } + } + ); +} + impl PrimVal { - pub fn expect_bool(self, error_msg: &str) -> bool { + declare_expect_fn!(expect_bool, Bool, bool); + declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer); + + pub fn expect_uint(self, error_msg: &str) -> u64 { + use self::PrimVal::*; match self { - PrimVal::Bool(b) => b, + U8(u) => u as u64, + U16(u) => u as u64, + U32(u) => u as u64, + U64(u) => u, _ => bug!("{}", error_msg), } } From 63cc7fc9e84c2332f2712c041e21bb748dfe9575 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 12:51:48 +0200 Subject: [PATCH 0505/1096] fix miri backtrace panic --- src/interpreter/mod.rs | 8 +------- src/interpreter/terminator.rs | 2 +- tests/run-pass/try-operator-custom.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 tests/run-pass/try-operator-custom.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f15755533034f..095b8c0c53403 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1188,13 +1188,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) } } - let inst = Instance(def_id, substs); - match ::std::panic::catch_unwind(|| { - format!("inside call to {}", inst) - }) { - Ok(msg) => err.span_note(span, &msg), - Err(_) => err.span_note(span, &format!("ppaux::parameterized failed: {:?}, {:?}", def_id, substs)), - }; + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); } err.emit(); } diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 6636e2b854fe9..851dc82adc50f 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -218,7 +218,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), None => (None, StackPopCleanup::None), }; - self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; + self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { let dest = self.frame().locals[i]; diff --git a/tests/run-pass/try-operator-custom.rs b/tests/run-pass/try-operator-custom.rs new file mode 100644 index 0000000000000..3b447f36ece1e --- /dev/null +++ b/tests/run-pass/try-operator-custom.rs @@ -0,0 +1,13 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert!(Ok::(42) == Ok(42)); +} From 1e0b3b207d5c337ed27a567f5cc27f69e7d86bb7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 12:52:01 +0200 Subject: [PATCH 0506/1096] prep for eddyb's find_method --- src/interpreter/terminator.rs | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 851dc82adc50f..478c0232c22ac 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -527,9 +527,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mname = self.tcx.item_name(def_id); // Create a concatenated set of substitutions which includes those from the impl // and those from the method: - let mth = get_impl_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); + let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - Ok((mth.method.def_id, mth.substs)) + Ok((did, substs)) } traits::VtableClosure(vtable_closure) => @@ -636,3 +636,35 @@ pub(super) fn get_impl_method<'a, 'tcx>( } } } + +/// Locates the applicable definition of a method, given its name. +pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + impl_def_id: DefId, + impl_substs: &'tcx Substs<'tcx>, + name: ast::Name) + -> (DefId, &'tcx Substs<'tcx>) +{ + assert!(!substs.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); + let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("find_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); + (node_item.item.def_id, substs) + } + None => { + bug!("method {:?} not found in {:?}", name, impl_def_id) + } + } +} From 477d1c20f46d122b4010c099834ff9b9938a4fd6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 13:34:53 +0200 Subject: [PATCH 0507/1096] fix enum variant downcasting --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 095b8c0c53403..9338044e53419 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -907,9 +907,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Downcast(_, variant) => { use rustc::ty::layout::Layout::*; match *base_layout { - General { discr, .. } => { + General { ref variants, .. } => { return Ok(Lvalue { - ptr: base.ptr.offset(discr.size().bytes() as isize), + ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), extra: LvalueExtra::DowncastVariant(variant), }); } From 8df6e7275ab3530eb5c35768a3a82c7bf7fef515 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 13:35:19 +0200 Subject: [PATCH 0508/1096] export `StackPopCleanup` (needed by priroda) --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index dba1b81b70f15..8598a8e7c5719 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub use interpreter::{ Frame, eval_main, run_mir_passes, + StackPopCleanup, }; pub use memory::{ From 4ab704c57df52569eaf6feec22a38d80d1b85bea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 16:05:30 +0200 Subject: [PATCH 0509/1096] implement more intrinsics --- src/interpreter/terminator/intrinsics.rs | 251 ++++++++++++++++++ .../{terminator.rs => terminator/mod.rs} | 148 +---------- 2 files changed, 253 insertions(+), 146 deletions(-) create mode 100644 src/interpreter/terminator/intrinsics.rs rename src/interpreter/{terminator.rs => terminator/mod.rs} (77%) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs new file mode 100644 index 0000000000000..e2381d59e2cd6 --- /dev/null +++ b/src/interpreter/terminator/intrinsics.rs @@ -0,0 +1,251 @@ +use rustc::hir::def_id::DefId; +use rustc::mir::repr as mir; +use rustc::ty::layout::Layout; +use rustc::ty::subst::Substs; +use rustc::ty; + +use error::{EvalError, EvalResult}; +use memory::Pointer; +use interpreter::EvalContext; +use primval; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(super) fn call_intrinsic( + &mut self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + args: &[mir::Operand<'tcx>], + dest: Pointer, + dest_layout: &'tcx Layout, + ) -> EvalResult<'tcx, ()> { + // TODO(solson): We can probably remove this _to_ptr easily. + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand_to_ptr(arg)) + .collect(); + let args_ptrs = args_res?; + let pointer_size = self.memory.pointer_size(); + + match &self.tcx.item_name(def_id).as_str()[..] { + "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, + "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, + "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, + + "arith_offset" => { + let ptr = self.memory.read_ptr(args_ptrs[0])?; + let offset = self.memory.read_int(args_ptrs[1], pointer_size)?; + let new_ptr = ptr.offset(offset as isize); + self.memory.write_ptr(dest, new_ptr)?; + } + + "assume" => { + if !self.memory.read_bool(args_ptrs[0])? { + return Err(EvalError::AssumptionNotHeld); + } + } + + "breakpoint" => unimplemented!(), // halt miri + + "copy" | + "copy_nonoverlapping" => { + // FIXME: check whether overlapping occurs + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let elem_align = self.type_align(elem_ty); + let src = self.memory.read_ptr(args_ptrs[0])?; + let dest = self.memory.read_ptr(args_ptrs[1])?; + let count = self.memory.read_isize(args_ptrs[2])?; + self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; + } + + "ctpop" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "ctlz" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "discriminant_value" => { + let ty = substs.type_at(0); + let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; + let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + self.memory.write_uint(dest, discr_val, 8)?; + } + + "fabsf32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + self.memory.write_f32(dest, f.abs())?; + } + + "fabsf64" => { + let f = self.memory.read_f64(args_ptrs[0])?; + self.memory.write_f64(dest, f.abs())?; + } + + "fadd_fast" => { + let ty = substs.type_at(0); + let a = self.read_primval(args_ptrs[0], ty)?; + let b = self.read_primval(args_ptrs[0], ty)?; + let result = primval::binary_op(mir::BinOp::Add, a, b)?; + self.memory.write_primval(dest, result.0)?; + } + + "likely" | + "unlikely" | + "forget" => {} + + "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, + + "min_align_of" => { + let elem_ty = substs.type_at(0); + let elem_align = self.type_align(elem_ty); + self.memory.write_uint(dest, elem_align as u64, pointer_size)?; + } + + "pref_align_of" => { + let ty = substs.type_at(0); + let layout = self.type_layout(ty); + let align = layout.align(&self.tcx.data_layout).pref(); + self.memory.write_uint(dest, align, pointer_size)?; + } + + "move_val_init" => { + let ty = substs.type_at(0); + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], ptr, ty)?; + } + + "needs_drop" => { + let ty = substs.type_at(0); + self.memory.write_bool(dest, self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()))?; + } + + "offset" => { + let pointee_ty = substs.type_at(0); + let pointee_size = self.type_size(pointee_ty) as isize; + let ptr_arg = args_ptrs[0]; + let offset = self.memory.read_isize(args_ptrs[1])?; + + match self.memory.read_ptr(ptr_arg) { + Ok(ptr) => { + let result_ptr = ptr.offset(offset as isize * pointee_size); + self.memory.write_ptr(dest, result_ptr)?; + } + Err(EvalError::ReadBytesAsPointer) => { + let addr = self.memory.read_isize(ptr_arg)?; + let result_addr = addr + offset * pointee_size as i64; + self.memory.write_isize(dest, result_addr)?; + } + Err(e) => return Err(e), + } + } + + "overflowing_sub" => { + self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; + } + + "overflowing_mul" => { + self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; + } + + "overflowing_add" => { + self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; + } + + "powif32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + let i = self.memory.read_int(args_ptrs[1], 4)?; + self.memory.write_f32(dest, f.powi(i as i32))?; + } + + "powif64" => { + let f = self.memory.read_f32(args_ptrs[0])?; + let i = self.memory.read_int(args_ptrs[1], 4)?; + self.memory.write_f32(dest, f.powi(i as i32))?; + } + + "sqrtf32" => { + let f = self.memory.read_f32(args_ptrs[0])?; + self.memory.write_f32(dest, f.sqrt())?; + } + + "sqrtf64" => { + let f = self.memory.read_f64(args_ptrs[0])?; + self.memory.write_f64(dest, f.sqrt())?; + } + + "size_of" => { + let ty = substs.type_at(0); + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } + + "size_of_val" => { + let ty = substs.type_at(0); + if self.type_is_sized(ty) { + let size = self.type_size(ty) as u64; + self.memory.write_uint(dest, size, pointer_size)?; + } else { + match ty.sty { + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty) as u64; + let ptr_size = self.memory.pointer_size() as isize; + let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; + self.memory.write_uint(dest, n * elem_size, pointer_size)?; + } + + _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), + } + } + } + // FIXME: wait for eval_operand_to_ptr to be gone + /* + "type_name" => { + let ty = substs.type_at(0); + let ty_name = ty.to_string(); + let s = self.str_to_value(&ty_name)?; + self.memory.write_ptr(dest, s)?; + }*/ + "type_id" => { + let ty = substs.type_at(0); + let n = self.tcx.type_id_hash(ty); + self.memory.write_uint(dest, n, 8)?; + } + + "transmute" => { + let ty = substs.type_at(0); + self.move_(args_ptrs[0], dest, ty)?; + } + + "try" => unimplemented!(), + + "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, + + "volatile_load" => { + let ty = substs.type_at(0); + let ptr = self.memory.read_ptr(args_ptrs[0])?; + self.move_(ptr, dest, ty)?; + } + + "volatile_store" => { + let ty = substs.type_at(0); + let dest = self.memory.read_ptr(args_ptrs[0])?; + self.move_(args_ptrs[1], dest, ty)?; + } + + name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), + } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + Ok(()) + } +} diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator/mod.rs similarity index 77% rename from src/interpreter/terminator.rs rename to src/interpreter/terminator/mod.rs index 478c0232c22ac..600bccfa404cb 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator/mod.rs @@ -15,6 +15,8 @@ use error::{EvalError, EvalResult}; use memory::Pointer; use super::{EvalContext, IntegerExt, StackPopCleanup}; +mod intrinsics; + impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { @@ -270,152 +272,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(if not_null { nndiscr } else { 1 - nndiscr }) } - fn call_intrinsic( - &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: &[mir::Operand<'tcx>], - dest: Pointer, - dest_layout: &'tcx Layout, - ) -> EvalResult<'tcx, ()> { - // TODO(solson): We can probably remove this _to_ptr easily. - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand_to_ptr(arg)) - .collect(); - let args_ptrs = args_res?; - let pointer_size = self.memory.pointer_size(); - - match &self.tcx.item_name(def_id).as_str()[..] { - "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, - "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, - "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, - - "assume" => { - if !self.memory.read_bool(args_ptrs[0])? { - return Err(EvalError::AssumptionNotHeld); - } - } - - "copy_nonoverlapping" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let elem_align = self.type_align(elem_ty); - let src = self.memory.read_ptr(args_ptrs[0])?; - let dest = self.memory.read_ptr(args_ptrs[1])?; - let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; - } - - "ctpop" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); - self.memory.write_uint(dest, num.into(), elem_size)?; - } - - "ctlz" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); - self.memory.write_uint(dest, num.into(), elem_size)?; - } - - "discriminant_value" => { - let ty = substs.type_at(0); - let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; - let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.memory.write_uint(dest, discr_val, 8)?; - } - - "forget" => {} - - "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, - - "min_align_of" => { - let elem_ty = substs.type_at(0); - let elem_align = self.type_align(elem_ty); - self.memory.write_uint(dest, elem_align as u64, pointer_size)?; - } - - "move_val_init" => { - let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], ptr, ty)?; - } - - "offset" => { - let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args_ptrs[0]; - let offset = self.memory.read_isize(args_ptrs[1])?; - - match self.memory.read_ptr(ptr_arg) { - Ok(ptr) => { - let result_ptr = ptr.offset(offset as isize * pointee_size); - self.memory.write_ptr(dest, result_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let addr = self.memory.read_isize(ptr_arg)?; - let result_addr = addr + offset * pointee_size as i64; - self.memory.write_isize(dest, result_addr)?; - } - Err(e) => return Err(e), - } - } - - "overflowing_sub" => { - self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; - } - - "overflowing_mul" => { - self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; - } - - "overflowing_add" => { - self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; - } - - "size_of" => { - let ty = substs.type_at(0); - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } - - "size_of_val" => { - let ty = substs.type_at(0); - if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } else { - match ty.sty { - ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size() as isize; - let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; - self.memory.write_uint(dest, n * elem_size, pointer_size)?; - } - - _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), - } - } - } - - "transmute" => { - let ty = substs.type_at(0); - self.move_(args_ptrs[0], dest, ty)?; - } - "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, - - name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), - } - - // Since we pushed no stack frame, the main loop will act - // as if the call just completed and it's returning to the - // current frame. - Ok(()) - } - fn call_c_abi( &mut self, def_id: DefId, From 16f6ae39336b61315b743d0b1721ecd0491fe402 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Sep 2016 18:31:55 +0200 Subject: [PATCH 0510/1096] fix calling Fn closures as FnOnce closures --- src/interpreter/terminator/mod.rs | 39 ++++++++++++++++++++++++++++--- tests/run-pass/closures.rs | 30 ++++++++++++++++-------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 600bccfa404cb..df1277d7a212a 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Trait method, which has to be resolved to an impl method. fn trait_method( - &self, + &mut self, trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx>, @@ -388,8 +388,41 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((did, substs)) } - traits::VtableClosure(vtable_closure) => - Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)), + traits::VtableClosure(vtable_closure) => { + let trait_closure_kind = self.tcx + .lang_items + .fn_trait_kind(trait_id) + .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); + let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); + trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); + match (closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. + // We want a `fn(self, ...)`. + // We can produce this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + + // interpreter magic: insert an intermediate pointer, so we can skip the intermediate function call + // FIXME: this is a memory leak, should probably add the pointer to the current stack + let ptr_size = self.memory.pointer_size(); + let first = self.memory.allocate(ptr_size, ptr_size)?; + self.memory.copy(args[0].0, first, ptr_size, ptr_size)?; + self.memory.write_ptr(args[0].0, first)?; + self.memory.dump(args[0].0.alloc_id); + } + _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), + } + Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)) + } traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { diff --git a/tests/run-pass/closures.rs b/tests/run-pass/closures.rs index e0b2c24c83574..9b379051eb774 100644 --- a/tests/run-pass/closures.rs +++ b/tests/run-pass/closures.rs @@ -21,18 +21,28 @@ fn crazy_closure() -> (i32, i32, i32) { inner(10) } -// TODO(solson): Implement closure argument adjustment and uncomment this test. -// fn closure_arg_adjustment_problem() -> i64 { -// fn once(f: F) { f(2); } -// let mut y = 1; -// { -// let f = |x| y += x; -// once(f); -// } -// y -// } +fn closure_arg_adjustment_problem() -> i64 { + fn once(f: F) { f(2); } + let mut y = 1; + { + let f = |x| y += x; + once(f); + } + y +} + +fn fn_once_closure_with_multiple_args() -> i64 { + fn once i64>(f: F) -> i64 { f(2, 3) } + let y = 1; + { + let f = |x, z| x + y + z; + once(f) + } +} fn main() { assert_eq!(simple(), 12); assert_eq!(crazy_closure(), (84, 10, 10)); + assert_eq!(closure_arg_adjustment_problem(), 3); + assert_eq!(fn_once_closure_with_multiple_args(), 6); } From 75ccfd57a508807af636a399a6fae5da1894134f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Sep 2016 11:03:41 +0200 Subject: [PATCH 0511/1096] remove leftover debug print --- src/interpreter/terminator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index df1277d7a212a..ae7eaf2c8b660 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -417,7 +417,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let first = self.memory.allocate(ptr_size, ptr_size)?; self.memory.copy(args[0].0, first, ptr_size, ptr_size)?; self.memory.write_ptr(args[0].0, first)?; - self.memory.dump(args[0].0.alloc_id); } _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), } From 21e924975d8236572d3abcf8e0b997138389c37f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Sep 2016 15:57:13 +0200 Subject: [PATCH 0512/1096] only split the Fn* arguments in case of closures and function pointers --- src/interpreter/terminator/mod.rs | 43 +++++++++++------------ tests/run-pass/function_pointers.rs | 11 ++++++ tests/run-pass/overloaded-calls-simple.rs | 33 +++++++++++++++++ 3 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 tests/run-pass/overloaded-calls-simple.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index ae7eaf2c8b660..8f0375936ccfa 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -178,9 +178,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Abi::Rust | Abi::RustCall => { - // TODO(solson): Adjust the first argument when calling a Fn or - // FnMut closure via FnOnce::call_once. - let mut arg_srcs = Vec::new(); for arg in args { let src = self.eval_operand_to_ptr(arg)?; @@ -196,25 +193,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs) }; - if fn_ty.abi == Abi::RustCall { - if let Some((last, last_ty)) = arg_srcs.pop() { - let last_layout = self.type_layout(last_ty); - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), - &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); - for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - arg_srcs.push((src, ty)); - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - } - let mir = self.load_mir(resolved_def_id); let (return_ptr, return_to_block) = match destination { Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), @@ -366,6 +344,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } + fn unpack_fn_args(&self, args: &mut Vec<(Pointer, Ty<'tcx>)>) { + if let Some((last, last_ty)) = args.pop() { + let last_layout = self.type_layout(last_ty); + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields), + &Layout::Univariant { ref variant, .. }) => { + let offsets = iter::once(0) + .chain(variant.offset_after_field.iter() + .map(|s| s.bytes())); + for (offset, ty) in offsets.zip(fields) { + let src = last.offset(offset as isize); + args.push((src, ty)); + } + } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + } + /// Trait method, which has to be resolved to an impl method. fn trait_method( &mut self, @@ -395,6 +392,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); + self.unpack_fn_args(args); match (closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | @@ -426,6 +424,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); + self.unpack_fn_args(args); Ok((did, substs)) } else { bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) diff --git a/tests/run-pass/function_pointers.rs b/tests/run-pass/function_pointers.rs index e7368004069f6..4f597d4a2e94f 100644 --- a/tests/run-pass/function_pointers.rs +++ b/tests/run-pass/function_pointers.rs @@ -6,6 +6,10 @@ fn g(i: i32) -> i32 { i*42 } +fn h(i: i32, j: i32) -> i32 { + j * i * 7 +} + fn return_fn_ptr() -> fn() -> i32 { f } @@ -22,6 +26,10 @@ fn indirect2 i32>(f: F) -> i32 { f(10) } fn indirect_mut2 i32>(mut f: F) -> i32 { f(10) } fn indirect_once2 i32>(f: F) -> i32 { f(10) } +fn indirect3 i32>(f: F) -> i32 { f(10, 3) } +fn indirect_mut3 i32>(mut f: F) -> i32 { f(10, 3) } +fn indirect_once3 i32>(f: F) -> i32 { f(10, 3) } + fn main() { assert_eq!(call_fn_ptr(), 42); assert_eq!(indirect(f), 42); @@ -30,6 +38,9 @@ fn main() { assert_eq!(indirect2(g), 420); assert_eq!(indirect_mut2(g), 420); assert_eq!(indirect_once2(g), 420); + assert_eq!(indirect3(h), 210); + assert_eq!(indirect_mut3(h), 210); + assert_eq!(indirect_once3(h), 210); assert!(return_fn_ptr() == f); assert!(return_fn_ptr() as unsafe fn() -> i32 == f as fn() -> i32 as unsafe fn() -> i32); } diff --git a/tests/run-pass/overloaded-calls-simple.rs b/tests/run-pass/overloaded-calls-simple.rs new file mode 100644 index 0000000000000..1eeda12ca06f8 --- /dev/null +++ b/tests/run-pass/overloaded-calls-simple.rs @@ -0,0 +1,33 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![feature(lang_items, unboxed_closures, fn_traits)] + +struct S3 { + x: i32, + y: i32, +} + +impl FnOnce<(i32,i32)> for S3 { + type Output = i32; + extern "rust-call" fn call_once(self, (z,zz): (i32,i32)) -> i32 { + self.x * self.y * z * zz + } +} + +fn main() { + let s = S3 { + x: 3, + y: 3, + }; + let ans = s(3, 1); + assert_eq!(ans, 27); +} From 145cbf844cc876b05e29a0e08aa1d7a461d00ae9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Sep 2016 18:00:04 +0200 Subject: [PATCH 0513/1096] enable A -> A downcasting --- src/interpreter/mod.rs | 16 ++++++++++++++-- tests/run-pass/traits.rs | 5 +++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9338044e53419..60f5a75d46c63 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -627,6 +627,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); + // A -> A conversion + let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(src_pointee_ty, dest_pointee_ty); + match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { self.memory.write_usize(extra, length as u64)?; @@ -881,7 +884,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::ProjectionElem::*; match proj.elem { - Field(field, _) => { + Field(field, field_ty) => { use rustc::ty::layout::Layout::*; let variant = match *base_layout { Univariant { ref variant, .. } => variant, @@ -901,7 +904,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let offset = variant.field_offset(field.index()).bytes(); - base.ptr.offset(offset as isize) + let ptr = base.ptr.offset(offset as isize); + match (&field_ty.sty, base.extra) { + (&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue { + ptr: ptr, + extra: extra, + }), + (&ty::TyTrait(_), _) => bug!("trait field without vtable"), + _ => ptr, + } }, Downcast(_, variant) => { @@ -922,6 +933,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); + let pointee_ty = self.tcx.struct_tail(pointee_ty); let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { diff --git a/tests/run-pass/traits.rs b/tests/run-pass/traits.rs index acef02d2bc886..e3d93957fd96b 100644 --- a/tests/run-pass/traits.rs +++ b/tests/run-pass/traits.rs @@ -10,9 +10,14 @@ impl Trait for Struct { } } +struct Foo(T); + fn main() { let y: &Trait = &Struct(42); y.method(); + let x: Foo = Foo(Struct(42)); + let y: &Foo = &x; + y.0.method(); /* let x: Box i32> = Box::new(|x| x * 2); assert_eq!(x(21), 42); From 89b9b3536eab99a673ae42ef71fc0c6a5f1ff1ae Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 19 Sep 2016 19:40:56 -0600 Subject: [PATCH 0514/1096] Remove more eval_operand_to_ptr. --- src/interpreter/mod.rs | 55 +++++++++++++++------------- src/interpreter/terminator/mod.rs | 59 ++++++++++++++++++------------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9338044e53419..f7d6b464cda3c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -779,31 +779,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // remove it as soon as PrimVal can represent fat pointers. fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { let value = self.eval_operand(op)?; - match value { - Value::ByRef(ptr) => Ok(ptr), - - Value::ByVal(primval) => { - let ty = self.operand_ty(op); - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - self.memory.write_primval(ptr, primval)?; - Ok(ptr) - } - - Value::ByValPair(primval1, primval2) => { - let ty = self.operand_ty(op); - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - - // FIXME(solson): Major dangerous assumptions here. Ideally obliterate this - // function. - self.memory.write_primval(ptr, primval1)?; - self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?; - Ok(ptr) - } - } + let ty = self.operand_ty(op); + self.value_to_ptr(value, ty) } fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { @@ -980,6 +957,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can + // remove it as soon as PrimVal can represent fat pointers. + fn value_to_ptr(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { + match value { + Value::ByRef(ptr) => Ok(ptr), + + Value::ByVal(primval) => { + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + self.memory.write_primval(ptr, primval)?; + Ok(ptr) + } + + Value::ByValPair(primval1, primval2) => { + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + + // FIXME(solson): Major dangerous assumptions here. Ideally obliterate this + // function. + self.memory.write_primval(ptr, primval1)?; + self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?; + Ok(ptr) + } + } + } + fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match value { Value::ByRef(ptr) => self.read_primval(ptr, ty), diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 8f0375936ccfa..b8d46323bfa87 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -13,7 +13,8 @@ use syntax::{ast, attr}; use error::{EvalError, EvalResult}; use memory::Pointer; -use super::{EvalContext, IntegerExt, StackPopCleanup}; +use primval::PrimVal; +use super::{EvalContext, IntegerExt, StackPopCleanup, Value}; mod intrinsics; @@ -154,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy, destination: Option<(Pointer, mir::BasicBlock)>, - args: &[mir::Operand<'tcx>], + arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx, ()> { use syntax::abi::Abi; @@ -163,7 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); let (ret, target) = destination.unwrap(); - self.call_intrinsic(def_id, substs, args, ret, layout)?; + self.call_intrinsic(def_id, substs, arg_operands, ret, layout)?; self.goto_block(target); Ok(()) } @@ -172,23 +173,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output; let size = self.type_size(ty); let (ret, target) = destination.unwrap(); - self.call_c_abi(def_id, args, ret, size)?; + self.call_c_abi(def_id, arg_operands, ret, size)?; self.goto_block(target); Ok(()) } Abi::Rust | Abi::RustCall => { - let mut arg_srcs = Vec::new(); - for arg in args { - let src = self.eval_operand_to_ptr(arg)?; - let src_ty = self.operand_ty(arg); - arg_srcs.push((src, src_ty)); + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); } // Only trait methods can have a Self parameter. let (resolved_def_id, resolved_substs) = if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs, &mut arg_srcs)? + self.trait_method(trait_id, def_id, substs, &mut args)? } else { (def_id, substs) }; @@ -200,9 +201,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; - for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() { + for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { let dest = self.frame().locals[i]; - self.move_(src, dest, src_ty)?; + self.write_value(arg_val, dest, arg_ty)?; } Ok(()) @@ -344,7 +345,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } - fn unpack_fn_args(&self, args: &mut Vec<(Pointer, Ty<'tcx>)>) { + fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) { if let Some((last, last_ty)) = args.pop() { let last_layout = self.type_layout(last_ty); match (&last_ty.sty, last_layout) { @@ -353,9 +354,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offsets = iter::once(0) .chain(variant.offset_after_field.iter() .map(|s| s.bytes())); + let last_ptr = match last { + Value::ByRef(ptr) => ptr, + _ => bug!("rust-call ABI tuple argument wasn't Value::ByRef"), + }; for (offset, ty) in offsets.zip(fields) { - let src = last.offset(offset as isize); - args.push((src, ty)); + let arg = Value::ByRef(last_ptr.offset(offset as isize)); + args.push((arg, ty)); } } ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), @@ -369,7 +374,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trait_id: DefId, def_id: DefId, substs: &'tcx Substs<'tcx>, - args: &mut Vec<(Pointer, Ty<'tcx>)>, + args: &mut Vec<(Value, Ty<'tcx>)>, ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -398,6 +403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. @@ -409,13 +415,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // These are both the same at trans time. - // interpreter magic: insert an intermediate pointer, so we can skip the intermediate function call - // FIXME: this is a memory leak, should probably add the pointer to the current stack - let ptr_size = self.memory.pointer_size(); - let first = self.memory.allocate(ptr_size, ptr_size)?; - self.memory.copy(args[0].0, first, ptr_size, ptr_size)?; - self.memory.write_ptr(args[0].0, first)?; + // Interpreter magic: insert an intermediate pointer, so we can skip the + // intermediate function call. + // FIXME: this is a memory leak, should probably add the pointer to the + // current stack. + let first = self.value_to_ptr(args[0].0, args[0].1)?; + args[0].0 = Value::ByVal(PrimVal::AbstractPtr(first)); + args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } + _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), } Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)) @@ -433,8 +441,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); - if let Some(&mut(first_arg, ref mut first_ty)) = args.get_mut(0) { - let (_, vtable) = self.get_fat_ptr(first_arg); + if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { + // FIXME(solson): Remove this allocating hack. + let ptr = self.value_to_ptr(*first_arg, *first_ty)?; + *first_arg = Value::ByRef(ptr); + let (_, vtable) = self.get_fat_ptr(ptr); let vtable = self.memory.read_ptr(vtable)?; let idx = idx + 3; let offset = idx * self.memory.pointer_size(); From 840594115dd6a992b45090ff986f8257503ee8a0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 21 Sep 2016 23:16:31 -0600 Subject: [PATCH 0515/1096] Update for changes in rustc. --- src/interpreter/step.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 6f8a297a6d8f8..f87f5e43a9605 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -82,6 +82,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Miri can safely ignore these. Only translation needs them. StorageLive(_) | StorageDead(_) => {} + + // Defined to do nothing. These are added by optimization passes, to avoid changing the + // size of MIR constantly. + Nop => {} } self.frame_mut().stmt += 1; @@ -186,12 +190,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } } - fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext, location: mir::Location) { + fn visit_lvalue( + &mut self, + lvalue: &mir::Lvalue<'tcx>, + context: LvalueContext<'tcx>, + location: mir::Location + ) { self.super_lvalue(lvalue, context, location); if let mir::Lvalue::Static(def_id) = *lvalue { let substs = subst::Substs::empty(self.ecx.tcx); let span = self.span; - if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = self.ecx.tcx.map.get_if_local(def_id).expect("static not found") { + let node_item = self.ecx.tcx.map.get_if_local(def_id).expect("static not found"); + if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { if let hir::ItemStatic(_, m, _) = *node { self.global_item(def_id, substs, span, m == hir::MutImmutable); return; From 5b012edc7ab8690610b0502be24f3eb8ed30486b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 21 Sep 2016 23:23:50 -0600 Subject: [PATCH 0516/1096] Rename AbstractPtr to Ptr. --- src/interpreter/cast.rs | 4 ++-- src/interpreter/mod.rs | 6 +++--- src/interpreter/terminator/mod.rs | 2 +- src/memory.rs | 2 +- src/primval.rs | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 580f957b44916..06cbf2495dac7 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -28,7 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64(u) | IntegerPtr(u) => self.cast_const_int(u, ty, false), FnPtr(ptr) | - AbstractPtr(ptr) => self.cast_ptr(ptr, ty), + Ptr(ptr) => self.cast_ptr(ptr, ty), } } @@ -36,7 +36,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use primval::PrimVal::*; match ty.sty { ty::TyRef(..) | - ty::TyRawPtr(_) => Ok(AbstractPtr(ptr)), + ty::TyRawPtr(_) => Ok(Ptr(ptr)), ty::TyFnPtr(_) => Ok(FnPtr(ptr)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f7d6b464cda3c..c6bbe829b5cad 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -246,7 +246,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; Value::ByValPair( - PrimVal::AbstractPtr(ptr), + PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64) ) } @@ -255,7 +255,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(bs.len(), 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - Value::ByVal(PrimVal::AbstractPtr(ptr)) + Value::ByVal(PrimVal::Ptr(ptr)) } Struct(_) => unimplemented!(), @@ -1050,7 +1050,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { if self.type_is_sized(ty) { match self.memory.read_ptr(ptr) { - Ok(p) => PrimVal::AbstractPtr(p), + Ok(p) => PrimVal::Ptr(p), Err(EvalError::ReadBytesAsPointer) => { PrimVal::IntegerPtr(self.memory.read_usize(ptr)?) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index b8d46323bfa87..10273881b67c6 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -420,7 +420,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: this is a memory leak, should probably add the pointer to the // current stack. let first = self.value_to_ptr(args[0].0, args[0].1)?; - args[0].0 = Value::ByVal(PrimVal::AbstractPtr(first)); + args[0].0 = Value::ByVal(PrimVal::Ptr(first)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } diff --git a/src/memory.rs b/src/memory.rs index 6042181cdf2e6..0108f7e5d9c18 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -530,7 +530,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::F32(f) => self.write_f32(ptr, f), PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(p) | - PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p), + PrimVal::Ptr(p) => self.write_ptr(ptr, p), } } diff --git a/src/primval.rs b/src/primval.rs index d75b7879c375c..50ef05a245dfd 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -12,7 +12,7 @@ pub enum PrimVal { I8(i8), I16(i16), I32(i32), I64(i64), U8(u8), U16(u16), U32(u32), U64(u64), - AbstractPtr(Pointer), + Ptr(Pointer), FnPtr(Pointer), IntegerPtr(u64), Char(char), @@ -211,10 +211,10 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), - (AbstractPtr(_), IntegerPtr(_)) | - (IntegerPtr(_), AbstractPtr(_)) | - (FnPtr(_), AbstractPtr(_)) | - (AbstractPtr(_), FnPtr(_)) | + (Ptr(_), IntegerPtr(_)) | + (IntegerPtr(_), Ptr(_)) | + (FnPtr(_), Ptr(_)) | + (Ptr(_), FnPtr(_)) | (FnPtr(_), IntegerPtr(_)) | (IntegerPtr(_), FnPtr(_)) => unrelated_ptr_ops(bin_op)?, @@ -225,7 +225,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))), }, - (AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => { + (Ptr(l_ptr), Ptr(r_ptr)) => { if l_ptr.alloc_id != r_ptr.alloc_id { return Ok((unrelated_ptr_ops(bin_op)?, false)); } From 0f578f0d2ef1943d8a3a1123520eba90f926e757 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Sep 2016 13:00:43 +0200 Subject: [PATCH 0517/1096] fully implement `size_of_val` and add various tests that now succeed --- src/interpreter/mod.rs | 3 + src/interpreter/terminator/intrinsics.rs | 128 ++++++++++++++++--- src/memory.rs | 2 +- tests/run-pass/cast-rfc0401-vtable-kinds.rs | 54 ++++++++ tests/run-pass/dst-irrefutable-bind.rs | 24 ++++ tests/run-pass/dst-raw.rs | 113 ++++++++++++++++ tests/run-pass/dst-struct-sole.rs | 85 ++++++++++++ tests/run-pass/issue-23261.rs | 70 ++++++++++ tests/run-pass/issue-36278-prefix-nesting.rs | 28 ++++ tests/run-pass/mir_fat_ptr.rs | 61 +++++++++ 10 files changed, 551 insertions(+), 17 deletions(-) create mode 100644 tests/run-pass/cast-rfc0401-vtable-kinds.rs create mode 100644 tests/run-pass/dst-irrefutable-bind.rs create mode 100644 tests/run-pass/dst-raw.rs create mode 100644 tests/run-pass/dst-struct-sole.rs create mode 100644 tests/run-pass/issue-23261.rs create mode 100644 tests/run-pass/issue-36278-prefix-nesting.rs create mode 100644 tests/run-pass/mir_fat_ptr.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7e4407ce59a8..b590905264e66 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -621,6 +621,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_operand_to_ptr(operand)?; let src_ty = self.operand_ty(operand); let dest_ty = self.monomorphize(dest_ty, self.substs()); + // FIXME: cases where dest_ty is not a fat pointer. e.g. Arc -> Arc assert!(self.type_is_fat_ptr(dest_ty)); let (ptr, extra) = self.get_fat_ptr(dest); self.move_(src, ptr, src_ty)?; @@ -883,6 +884,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = variant.field_offset(field.index()).bytes(); let ptr = base.ptr.offset(offset as isize); match (&field_ty.sty, base.extra) { + (&ty::TyStr, extra @ LvalueExtra::Length(_)) | + (&ty::TySlice(_), extra @ LvalueExtra::Length(_)) | (&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue { ptr: ptr, extra: extra, diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index e2381d59e2cd6..f7fa1588b5610 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -188,22 +188,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of_val" => { let ty = substs.type_at(0); - if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; - } else { - match ty.sty { - ty::TySlice(_) | ty::TyStr => { - let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size() as isize; - let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; - self.memory.write_uint(dest, n * elem_size, pointer_size)?; - } - - _ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))), - } - } + let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?; + self.memory.write_uint(dest, size, pointer_size)?; } // FIXME: wait for eval_operand_to_ptr to be gone /* @@ -248,4 +234,114 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // current frame. Ok(()) } + + fn size_and_align_of_dst( + &self, + ty: ty::Ty<'tcx>, + value: Pointer, + ) -> EvalResult<'tcx, (u64, u64)> { + let pointer_size = self.memory.pointer_size(); + if self.type_is_sized(ty) { + Ok((self.type_size(ty) as u64, self.type_align(ty) as u64)) + } else { + match ty.sty { + ty::TyAdt(def, substs) => { + // First get the size of all statically known fields. + // Don't use type_of::sizing_type_of because that expects t to be sized, + // and it also rounds up to alignment, which we want to avoid, + // as the unsized field's alignment could be smaller. + assert!(!ty.is_simd()); + let layout = self.type_layout(ty); + debug!("DST {} layout: {:?}", ty, layout); + + // Returns size in bytes of all fields except the last one + // (we will be recursing on the last one). + fn local_prefix_bytes(variant: &ty::layout::Struct) -> u64 { + let fields = variant.offset_after_field.len(); + if fields > 1 { + variant.offset_after_field[fields - 2].bytes() + } else { + 0 + } + } + + let (sized_size, sized_align) = match *layout { + ty::layout::Layout::Univariant { ref variant, .. } => { + (local_prefix_bytes(variant), variant.align.abi()) + } + _ => { + bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", + ty, layout); + } + }; + debug!("DST {} statically sized prefix size: {} align: {}", + ty, sized_size, sized_align); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let last_field = def.struct_variant().fields.last().unwrap(); + let field_ty = self.field_ty(substs, last_field); + let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?; + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = sized_size + unsized_size; + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let align = ::std::cmp::max(sized_align, unsized_align); + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + + if size & (align - 1) != 0 { + Ok((size + align, align)) + } else { + Ok((size, align)) + } + } + ty::TyTrait(..) => { + let (_, vtable) = self.get_fat_ptr(value); + let vtable = self.memory.read_ptr(vtable)?; + // the second entry in the vtable is the dynamic size of the object. + let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?; + let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?; + Ok((size, align)) + } + + ty::TySlice(_) | ty::TyStr => { + let elem_ty = ty.sequence_element_type(self.tcx); + let elem_size = self.type_size(elem_ty) as u64; + let (_, len_ptr) = self.get_fat_ptr(value); + let n = self.memory.read_usize(len_ptr)?; + let align = self.type_align(elem_ty); + Ok((n * elem_size, align as u64)) + } + + _ => bug!("size_of_val::<{:?}>", ty), + } + } + } + /// Returns the normalized type of a struct field + fn field_ty( + &self, + param_substs: &Substs<'tcx>, + f: ty::FieldDef<'tcx>, + )-> ty::Ty<'tcx> { + self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) + } } diff --git a/src/memory.rs b/src/memory.rs index 0108f7e5d9c18..980f9ca728bb5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), - align: 1, + align: 8, // should be infinity? immutable: false, // must be mutable, because sometimes we "move out" of a ZST }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); diff --git a/tests/run-pass/cast-rfc0401-vtable-kinds.rs b/tests/run-pass/cast-rfc0401-vtable-kinds.rs new file mode 100644 index 0000000000000..3a9f24ad4cc7c --- /dev/null +++ b/tests/run-pass/cast-rfc0401-vtable-kinds.rs @@ -0,0 +1,54 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that you can cast between different pointers to trait objects +// whose vtable have the same kind (both lengths, or both trait pointers). + +trait Foo { + fn foo(&self, _: T) -> u32 { 42 } +} + +trait Bar { + fn bar(&self) { println!("Bar!"); } +} + +impl Foo for () {} +impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } +impl Bar for () {} + +unsafe fn round_trip_and_call<'a>(t: *const (Foo+'a)) -> u32 { + let foo_e : *const Foo = t as *const _; + let r_1 = foo_e as *mut Foo; + + (&*r_1).foo(0) +} + +#[repr(C)] +struct FooS(T); +#[repr(C)] +struct BarS(T); + +fn foo_to_bar(u: *const FooS) -> *const BarS { + u as *const BarS +} + +fn main() { + let x = 4u32; + let y : &Foo = &x; + let fl = unsafe { round_trip_and_call(y as *const Foo) }; + assert_eq!(fl, (43+4)); + + let s = FooS([0,1,2]); + let u: &FooS<[u32]> = &s; + let u: *const FooS<[u32]> = u; + let bar_ref : *const BarS<[u32]> = foo_to_bar(u); + let z : &BarS<[u32]> = unsafe{&*bar_ref}; + assert_eq!(&z.0, &[0,1,2]); +} diff --git a/tests/run-pass/dst-irrefutable-bind.rs b/tests/run-pass/dst-irrefutable-bind.rs new file mode 100644 index 0000000000000..9f8067f372aef --- /dev/null +++ b/tests/run-pass/dst-irrefutable-bind.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Test(T); + +fn main() { + let x = Test([1,2,3]); + let x : &Test<[i32]> = &x; + + let & ref _y = x; + + // Make sure binding to a fat pointer behind a reference + // still works + let slice = &[1,2,3]; + let x = Test(&slice); + let Test(&_slice) = x; +} diff --git a/tests/run-pass/dst-raw.rs b/tests/run-pass/dst-raw.rs new file mode 100644 index 0000000000000..3a74626b0299f --- /dev/null +++ b/tests/run-pass/dst-raw.rs @@ -0,0 +1,113 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test DST raw pointers + + +trait Trait { + fn foo(&self) -> isize; +} + +struct A { + f: isize +} +impl Trait for A { + fn foo(&self) -> isize { + self.f + } +} + +struct Foo { + f: T +} + +pub fn main() { + // raw trait object + let x = A { f: 42 }; + let z: *const Trait = &x; + let r = unsafe { + (&*z).foo() + }; + assert_eq!(r, 42); + + // raw DST struct + let p = Foo {f: A { f: 42 }}; + let o: *const Foo = &p; + let r = unsafe { + (&*o).f.foo() + }; + assert_eq!(r, 42); + + // raw slice + let a: *const [_] = &[1, 2, 3]; + unsafe { + let b = (*a)[2]; + assert_eq!(b, 3); + let len = (*a).len(); + assert_eq!(len, 3); + } + + // raw slice with explicit cast + let a = &[1, 2, 3] as *const [i32]; + unsafe { + let b = (*a)[2]; + assert_eq!(b, 3); + let len = (*a).len(); + assert_eq!(len, 3); + } + + // raw DST struct with slice + let c: *const Foo<[_]> = &Foo {f: [1, 2, 3]}; + unsafe { + let b = (&*c).f[0]; + assert_eq!(b, 1); + let len = (&*c).f.len(); + assert_eq!(len, 3); + } + + // all of the above with *mut + let mut x = A { f: 42 }; + let z: *mut Trait = &mut x; + let r = unsafe { + (&*z).foo() + }; + assert_eq!(r, 42); + + let mut p = Foo {f: A { f: 42 }}; + let o: *mut Foo = &mut p; + let r = unsafe { + (&*o).f.foo() + }; + assert_eq!(r, 42); + + let a: *mut [_] = &mut [1, 2, 3]; + unsafe { + let b = (*a)[2]; + assert_eq!(b, 3); + let len = (*a).len(); + assert_eq!(len, 3); + } + + let a = &mut [1, 2, 3] as *mut [i32]; + unsafe { + let b = (*a)[2]; + assert_eq!(b, 3); + let len = (*a).len(); + assert_eq!(len, 3); + } + + let c: *mut Foo<[_]> = &mut Foo {f: [1, 2, 3]}; + unsafe { + let b = (&*c).f[0]; + assert_eq!(b, 1); + let len = (&*c).f.len(); + assert_eq!(len, 3); + } +} diff --git a/tests/run-pass/dst-struct-sole.rs b/tests/run-pass/dst-struct-sole.rs new file mode 100644 index 0000000000000..58d7b35a5275c --- /dev/null +++ b/tests/run-pass/dst-struct-sole.rs @@ -0,0 +1,85 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// As dst-struct.rs, but the unsized field is the only field in the struct. + + +struct Fat { + ptr: T +} + +// x is a fat pointer +fn foo(x: &Fat<[isize]>) { + let y = &x.ptr; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0], 1); + assert_eq!(x.ptr[1], 2); +} + +fn foo2(x: &Fat<[T]>) { + let y = &x.ptr; + let bar = Bar; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0].to_bar(), bar); + assert_eq!(x.ptr[1].to_bar(), bar); +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Bar; + +trait ToBar { + fn to_bar(&self) -> Bar; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { ptr: [1, 2, 3] }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<[isize]> = f2; + foo(f3); + let f4: &Fat<[isize]> = &f1; + foo(f4); + let f5: &Fat<[isize]> = &Fat { ptr: [1, 2, 3] }; + foo(f5); + + // With a vec of Bars. + let bar = Bar; + let f1 = Fat { ptr: [bar, bar, bar] }; + foo2(&f1); + let f2 = &f1; + foo2(f2); + let f3: &Fat<[Bar]> = f2; + foo2(f3); + let f4: &Fat<[Bar]> = &f1; + foo2(f4); + let f5: &Fat<[Bar]> = &Fat { ptr: [bar, bar, bar] }; + foo2(f5); + + // Assignment. + let f5: &mut Fat<[isize]> = &mut Fat { ptr: [1, 2, 3] }; + f5.ptr[1] = 34; + assert_eq!(f5.ptr[0], 1); + assert_eq!(f5.ptr[1], 34); + assert_eq!(f5.ptr[2], 3); + + // Zero size vec. + let f5: &Fat<[isize]> = &Fat { ptr: [] }; + assert!(f5.ptr.is_empty()); + let f5: &Fat<[Bar]> = &Fat { ptr: [] }; + assert!(f5.ptr.is_empty()); +} diff --git a/tests/run-pass/issue-23261.rs b/tests/run-pass/issue-23261.rs new file mode 100644 index 0000000000000..fc806f5429a47 --- /dev/null +++ b/tests/run-pass/issue-23261.rs @@ -0,0 +1,70 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Matching on a DST struct should not trigger an LLVM assertion. + +struct Foo { + a: i32, + inner: T +} + +trait Get { + fn get(&self) -> i32; +} + +impl Get for i32 { + fn get(&self) -> i32 { + *self + } +} + +fn check_val(val: &Foo<[u8]>) { + match *val { + Foo { a, .. } => { + assert_eq!(a, 32); + } + } +} + +fn check_dst_val(val: &Foo<[u8]>) { + match *val { + Foo { ref inner, .. } => { + assert_eq!(inner, [1, 2, 3]); + } + } +} + +fn check_both(val: &Foo<[u8]>) { + match *val { + Foo { a, ref inner } => { + assert_eq!(a, 32); + assert_eq!(inner, [1, 2, 3]); + } + } +} + +fn check_trait_obj(val: &Foo) { + match *val { + Foo { a, ref inner } => { + assert_eq!(a, 32); + assert_eq!(inner.get(), 32); + } + } +} + +fn main() { + let foo: &Foo<[u8]> = &Foo { a: 32, inner: [1, 2, 3] }; + check_val(foo); + check_dst_val(foo); + check_both(foo); + + let foo: &Foo = &Foo { a: 32, inner: 32 }; + check_trait_obj(foo); +} diff --git a/tests/run-pass/issue-36278-prefix-nesting.rs b/tests/run-pass/issue-36278-prefix-nesting.rs new file mode 100644 index 0000000000000..95269d0569dec --- /dev/null +++ b/tests/run-pass/issue-36278-prefix-nesting.rs @@ -0,0 +1,28 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Issue 36278: On an unsized struct with >1 level of nontrivial +// nesting, ensure we are computing dynamic size of prefix correctly. + +use std::mem; + +const SZ: usize = 100; +struct P([u8; SZ], T); + +type Ack = P>; + +fn main() { + let size_of_sized; let size_of_unsized; + let x: Box> = Box::new(P([0; SZ], P([0; SZ], [0; 0]))); + size_of_sized = mem::size_of_val::>(&x); + let y: Box> = x; + size_of_unsized = mem::size_of_val::>(&y); + assert_eq!(size_of_sized, size_of_unsized); +} diff --git a/tests/run-pass/mir_fat_ptr.rs b/tests/run-pass/mir_fat_ptr.rs new file mode 100644 index 0000000000000..e5c9e3577d1c3 --- /dev/null +++ b/tests/run-pass/mir_fat_ptr.rs @@ -0,0 +1,61 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// test that ordinary fat pointer operations work. + +struct Wrapper(u32, T); + +struct FatPtrContainer<'a> { + ptr: &'a [u8] +} + +fn fat_ptr_project(a: &Wrapper<[u8]>) -> &[u8] { + &a.1 +} + +fn fat_ptr_simple(a: &[u8]) -> &[u8] { + a +} + +fn fat_ptr_via_local(a: &[u8]) -> &[u8] { + let x = a; + x +} + +fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] { + s.ptr +} + +fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer { + FatPtrContainer { ptr: a } +} + +fn fat_ptr_store_to<'a>(a: &'a [u8], b: &mut &'a [u8]) { + *b = a; +} + +fn fat_ptr_constant() -> &'static str { + "HELLO" +} + +fn main() { + let a = Wrapper(4, [7,6,5]); + + let p = fat_ptr_project(&a); + let p = fat_ptr_simple(p); + let p = fat_ptr_via_local(p); + let p = fat_ptr_from_struct(fat_ptr_to_struct(p)); + + let mut target : &[u8] = &[42]; + fat_ptr_store_to(p, &mut target); + assert_eq!(target, &a.1); + + assert_eq!(fat_ptr_constant(), "HELLO"); +} From 0690a26ddfb762c7fb7906326f41a7e470728654 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Sep 2016 13:01:08 +0200 Subject: [PATCH 0518/1096] make Memory::dump use `trace!` instead of `println!` --- src/memory.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 980f9ca728bb5..ae590b0eef106 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -345,25 +345,26 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Print an allocation and all allocations it points to, recursively. pub fn dump(&self, id: AllocId) { + use std::fmt::Write; let mut allocs_seen = HashSet::new(); let mut allocs_to_print = VecDeque::new(); allocs_to_print.push_back(id); while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id); - let prefix = format!("Alloc {:<5} ", format!("{}:", id)); - print!("{}", prefix); + let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); + let prefix_len = msg.len(); let mut relocations = vec![]; let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { (Some(a), None) => a, (None, Some(_)) => { // FIXME: print function name - println!("function pointer"); + trace!("{} function pointer", msg); continue; }, (None, None) => { - println!("(deallocated)"); + trace!("{} (deallocated)", msg); continue; }, (Some(_), Some(_)) => bug!("miri invariant broken: an allocation id exists that points to both a function and a memory location"), @@ -377,25 +378,26 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations.push((i, target_id)); } if alloc.undef_mask.is_range_defined(i, i + 1) { - print!("{:02x} ", alloc.bytes[i]); + write!(msg, "{:02x} ", alloc.bytes[i]).unwrap(); } else { - print!("__ "); + msg.push_str("__ "); } } let immutable = if alloc.immutable { " (immutable)" } else { "" }; - println!("({} bytes){}", alloc.bytes.len(), immutable); + trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable); if !relocations.is_empty() { - print!("{:1$}", "", prefix.len()); // Print spaces. + msg.clear(); + write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces. let mut pos = 0; let relocation_width = (self.pointer_size() - 1) * 3; for (i, target_id) in relocations { - print!("{:1$}", "", (i - pos) * 3); - print!("└{0:─^1$}┘ ", format!("({})", target_id), relocation_width); + write!(msg, "{:1$}", "", (i - pos) * 3).unwrap(); + write!(msg, "└{0:─^1$}┘ ", format!("({})", target_id), relocation_width).unwrap(); pos = i + self.pointer_size(); } - println!(""); + trace!("{}", msg); } } } From 875a4542f9eeace01f1ee5ec5fd25904601f58c5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Sep 2016 15:22:00 +0200 Subject: [PATCH 0519/1096] remove the ZST allocation and abort all zero byte writes/reads --- src/error.rs | 3 +++ src/memory.rs | 45 +++++++++++++++++++++++---------------- tests/compile-fail/zst.rs | 4 ++++ 3 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 tests/compile-fail/zst.rs diff --git a/src/error.rs b/src/error.rs index 5624734e888ab..82b4eff6e9264 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), DanglingPointerDeref, + ZstAllocAccess, InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, @@ -53,6 +54,8 @@ impl<'tcx> Error for EvalError<'tcx> { match *self { EvalError::FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", + EvalError::ZstAllocAccess => + "tried to access the ZST allocation", EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => diff --git a/src/memory.rs b/src/memory.rs index ae590b0eef106..53c50e45e8566 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -105,7 +105,7 @@ const ZST_ALLOC_ID: AllocId = AllocId(0); impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { - let mut mem = Memory { + Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), @@ -113,21 +113,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { layout: layout, memory_size: max_memory, memory_usage: 0, - }; - // alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST - // (e.g. function items, (), [], ...) from requiring memory - let alloc = Allocation { - bytes: Vec::new(), - relocations: BTreeMap::new(), - undef_mask: UndefMask::new(0), - align: 8, // should be infinity? - immutable: false, // must be mutable, because sometimes we "move out" of a ZST - }; - mem.alloc_map.insert(ZST_ALLOC_ID, alloc); - // check that additional zst allocs work - debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst()); - debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); - mem + } } pub fn allocations(&self) -> ::std::collections::hash_map::Iter { @@ -293,6 +279,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), + None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -304,6 +291,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), + None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -353,6 +341,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id); let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); + if id == ZST_ALLOC_ID { + trace!("{} zst allocation", msg); + continue; + } let prefix_len = msg.len(); let mut relocations = vec![]; @@ -406,6 +398,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + if size == 0 { + return Ok(&[]); + } let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -418,6 +413,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + if size == 0 { + return Ok(&mut []); + } let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -430,6 +428,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + if size == 0 { + return Ok(&[]); + } self.check_align(ptr, align)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); @@ -439,6 +440,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + if size == 0 { + return Ok(&mut []); + } self.check_align(ptr, align)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; @@ -449,8 +453,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { - // Never freeze the zero-sized allocation. If you do that, then getting a mutable handle to - // _any_ ZST becomes an error, since they all share the same allocation. + // It's not possible to freeze the zero-sized allocation, because it doesn't exist. if alloc_id != ZST_ALLOC_ID { self.get_mut(alloc_id)?.immutable = true; } @@ -458,6 +461,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { + if size == 0 { + return Ok(()); + } self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked(src, size)?.as_ptr(); @@ -714,6 +720,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<'tcx, ()> { + if size == 0 { + return Ok(()) + } let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) diff --git a/tests/compile-fail/zst.rs b/tests/compile-fail/zst.rs new file mode 100644 index 0000000000000..a244befed018b --- /dev/null +++ b/tests/compile-fail/zst.rs @@ -0,0 +1,4 @@ +fn main() { + let x = &() as *const () as *const i32; + let _ = unsafe { *x }; //~ ERROR: tried to access the ZST allocation +} From 38748fa6150a99aaa16902ecd081a38bcfb92630 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Sep 2016 15:47:16 +0200 Subject: [PATCH 0520/1096] refactor away IntegerPtr --- src/error.rs | 9 +++------ src/interpreter/cast.rs | 5 ++--- src/interpreter/mod.rs | 8 +------- src/interpreter/terminator/intrinsics.rs | 15 +++------------ src/memory.rs | 14 +++++++++----- src/primval.rs | 9 +-------- tests/compile-fail/null_pointer_deref.rs | 2 +- tests/compile-fail/wild_pointer_deref.rs | 2 +- tests/compile-fail/zst.rs | 2 +- tests/run-pass/zst.rs | 2 ++ 10 files changed, 24 insertions(+), 44 deletions(-) diff --git a/src/error.rs b/src/error.rs index 82b4eff6e9264..fde3db15968cf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), DanglingPointerDeref, - ZstAllocAccess, + InvalidMemoryAccess, InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, @@ -20,7 +20,6 @@ pub enum EvalError<'tcx> { allocation_size: usize, }, ReadPointerAsBytes, - ReadBytesAsPointer, InvalidPointerMath, ReadUndefBytes, InvalidBoolOp(mir::BinOp), @@ -54,8 +53,8 @@ impl<'tcx> Error for EvalError<'tcx> { match *self { EvalError::FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", - EvalError::ZstAllocAccess => - "tried to access the ZST allocation", + EvalError::InvalidMemoryAccess => + "tried to access memory through an invalid pointer", EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => @@ -68,8 +67,6 @@ impl<'tcx> Error for EvalError<'tcx> { "pointer offset outside bounds of allocation", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", - EvalError::ReadBytesAsPointer => - "attempted to interpret some raw bytes as a pointer address", EvalError::InvalidPointerMath => "attempted to do math or a comparison on pointers into different allocations", EvalError::ReadUndefBytes => diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 06cbf2495dac7..6227999569cf4 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -25,8 +25,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U16(u) => self.cast_const_int(u as u64, ty, false), U32(u) => self.cast_const_int(u as u64, ty, false), Char(c) => self.cast_const_int(c as u64, ty, false), - U64(u) | - IntegerPtr(u) => self.cast_const_int(u, ty, false), + U64(u) => self.cast_const_int(u, ty, false), FnPtr(ptr) | Ptr(ptr) => self.cast_ptr(ptr, ty), } @@ -74,7 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)), ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)), ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)), - ty::TyRawPtr(_) => Ok(IntegerPtr(v)), + ty::TyRawPtr(_) => Ok(Ptr(Pointer::from_int(v as usize))), ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)), ty::TyChar => Err(EvalError::InvalidChar(v)), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b590905264e66..9de72dc6225b4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1064,13 +1064,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &ty::TyRef(_, ty::TypeAndMut { ty, .. }) | &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { if self.type_is_sized(ty) { - match self.memory.read_ptr(ptr) { - Ok(p) => PrimVal::Ptr(p), - Err(EvalError::ReadBytesAsPointer) => { - PrimVal::IntegerPtr(self.memory.read_usize(ptr)?) - } - Err(e) => return Err(e), - } + PrimVal::Ptr(self.memory.read_ptr(ptr)?) } else { bug!("primitive read of fat pointer type: {:?}", ty); } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index f7fa1588b5610..38b62254130f6 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -132,18 +132,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_arg = args_ptrs[0]; let offset = self.memory.read_isize(args_ptrs[1])?; - match self.memory.read_ptr(ptr_arg) { - Ok(ptr) => { - let result_ptr = ptr.offset(offset as isize * pointee_size); - self.memory.write_ptr(dest, result_ptr)?; - } - Err(EvalError::ReadBytesAsPointer) => { - let addr = self.memory.read_isize(ptr_arg)?; - let result_addr = addr + offset * pointee_size as i64; - self.memory.write_isize(dest, result_addr)?; - } - Err(e) => return Err(e), - } + let ptr = self.memory.read_ptr(ptr_arg)?; + let result_ptr = ptr.offset(offset as isize * pointee_size); + self.memory.write_ptr(dest, result_ptr)?; } "overflowing_sub" => { diff --git a/src/memory.rs b/src/memory.rs index 53c50e45e8566..27e6ffff00ebe 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -55,6 +55,12 @@ impl Pointer { pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } + pub fn from_int(i: usize) -> Self { + Pointer { + alloc_id: ZST_ALLOC_ID, + offset: i, + } + } fn zst_ptr() -> Self { Pointer { alloc_id: ZST_ALLOC_ID, @@ -279,7 +285,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), + None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -291,7 +297,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), + None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -511,7 +517,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), - None => Err(EvalError::ReadBytesAsPointer), + None => Ok(Pointer::from_int(offset)), } } @@ -522,7 +528,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), @@ -534,7 +539,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), - PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::F32(f) => self.write_f32(ptr, f), PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(p) | diff --git a/src/primval.rs b/src/primval.rs index 50ef05a245dfd..717ad99dbcd50 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,7 +14,6 @@ pub enum PrimVal { Ptr(Pointer), FnPtr(Pointer), - IntegerPtr(u64), Char(char), F32(f32), F64(f64), @@ -209,14 +208,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva }) } - (IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), - - (Ptr(_), IntegerPtr(_)) | - (IntegerPtr(_), Ptr(_)) | (FnPtr(_), Ptr(_)) | - (Ptr(_), FnPtr(_)) | - (FnPtr(_), IntegerPtr(_)) | - (IntegerPtr(_), FnPtr(_)) => + (Ptr(_), FnPtr(_)) => unrelated_ptr_ops(bin_op)?, (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op { diff --git a/tests/compile-fail/null_pointer_deref.rs b/tests/compile-fail/null_pointer_deref.rs index 3d1afe9215618..fcf34ed44c93a 100644 --- a/tests/compile-fail/null_pointer_deref.rs +++ b/tests/compile-fail/null_pointer_deref.rs @@ -1,4 +1,4 @@ fn main() { - let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: attempted to interpret some raw bytes as a pointer address + let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: tried to access memory through an invalid pointer panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/wild_pointer_deref.rs b/tests/compile-fail/wild_pointer_deref.rs index 1f472489b4fca..937546fdc350a 100644 --- a/tests/compile-fail/wild_pointer_deref.rs +++ b/tests/compile-fail/wild_pointer_deref.rs @@ -1,5 +1,5 @@ fn main() { let p = 42 as *const i32; - let x = unsafe { *p }; //~ ERROR: attempted to interpret some raw bytes as a pointer address + let x = unsafe { *p }; //~ ERROR: tried to access memory through an invalid pointer panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/zst.rs b/tests/compile-fail/zst.rs index a244befed018b..970cc9abc9daf 100644 --- a/tests/compile-fail/zst.rs +++ b/tests/compile-fail/zst.rs @@ -1,4 +1,4 @@ fn main() { let x = &() as *const () as *const i32; - let _ = unsafe { *x }; //~ ERROR: tried to access the ZST allocation + let _ = unsafe { *x }; //~ ERROR: tried to access memory through an invalid pointer } diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs index 4ebb2001e7203..78d3025587f04 100644 --- a/tests/run-pass/zst.rs +++ b/tests/run-pass/zst.rs @@ -21,4 +21,6 @@ fn main() { assert_eq!(use_zst(), A); assert_eq!(&A as *const A as *const (), &() as *const _); assert_eq!(&A as *const A, &A as *const A); + let x = 42 as *mut (); + unsafe { *x = (); } } From 2282289ad5fbfaaaffd6b4fe4ceb6665ab9ab391 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 10:27:14 +0200 Subject: [PATCH 0521/1096] refactor away intermediate allocations, stage1 --- src/interpreter/cast.rs | 1 + src/interpreter/mod.rs | 119 +++++++++-------------- src/interpreter/terminator/intrinsics.rs | 95 ++++++++++-------- src/interpreter/terminator/mod.rs | 34 +++---- src/interpreter/value.rs | 60 ++++++++++++ src/memory.rs | 17 ++++ src/primval.rs | 18 ++++ tests/run-pass/issue-33387.rs | 19 ++++ 8 files changed, 233 insertions(+), 130 deletions(-) create mode 100644 src/interpreter/value.rs create mode 100644 tests/run-pass/issue-33387.rs diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 6227999569cf4..4f31cc210339c 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64(u) => self.cast_const_int(u, ty, false), FnPtr(ptr) | Ptr(ptr) => self.cast_ptr(ptr, ty), + VtablePtr(..) | SlicePtr(..) => unimplemented!(), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9de72dc6225b4..c30c47961daec 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -17,6 +17,7 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer, AllocId}; use primval::{self, PrimVal}; +use self::value::Value; use std::collections::HashMap; @@ -24,6 +25,7 @@ mod step; mod terminator; mod cast; mod vtable; +mod value; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -99,21 +101,6 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } -/// A `Value` represents a single self-contained Rust value. -/// -/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve -/// value held directly, outside of any allocation (`ByVal`). -/// -/// For optimization of a few very common cases, there is also a representation for a pair of -/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary -/// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug)] -enum Value { - ByRef(Pointer), - ByVal(PrimVal), - ByValPair(PrimVal, PrimVal), -} - #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Lvalue { ptr: Pointer, @@ -245,10 +232,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Value::ByValPair( - PrimVal::Ptr(ptr), - self.target_usize_primval(s.len() as u64) - ) + Value::ByVal(PrimVal::SlicePtr(ptr, s.len() as u64)) } ByteStr(ref bs) => { @@ -618,13 +602,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::CastKind::*; match kind { Unsize => { - let src = self.eval_operand_to_ptr(operand)?; + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); let dest_ty = self.monomorphize(dest_ty, self.substs()); // FIXME: cases where dest_ty is not a fat pointer. e.g. Arc -> Arc assert!(self.type_is_fat_ptr(dest_ty)); let (ptr, extra) = self.get_fat_ptr(dest); - self.move_(src, ptr, src_ty)?; + self.move_value(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -639,9 +623,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - let (_, src_extra) = self.get_fat_ptr(src); - let src_extra = self.memory.read_ptr(src_extra)?; - self.memory.write_ptr(extra, src_extra)?; + let src_extra = src.expect_fat_ptr_extra(&self.memory)?; + self.memory.write_primval(extra, src_extra)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); @@ -655,25 +638,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Misc => { - let src = self.eval_operand_to_ptr(operand)?; + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); if self.type_is_fat_ptr(src_ty) { - let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); + trace!("misc cast: {:?}", src); let ptr_size = self.memory.pointer_size(); - let dest_ty = self.monomorphize(dest_ty, self.substs()); - if self.type_is_fat_ptr(dest_ty) { - // FIXME: add assertion that the extra part of the src_ty and - // dest_ty is of the same type - self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?; - } else { // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; + match (src, self.type_is_fat_ptr(dest_ty)) { + (Value::ByVal(PrimVal::VtablePtr(data, meta)), true) => { + self.memory.write_ptr(dest, data)?; + self.memory.write_ptr(dest.offset(ptr_size as isize), meta)?; + }, + (Value::ByVal(PrimVal::SlicePtr(data, meta)), true) => { + self.memory.write_ptr(dest, data)?; + self.memory.write_usize(dest.offset(ptr_size as isize), meta)?; + }, + (Value::ByVal(PrimVal::SlicePtr(data, _)), false) | + (Value::ByVal(PrimVal::VtablePtr(data, _)), false) => { + self.memory.write_ptr(dest, data)?; + }, + (Value::ByRef(ptr), true) => { + self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?; + }, + (Value::ByRef(ptr), false) => { + self.memory.copy(ptr, dest, ptr_size, ptr_size)?; + }, + (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { - // FIXME: dest_ty should already be monomorphized - let dest_ty = self.monomorphize(dest_ty, self.substs()); - let src_val = self.read_primval(src, src_ty)?; + let src_val = self.value_to_primval(src, src_ty)?; let dest_val = self.cast_primval(src_val, dest_ty)?; self.memory.write_primval(dest, dest_val)?; } @@ -689,8 +683,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { - let src = self.eval_operand_to_ptr(operand)?; - let ptr = self.memory.read_ptr(src)?; + let src = self.eval_operand(operand)?; + let ptr = src.read_ptr(&self.memory)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); self.memory.write_ptr(dest, fn_ptr)?; @@ -779,14 +773,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can - // remove it as soon as PrimVal can represent fat pointers. - fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { - let value = self.eval_operand(op)?; - let ty = self.operand_ty(op); - self.value_to_ptr(value, ty) - } - fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { let value = self.eval_operand(op)?; let ty = self.operand_ty(op); @@ -857,6 +843,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; + trace!("projection base: {:?}", base); + trace!("projection: {:?}", proj.elem); let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -937,8 +925,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => bug!("indexing expected an array or slice, got {:?}", base_ty), }; - let n_ptr = self.eval_operand_to_ptr(operand)?; - let n = self.memory.read_usize(n_ptr)?; + let n_ptr = self.eval_operand(operand)?; + let usize = self.tcx.types.usize; + let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); base.ptr.offset(n as isize * elem_size as isize) } @@ -965,6 +954,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } + fn move_value(&mut self, src: Value, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + match src { + Value::ByRef(ptr) => self.move_(ptr, dest, ty), + Value::ByVal(val) => self.memory.write_primval(dest, val), + } + } + fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); @@ -974,7 +970,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can // remove it as soon as PrimVal can represent fat pointers. - fn value_to_ptr(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { + fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { match value { Value::ByRef(ptr) => Ok(ptr), @@ -985,18 +981,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(ptr, primval)?; Ok(ptr) } - - Value::ByValPair(primval1, primval2) => { - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - - // FIXME(solson): Major dangerous assumptions here. Ideally obliterate this - // function. - self.memory.write_primval(ptr, primval1)?; - self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?; - Ok(ptr) - } } } @@ -1006,7 +990,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Sanity-check the primval type against the input type. Value::ByVal(primval) => Ok(primval), - Value::ByValPair(..) => bug!("can't turn a ByValPair into a single PrimVal"), } } @@ -1019,14 +1002,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), - Value::ByValPair(primval1, primval2) => { - let size = self.type_size(dest_ty); - - // FIXME(solson): Major dangerous assumptions here. - self.memory.write_primval(dest, primval1)?; - self.memory.write_primval(dest.offset((size / 2) as isize), primval2)?; - Ok(()) - } } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 38b62254130f6..94ab3014ffd3f 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -7,7 +7,8 @@ use rustc::ty; use error::{EvalError, EvalResult}; use memory::Pointer; use interpreter::EvalContext; -use primval; +use primval::{self, PrimVal}; +use interpreter::value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -18,12 +19,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Pointer, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { - // TODO(solson): We can probably remove this _to_ptr easily. - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand_to_ptr(arg)) + let args_ptrs: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) .collect(); - let args_ptrs = args_res?; + let args_ptrs = args_ptrs?; let pointer_size = self.memory.pointer_size(); + let i32 = self.tcx.types.i32; + let isize = self.tcx.types.isize; + let usize = self.tcx.types.usize; + let f32 = self.tcx.types.f32; + let f64 = self.tcx.types.f64; match &self.tcx.item_name(def_id).as_str()[..] { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, @@ -31,14 +36,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, "arith_offset" => { - let ptr = self.memory.read_ptr(args_ptrs[0])?; - let offset = self.memory.read_int(args_ptrs[1], pointer_size)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; + let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); self.memory.write_ptr(dest, new_ptr)?; } "assume" => { - if !self.memory.read_bool(args_ptrs[0])? { + let bool = self.tcx.types.bool; + if !self.value_to_primval(args_ptrs[0], bool)?.expect_bool("assume arg not bool") { return Err(EvalError::AssumptionNotHeld); } } @@ -51,47 +57,59 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); - let src = self.memory.read_ptr(args_ptrs[0])?; - let dest = self.memory.read_ptr(args_ptrs[1])?; - let count = self.memory.read_isize(args_ptrs[2])?; + let src = args_ptrs[0].read_ptr(&self.memory)?; + let dest = args_ptrs[1].read_ptr(&self.memory)?; + let count = self.value_to_primval(args_ptrs[2], usize)?.expect_uint("arith_offset second arg not isize"); self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } "ctpop" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + let num = self.value_to_primval(args_ptrs[2], elem_ty)?.expect_int("ctpop second arg not integral"); + let num = num.count_ones(); self.memory.write_uint(dest, num.into(), elem_size)?; } "ctlz" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + let num = self.value_to_primval(args_ptrs[2], elem_ty)?; + let num = match num { + PrimVal::I8(i) => i.leading_zeros(), + PrimVal::U8(i) => i.leading_zeros(), + PrimVal::I16(i) => i.leading_zeros(), + PrimVal::U16(i) => i.leading_zeros(), + PrimVal::I32(i) => i.leading_zeros(), + PrimVal::U32(i) => i.leading_zeros(), + PrimVal::I64(i) => i.leading_zeros(), + PrimVal::U64(i) => i.leading_zeros(), + _ => bug!("ctlz called with non-integer type"), + }; self.memory.write_uint(dest, num.into(), elem_size)?; } "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; + let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.memory.write_uint(dest, discr_val, 8)?; } "fabsf32" => { - let f = self.memory.read_f32(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32"); self.memory.write_f32(dest, f.abs())?; } "fabsf64" => { - let f = self.memory.read_f64(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64"); self.memory.write_f64(dest, f.abs())?; } "fadd_fast" => { let ty = substs.type_at(0); - let a = self.read_primval(args_ptrs[0], ty)?; - let b = self.read_primval(args_ptrs[0], ty)?; + let a = self.value_to_primval(args_ptrs[0], ty)?; + let b = self.value_to_primval(args_ptrs[0], ty)?; let result = primval::binary_op(mir::BinOp::Add, a, b)?; self.memory.write_primval(dest, result.0)?; } @@ -117,8 +135,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], ptr, ty)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; + self.move_value(args_ptrs[1], ptr, ty)?; } "needs_drop" => { @@ -129,10 +147,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args_ptrs[0]; - let offset = self.memory.read_isize(args_ptrs[1])?; + let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("offset second arg not isize"); - let ptr = self.memory.read_ptr(ptr_arg)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); self.memory.write_ptr(dest, result_ptr)?; } @@ -150,24 +167,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "powif32" => { - let f = self.memory.read_f32(args_ptrs[0])?; - let i = self.memory.read_int(args_ptrs[1], 4)?; + let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("powif32 first arg not f32"); + let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif32 second arg not i32"); self.memory.write_f32(dest, f.powi(i as i32))?; } "powif64" => { - let f = self.memory.read_f32(args_ptrs[0])?; - let i = self.memory.read_int(args_ptrs[1], 4)?; - self.memory.write_f32(dest, f.powi(i as i32))?; + let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("powif64 first arg not f64"); + let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif64 second arg not i32"); + self.memory.write_f64(dest, f.powi(i as i32))?; } "sqrtf32" => { - let f = self.memory.read_f32(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32"); self.memory.write_f32(dest, f.sqrt())?; } "sqrtf64" => { - let f = self.memory.read_f64(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64"); self.memory.write_f64(dest, f.sqrt())?; } @@ -198,7 +215,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let ty = substs.type_at(0); - self.move_(args_ptrs[0], dest, ty)?; + self.move_value(args_ptrs[0], dest, ty)?; } "try" => unimplemented!(), @@ -207,14 +224,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "volatile_load" => { let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; self.move_(ptr, dest, ty)?; } "volatile_store" => { let ty = substs.type_at(0); - let dest = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], dest, ty)?; + let dest = args_ptrs[0].read_ptr(&self.memory)?; + self.move_value(args_ptrs[1], dest, ty)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), @@ -229,7 +246,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn size_and_align_of_dst( &self, ty: ty::Ty<'tcx>, - value: Pointer, + value: Value, ) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); if self.type_is_sized(ty) { @@ -306,8 +323,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } ty::TyTrait(..) => { - let (_, vtable) = self.get_fat_ptr(value); - let vtable = self.memory.read_ptr(vtable)?; + let vtable = value.expect_vtable(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?; let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?; @@ -317,10 +333,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty) as u64; - let (_, len_ptr) = self.get_fat_ptr(value); - let n = self.memory.read_usize(len_ptr)?; + let len = value.expect_slice_len(&self.memory)?; let align = self.type_align(elem_ty); - Ok((n * elem_size, align as u64)) + Ok((len * elem_size, align as u64)) } _ => bug!("size_of_val::<{:?}>", ty), diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 10273881b67c6..3a4e2f5ff8424 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -14,7 +14,8 @@ use syntax::{ast, attr}; use error::{EvalError, EvalResult}; use memory::Pointer; use primval::PrimVal; -use super::{EvalContext, IntegerExt, StackPopCleanup, Value}; +use super::{EvalContext, IntegerExt, StackPopCleanup}; +use super::value::Value; mod intrinsics; @@ -265,9 +266,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { None => name.as_str(), }; - // TODO(solson): We can probably remove this _to_ptr easily. - let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand_to_ptr(arg)) + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) .collect(); let args = args_res?; @@ -276,26 +276,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); } + let usize = self.tcx.types.usize; + match &link_name[..] { "__rust_allocate" => { - let size = self.memory.read_usize(args[0])?; - let align = self.memory.read_usize(args[1])?; + let size = self.value_to_primval(args[0], usize)?.expect_uint("__rust_allocate first arg not usize"); + let align = self.value_to_primval(args[1], usize)?.expect_uint("__rust_allocate second arg not usize"); let ptr = self.memory.allocate(size as usize, align as usize)?; self.memory.write_ptr(dest, ptr)?; } "__rust_reallocate" => { - let ptr = self.memory.read_ptr(args[0])?; - let size = self.memory.read_usize(args[2])?; - let align = self.memory.read_usize(args[3])?; + let ptr = args[0].read_ptr(&self.memory)?; + let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); + let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; self.memory.write_ptr(dest, new_ptr)?; } "memcmp" => { - let left = self.memory.read_ptr(args[0])?; - let right = self.memory.read_ptr(args[1])?; - let n = self.memory.read_usize(args[2])? as usize; + let left = args[0].read_ptr(&self.memory)?; + let right = args[1].read_ptr(&self.memory)?; + let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize") as usize; let result = { let left_bytes = self.memory.read_bytes(left, n)?; @@ -419,7 +421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // intermediate function call. // FIXME: this is a memory leak, should probably add the pointer to the // current stack. - let first = self.value_to_ptr(args[0].0, args[0].1)?; + let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?; args[0].0 = Value::ByVal(PrimVal::Ptr(first)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } @@ -442,11 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { - // FIXME(solson): Remove this allocating hack. - let ptr = self.value_to_ptr(*first_arg, *first_ty)?; - *first_arg = Value::ByRef(ptr); - let (_, vtable) = self.get_fat_ptr(ptr); - let vtable = self.memory.read_ptr(vtable)?; + let vtable = first_arg.expect_vtable(&self.memory)?; let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs new file mode 100644 index 0000000000000..0237afd06a34c --- /dev/null +++ b/src/interpreter/value.rs @@ -0,0 +1,60 @@ +use memory::{Memory, Pointer}; +use error::EvalResult; +use primval::PrimVal; + +/// A `Value` represents a single self-contained Rust value. +/// +/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve +/// value held directly, outside of any allocation (`ByVal`). +/// +/// For optimization of a few very common cases, there is also a representation for a pair of +/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary +/// operations and fat pointers. This idea was taken from rustc's trans. +#[derive(Clone, Copy, Debug)] +pub(super) enum Value { + ByRef(Pointer), + ByVal(PrimVal), +} + +impl Value { + pub(super) fn read_ptr<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_ptr(ptr), + ByVal(PrimVal::Ptr(ptr)) | + ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr), + ByVal(_other) => unimplemented!(), + } + } + + pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)), + ByVal(PrimVal::VtablePtr(_, vtable)) => Ok(vtable), + _ => unimplemented!(), + } + } + + pub(super) fn expect_slice_len<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), + ByVal(PrimVal::SlicePtr(_, len)) => Ok(len), + _ => unimplemented!(), + } + } + + pub(super) fn expect_fat_ptr_extra<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { + use self::Value::*; + match (*self, mem.pointer_size()) { + (ByRef(ptr), size) => mem.read_ptr(ptr.offset(size as isize)).map(PrimVal::Ptr), + (ByVal(PrimVal::SlicePtr(_, len)), 8) => Ok(PrimVal::U64(len)), + (ByVal(PrimVal::SlicePtr(_, len)), 4) => Ok(PrimVal::U32(len as u32)), + (ByVal(PrimVal::SlicePtr(_, len)), 2) => Ok(PrimVal::U16(len as u16)), + (ByVal(PrimVal::SlicePtr(_, len)), 1) => Ok(PrimVal::U8(len as u8)), + (ByVal(PrimVal::VtablePtr(_, ptr)), _) => Ok(PrimVal::Ptr(ptr)), + _ => unimplemented!(), + } + } +} diff --git a/src/memory.rs b/src/memory.rs index 27e6ffff00ebe..d1c0c7408433f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -55,6 +55,9 @@ impl Pointer { pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } + pub fn to_int(&self) -> usize { + self.offset + } pub fn from_int(i: usize) -> Self { Pointer { alloc_id: ZST_ALLOC_ID, @@ -543,6 +546,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(p) | PrimVal::Ptr(p) => self.write_ptr(ptr, p), + PrimVal::VtablePtr(p, v) => { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + self.write_ptr(ptr, p)?; + let vptr = ptr.offset(self.pointer_size() as isize); + self.write_ptr(vptr, v) + } + PrimVal::SlicePtr(p, n) => { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + self.write_ptr(ptr, p)?; + let nptr = ptr.offset(self.pointer_size() as isize); + self.write_usize(nptr, n) + } } } diff --git a/src/primval.rs b/src/primval.rs index 717ad99dbcd50..82e2616252490 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,6 +14,8 @@ pub enum PrimVal { Ptr(Pointer), FnPtr(Pointer), + VtablePtr(Pointer, Pointer), + SlicePtr(Pointer, u64), Char(char), F32(f32), F64(f64), @@ -32,7 +34,10 @@ macro_rules! declare_expect_fn { impl PrimVal { declare_expect_fn!(expect_bool, Bool, bool); + declare_expect_fn!(expect_f32, F32, f32); + declare_expect_fn!(expect_f64, F64, f64); declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer); + declare_expect_fn!(expect_ptr, Ptr, Pointer); pub fn expect_uint(self, error_msg: &str) -> u64 { use self::PrimVal::*; @@ -41,6 +46,19 @@ impl PrimVal { U16(u) => u as u64, U32(u) => u as u64, U64(u) => u, + Ptr(ptr) => ptr.to_int() as u64, + _ => bug!("{}", error_msg), + } + } + + pub fn expect_int(self, error_msg: &str) -> i64 { + use self::PrimVal::*; + match self { + I8(i) => i as i64, + I16(i) => i as i64, + I32(i) => i as i64, + I64(i) => i, + Ptr(ptr) => ptr.to_int() as i64, _ => bug!("{}", error_msg), } } diff --git a/tests/run-pass/issue-33387.rs b/tests/run-pass/issue-33387.rs new file mode 100644 index 0000000000000..edbf2b81ce941 --- /dev/null +++ b/tests/run-pass/issue-33387.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::Arc; + +trait Foo {} + +impl Foo for [u8; 2] {} + +fn main() { + let _: Arc = Arc::new([3, 4]); +} From 7714cccf2657151b634ae0cdcf908573e838e4d7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 10:38:30 +0200 Subject: [PATCH 0522/1096] implement "type_name" intrinsic --- src/interpreter/mod.rs | 47 +++++++++++++----------- src/interpreter/terminator/intrinsics.rs | 8 ++-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c30c47961daec..dcca9605b1a44 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -204,42 +204,45 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { + pub fn str_to_primval(&mut self, s: &str) -> EvalResult<'tcx, PrimVal> { + // FIXME: cache these allocs + let ptr = self.memory.allocate(s.len(), 1)?; + self.memory.write_bytes(ptr, s.as_bytes())?; + self.memory.freeze(ptr.alloc_id)?; + Ok(PrimVal::SlicePtr(ptr, s.len() as u64)) + } + + fn const_to_primval(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, PrimVal> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; let primval = match *const_val { - Integral(ConstInt::I8(i)) => Value::ByVal(PrimVal::I8(i)), - Integral(ConstInt::U8(i)) => Value::ByVal(PrimVal::U8(i)), + Integral(ConstInt::I8(i)) => PrimVal::I8(i), + Integral(ConstInt::U8(i)) => PrimVal::U8(i), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => Value::ByVal(PrimVal::I16(i)), + Integral(ConstInt::I16(i)) => PrimVal::I16(i), Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => Value::ByVal(PrimVal::U16(i)), + Integral(ConstInt::U16(i)) => PrimVal::U16(i), Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => Value::ByVal(PrimVal::I32(i)), + Integral(ConstInt::I32(i)) => PrimVal::I32(i), Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => Value::ByVal(PrimVal::U32(i)), + Integral(ConstInt::U32(i)) => PrimVal::U32(i), Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => Value::ByVal(PrimVal::I64(i)), + Integral(ConstInt::I64(i)) => PrimVal::I64(i), Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => Value::ByVal(PrimVal::U64(i)), - Float(ConstFloat::F32(f)) => Value::ByVal(PrimVal::F32(f)), - Float(ConstFloat::F64(f)) => Value::ByVal(PrimVal::F64(f)), - Bool(b) => Value::ByVal(PrimVal::Bool(b)), - Char(c) => Value::ByVal(PrimVal::Char(c)), - - Str(ref s) => { - let ptr = self.memory.allocate(s.len(), 1)?; - self.memory.write_bytes(ptr, s.as_bytes())?; - self.memory.freeze(ptr.alloc_id)?; - Value::ByVal(PrimVal::SlicePtr(ptr, s.len() as u64)) - } + Integral(ConstInt::U64(i)) => PrimVal::U64(i), + Float(ConstFloat::F32(f)) => PrimVal::F32(f), + Float(ConstFloat::F64(f)) => PrimVal::F64(f), + Bool(b) => PrimVal::Bool(b), + Char(c) => PrimVal::Char(c), + + Str(ref s) => self.str_to_primval(s)?, ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len(), 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - Value::ByVal(PrimVal::Ptr(ptr)) + PrimVal::Ptr(ptr) } Struct(_) => unimplemented!(), @@ -787,7 +790,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; let value = match *literal { - Literal::Value { ref value } => self.const_to_value(value)?, + Literal::Value { ref value } => Value::ByVal(self.const_to_primval(value)?), Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 94ab3014ffd3f..103fe111fa353 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -199,14 +199,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?; self.memory.write_uint(dest, size, pointer_size)?; } - // FIXME: wait for eval_operand_to_ptr to be gone - /* "type_name" => { let ty = substs.type_at(0); let ty_name = ty.to_string(); - let s = self.str_to_value(&ty_name)?; - self.memory.write_ptr(dest, s)?; - }*/ + let s = self.str_to_primval(&ty_name)?; + self.memory.write_primval(dest, s)?; + } "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); From d743c0784ed487c3329010b77a935eb13b538816 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 15:23:01 +0200 Subject: [PATCH 0523/1096] clean up get_fat_ptr usage in Unsize --- src/interpreter/mod.rs | 11 +++++------ src/interpreter/value.rs | 13 ------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index dcca9605b1a44..e381ebdba693b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -610,8 +610,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_ty = self.monomorphize(dest_ty, self.substs()); // FIXME: cases where dest_ty is not a fat pointer. e.g. Arc -> Arc assert!(self.type_is_fat_ptr(dest_ty)); - let (ptr, extra) = self.get_fat_ptr(dest); - self.move_value(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -620,20 +618,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - self.memory.write_usize(extra, length as u64)?; + let ptr = src.read_ptr(&self.memory)?; + self.memory.write_primval(dest, PrimVal::SlicePtr(ptr, length as u64))?; } (&ty::TyTrait(_), &ty::TyTrait(_)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - let src_extra = src.expect_fat_ptr_extra(&self.memory)?; - self.memory.write_primval(extra, src_extra)?; + self.write_value(src, dest, dest_ty)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; - self.memory.write_ptr(extra, vtable)?; + let ptr = src.read_ptr(&self.memory)?; + self.memory.write_primval(dest, PrimVal::VtablePtr(ptr, vtable))?; }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 0237afd06a34c..58805ee4c5c62 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -44,17 +44,4 @@ impl Value { _ => unimplemented!(), } } - - pub(super) fn expect_fat_ptr_extra<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { - use self::Value::*; - match (*self, mem.pointer_size()) { - (ByRef(ptr), size) => mem.read_ptr(ptr.offset(size as isize)).map(PrimVal::Ptr), - (ByVal(PrimVal::SlicePtr(_, len)), 8) => Ok(PrimVal::U64(len)), - (ByVal(PrimVal::SlicePtr(_, len)), 4) => Ok(PrimVal::U32(len as u32)), - (ByVal(PrimVal::SlicePtr(_, len)), 2) => Ok(PrimVal::U16(len as u16)), - (ByVal(PrimVal::SlicePtr(_, len)), 1) => Ok(PrimVal::U8(len as u8)), - (ByVal(PrimVal::VtablePtr(_, ptr)), _) => Ok(PrimVal::Ptr(ptr)), - _ => unimplemented!(), - } - } } From b3190359df5234e0b707f59635365be841bf8848 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 15:48:23 +0200 Subject: [PATCH 0524/1096] refactor away `get_fat_ptr` --- src/interpreter/mod.rs | 47 +++++------- tests/run-pass/dst-struct.rs | 134 +++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 tests/run-pass/dst-struct.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e381ebdba693b..9e8c2996d2655 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -579,15 +579,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let lv = self.eval_lvalue(lvalue)?; - let (ptr, extra) = self.get_fat_ptr(dest); - self.memory.write_ptr(ptr, lv.ptr)?; match lv.extra { - LvalueExtra::None => {}, + LvalueExtra::None => self.memory.write_ptr(dest, lv.ptr)?, LvalueExtra::Length(len) => { - self.memory.write_usize(extra, len)?; + self.memory.write_primval(dest, PrimVal::SlicePtr(lv.ptr, len))?; } LvalueExtra::Vtable(ptr) => { - self.memory.write_ptr(extra, ptr)?; + self.memory.write_primval(dest, PrimVal::VtablePtr(lv.ptr, ptr))?; }, LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -902,21 +900,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Deref => { - let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - let pointee_ty = self.tcx.struct_tail(pointee_ty); - let ptr = self.memory.read_ptr(base.ptr)?; - let extra = match pointee_ty.sty { - ty::TySlice(_) | ty::TyStr => { - let (_, extra) = self.get_fat_ptr(base.ptr); - let len = self.memory.read_usize(extra)?; - LvalueExtra::Length(len) - } - ty::TyTrait(_) => { - let (_, extra) = self.get_fat_ptr(base.ptr); - let vtable = self.memory.read_ptr(extra)?; - LvalueExtra::Vtable(vtable) - }, - _ => LvalueExtra::None, + let (ptr, extra) = match self.read_primval(base.ptr, base_ty)? { + PrimVal::SlicePtr(ptr, n) => (ptr, LvalueExtra::Length(n)), + PrimVal::VtablePtr(ptr, vptr) => (ptr, LvalueExtra::Vtable(vptr)), + PrimVal::Ptr(ptr) => (ptr, LvalueExtra::None), + _ => bug!("can't deref non pointer types"), }; return Ok(Lvalue { ptr: ptr, extra: extra }); } @@ -942,12 +930,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } - fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) { - assert_eq!(layout::FAT_PTR_ADDR, 0); - assert_eq!(layout::FAT_PTR_EXTRA, 1); - (ptr, ptr.offset(self.memory.pointer_size() as isize)) - } - fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } @@ -1038,12 +1020,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, &ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, + &ty::TyBox(ty) | &ty::TyRef(_, ty::TypeAndMut { ty, .. }) | &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { - PrimVal::Ptr(self.memory.read_ptr(ptr)?) + PrimVal::Ptr(p) } else { - bug!("primitive read of fat pointer type: {:?}", ty); + // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` + let extra = ptr.offset(self.memory.pointer_size() as isize); + match self.tcx.struct_tail(ty).sty { + ty::TyTrait(..) => PrimVal::VtablePtr(p, self.memory.read_ptr(extra)?), + ty::TySlice(..) | + ty::TyStr => PrimVal::SlicePtr(p, self.memory.read_usize(extra)?), + _ => bug!("unsized primval ptr read from {:?}", ty), + } } } diff --git a/tests/run-pass/dst-struct.rs b/tests/run-pass/dst-struct.rs new file mode 100644 index 0000000000000..932b571eccdbb --- /dev/null +++ b/tests/run-pass/dst-struct.rs @@ -0,0 +1,134 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![allow(unused_features)] +#![feature(box_syntax)] + +struct Fat { + f1: isize, + f2: &'static str, + ptr: T +} + +// x is a fat pointer +fn foo(x: &Fat<[isize]>) { + let y = &x.ptr; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0], 1); + assert_eq!(x.ptr[1], 2); + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); +} + +fn foo2(x: &Fat<[T]>) { + let y = &x.ptr; + let bar = Bar; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0].to_bar(), bar); + assert_eq!(x.ptr[1].to_bar(), bar); + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); +} + +fn foo3(x: &Fat>) { + let y = &x.ptr.ptr; + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); + assert_eq!(x.ptr.f1, 8); + assert_eq!(x.ptr.f2, "deep str"); + assert_eq!(x.ptr.ptr.len(), 3); + assert_eq!(y[0], 1); + assert_eq!(x.ptr.ptr[1], 2); +} + + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Bar; + +trait ToBar { + fn to_bar(&self) -> Bar; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<[isize]> = f2; + foo(f3); + let f4: &Fat<[isize]> = &f1; + foo(f4); + let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(f5); + + // With a vec of Bars. + let bar = Bar; + let f1 = Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(&f1); + let f2 = &f1; + foo2(f2); + let f3: &Fat<[Bar]> = f2; + foo2(f3); + let f4: &Fat<[Bar]> = &f1; + foo2(f4); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(f5); + + // Assignment. + let f5: &mut Fat<[isize]> = &mut Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + f5.ptr[1] = 34; + assert_eq!(f5.ptr[0], 1); + assert_eq!(f5.ptr[1], 34); + assert_eq!(f5.ptr[2], 3); + + // Zero size vec. + let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.is_empty()); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.is_empty()); + + // Deeply nested. + let f1 = Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(&f1); + let f2 = &f1; + foo3(f2); + let f3: &Fat> = f2; + foo3(f3); + let f4: &Fat> = &f1; + foo3(f4); + let f5: &Fat> = + &Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(f5); + + // Box. + let f1 = Box::new([1, 2, 3]); + assert_eq!((*f1)[1], 2); + let f2: Box<[isize]> = f1; + assert_eq!((*f2)[1], 2); + + // Nested Box. + let f1 : Box> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&*f1); + let f2 : Box> = f1; + foo(&*f2); + + // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. + let f3 : Box> = + Box::>::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }); + foo(&*f3); +} From fcbaf990f222cdfbfcd89f639793290bba1fed23 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 11:37:23 +0200 Subject: [PATCH 0525/1096] check `Pointer::to_int` for non-integer pointers --- src/interpreter/cast.rs | 11 ++++++++++- src/memory.rs | 8 ++++++-- src/primval.rs | 4 ++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 4f31cc210339c..a924d59782ad4 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -8,7 +8,7 @@ use primval::PrimVal; use memory::Pointer; use rustc::ty::Ty; -use syntax::ast; +use syntax::ast::{self, IntTy, UintTy}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { @@ -38,6 +38,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(..) | ty::TyRawPtr(_) => Ok(Ptr(ptr)), ty::TyFnPtr(_) => Ok(FnPtr(ptr)), + // FIXME: can truncation happen here? + ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)), + ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)), + ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)), + ty::TyInt(IntTy::I64) => Ok(I64(ptr.to_int()? as i64)), + ty::TyUint(UintTy::U8) => Ok(U8(ptr.to_int()? as u8)), + ty::TyUint(UintTy::U16) => Ok(U16(ptr.to_int()? as u16)), + ty::TyUint(UintTy::U32) => Ok(U32(ptr.to_int()? as u32)), + ty::TyUint(UintTy::U64) => Ok(U64(ptr.to_int()? as u64)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/memory.rs b/src/memory.rs index d1c0c7408433f..bdd9cd2fa0f4f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -55,8 +55,12 @@ impl Pointer { pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } - pub fn to_int(&self) -> usize { - self.offset + pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> { + if self.points_to_zst() { + Ok(self.offset) + } else { + Err(EvalError::ReadPointerAsBytes) + } } pub fn from_int(i: usize) -> Self { Pointer { diff --git a/src/primval.rs b/src/primval.rs index 82e2616252490..7d88e2ee7b14f 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -46,7 +46,7 @@ impl PrimVal { U16(u) => u as u64, U32(u) => u as u64, U64(u) => u, - Ptr(ptr) => ptr.to_int() as u64, + Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as u64, _ => bug!("{}", error_msg), } } @@ -58,7 +58,7 @@ impl PrimVal { I16(i) => i as i64, I32(i) => i as i64, I64(i) => i, - Ptr(ptr) => ptr.to_int() as i64, + Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as i64, _ => bug!("{}", error_msg), } } From fe614e342d6f40a7a4dc79aaf2cf349c424bceaf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 11:40:09 +0200 Subject: [PATCH 0526/1096] remove `move_value`, which is a dupe of `write_value` --- src/interpreter/mod.rs | 7 ------- src/interpreter/terminator/intrinsics.rs | 6 +++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9e8c2996d2655..1c6bd25f712ed 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -938,13 +938,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } - fn move_value(&mut self, src: Value, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { - match src { - Value::ByRef(ptr) => self.move_(ptr, dest, ty), - Value::ByVal(val) => self.memory.write_primval(dest, val), - } - } - fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 103fe111fa353..efdd47e64da68 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.move_value(args_ptrs[1], ptr, ty)?; + self.write_value(args_ptrs[1], ptr, ty)?; } "needs_drop" => { @@ -213,7 +213,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let ty = substs.type_at(0); - self.move_value(args_ptrs[0], dest, ty)?; + self.write_value(args_ptrs[0], dest, ty)?; } "try" => unimplemented!(), @@ -229,7 +229,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "volatile_store" => { let ty = substs.type_at(0); let dest = args_ptrs[0].read_ptr(&self.memory)?; - self.move_value(args_ptrs[1], dest, ty)?; + self.write_value(args_ptrs[1], dest, ty)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), From 5d1080d0caf0e334a9ec800086723ad15721b3c1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 17:49:30 +0200 Subject: [PATCH 0527/1096] refactor `Lvalue` and `PrimVal::{SlicePtr, VtablePtr}` into `Value::ByValPair` --- src/interpreter/cast.rs | 1 - src/interpreter/mod.rs | 224 +++++++++++++---------- src/interpreter/terminator/intrinsics.rs | 7 +- src/interpreter/terminator/mod.rs | 6 +- src/interpreter/value.rs | 31 +++- src/memory.rs | 14 -- src/primval.rs | 2 - 7 files changed, 159 insertions(+), 126 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index a924d59782ad4..03e62176881df 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -28,7 +28,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64(u) => self.cast_const_int(u, ty, false), FnPtr(ptr) | Ptr(ptr) => self.cast_ptr(ptr, ty), - VtablePtr(..) | SlicePtr(..) => unimplemented!(), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1c6bd25f712ed..aec16a668de75 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -101,20 +101,6 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct Lvalue { - ptr: Pointer, - extra: LvalueExtra, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum LvalueExtra { - None, - Length(u64), - Vtable(Pointer), - DowncastVariant(usize), -} - #[derive(Clone)] pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), @@ -204,15 +190,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn str_to_primval(&mut self, s: &str) -> EvalResult<'tcx, PrimVal> { + fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(PrimVal::SlicePtr(ptr, s.len() as u64)) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64))) } - fn const_to_primval(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, PrimVal> { + fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; @@ -236,7 +222,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool(b) => PrimVal::Bool(b), Char(c) => PrimVal::Char(c), - Str(ref s) => self.str_to_primval(s)?, + Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len(), 1)?; @@ -258,7 +244,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { bug!("uninferred constants only exist before typeck"), }; - Ok(primval) + Ok(Value::ByVal(primval)) } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { @@ -565,30 +551,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Len(ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); - let len = match ty.sty { - ty::TyArray(_, n) => n as u64, - ty::TySlice(_) => if let LvalueExtra::Length(n) = src.extra { - n + match ty.sty { + ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?, + ty::TySlice(_) => if let Value::ByValPair(_, len) = src { + self.memory.write_primval(dest, len)?; } else { bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); }, _ => bug!("Rvalue::Len expected array or slice, got {:?}", ty), - }; - self.memory.write_usize(dest, len)?; + } } Ref(_, _, ref lvalue) => { - let lv = self.eval_lvalue(lvalue)?; - match lv.extra { - LvalueExtra::None => self.memory.write_ptr(dest, lv.ptr)?, - LvalueExtra::Length(len) => { - self.memory.write_primval(dest, PrimVal::SlicePtr(lv.ptr, len))?; - } - LvalueExtra::Vtable(ptr) => { - self.memory.write_primval(dest, PrimVal::VtablePtr(lv.ptr, ptr))?; - }, - LvalueExtra::DowncastVariant(..) => - bug!("attempted to take a reference to an enum downcast lvalue"), + match self.eval_lvalue(lvalue)? { + Value::ByRef(ptr) => self.memory.write_ptr(dest, ptr)?, + Value::ByVal(..) => bug!("cannot take reference of immediate"), + pair @ Value::ByValPair(..) => self.write_value(pair, dest, dest_ty)?, } } @@ -617,7 +595,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - self.memory.write_primval(dest, PrimVal::SlicePtr(ptr, length as u64))?; + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_usize(dest_extra, length as u64)?; } (&ty::TyTrait(_), &ty::TyTrait(_)) => { // For now, upcasts are limited to changes in marker @@ -630,7 +611,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - self.memory.write_primval(dest, PrimVal::VtablePtr(ptr, vtable))?; + + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_ptr(dest_extra, vtable)?; }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), @@ -646,17 +631,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("misc cast: {:?}", src); let ptr_size = self.memory.pointer_size(); match (src, self.type_is_fat_ptr(dest_ty)) { - (Value::ByVal(PrimVal::VtablePtr(data, meta)), true) => { - self.memory.write_ptr(dest, data)?; - self.memory.write_ptr(dest.offset(ptr_size as isize), meta)?; + (Value::ByValPair(data, meta), true) => { + self.memory.write_primval(dest, data)?; + self.memory.write_primval(dest.offset(ptr_size as isize), meta)?; }, - (Value::ByVal(PrimVal::SlicePtr(data, meta)), true) => { - self.memory.write_ptr(dest, data)?; - self.memory.write_usize(dest.offset(ptr_size as isize), meta)?; - }, - (Value::ByVal(PrimVal::SlicePtr(data, _)), false) | - (Value::ByVal(PrimVal::VtablePtr(data, _)), false) => { - self.memory.write_ptr(dest, data)?; + (Value::ByValPair(data, _), false) => { + self.memory.write_primval(dest, data)?; }, (Value::ByRef(ptr), true) => { self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?; @@ -782,12 +762,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), + Consume(ref lvalue) => self.eval_lvalue(lvalue), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; let value = match *literal { - Literal::Value { ref value } => Value::ByVal(self.const_to_primval(value)?), + Literal::Value { ref value } => self.const_to_value(value)?, Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { @@ -822,14 +802,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Lvalue::*; - let ptr = match *lvalue { - ReturnPointer => self.frame().return_ptr - .expect("ReturnPointer used in a function with no return value"), - Arg(i) => self.frame().locals[i.index()], - Var(i) => self.frame().locals[self.frame().var_offset + i.index()], - Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], + let value = match *lvalue { + ReturnPointer => Value::ByRef(self.frame().return_ptr + .expect("ReturnPointer used in a function with no return value")), + Arg(i) => Value::ByRef(self.frame().locals[i.index()]), + Var(i) => Value::ByRef(self.frame().locals[self.frame().var_offset + i.index()]), + Temp(i) => Value::ByRef(self.frame().locals[self.frame().temp_offset + i.index()]), Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -838,25 +818,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - *self.statics.get(&cid).expect("static should have been cached (lvalue)") + Value::ByRef(*self.statics.get(&cid).expect("static should have been cached (lvalue)")) }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; trace!("projection base: {:?}", base); trace!("projection: {:?}", proj.elem); + match base { + Value::ByRef(ptr) => self.memory.dump(ptr.alloc_id), + _ => {}, + } let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, field_ty) => { + let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; let variant = match *base_layout { Univariant { ref variant, .. } => variant, General { ref variants, .. } => { - if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { - &variants[variant_idx] + if let Value::ByValPair(PrimVal::Ptr(ptr), variant_idx) = base { + // early exit, because enum variant field access is passed + // as a non-fat-pointer ByValPair + let idx = variant_idx.expect_uint("enum variant id not integral") as usize; + let offset = variants[idx].field_offset(field.index()).bytes() as isize; + return Ok(ByRef(ptr.offset(offset))); } else { bug!("field access on enum had no variant index"); } @@ -869,17 +858,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let offset = variant.field_offset(field.index()).bytes(); - let ptr = base.ptr.offset(offset as isize); - match (&field_ty.sty, base.extra) { - (&ty::TyStr, extra @ LvalueExtra::Length(_)) | - (&ty::TySlice(_), extra @ LvalueExtra::Length(_)) | - (&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue { - ptr: ptr, - extra: extra, - }), - (&ty::TyTrait(_), _) => bug!("trait field without vtable"), - _ => ptr, + let offset = variant.field_offset(field.index()).bytes() as isize; + use self::value::Value::*; + match base { + ByRef(ptr) => if self.type_is_fat_ptr(field_ty) { + self.read_value(ptr.offset(offset), field_ty)? + } else { + ByRef(ptr.offset(offset)) + }, + // indexing into a field of an unsized struct + ByValPair(PrimVal::Ptr(ptr), extra) => if self.type_is_sized(field_ty) { + ByRef(ptr.offset(offset)) + } else { + ByValPair(PrimVal::Ptr(ptr.offset(offset)), extra) + }, + other => bug!("expected thin ptr, got: {:?}", other), } }, @@ -887,10 +880,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *base_layout { General { ref variants, .. } => { - return Ok(Lvalue { - ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), - extra: LvalueExtra::DowncastVariant(variant), - }); + use self::value::Value::*; + match base { + ByRef(ptr) => return Ok(ByValPair( + PrimVal::Ptr(ptr.offset(variants[variant].field_offset(1).bytes() as isize)), + PrimVal::U64(variant as u64), + )), + other => bug!("bad downcast base: {:?}", other), + } } RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { return Ok(base); @@ -899,15 +896,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, - Deref => { - let (ptr, extra) = match self.read_primval(base.ptr, base_ty)? { - PrimVal::SlicePtr(ptr, n) => (ptr, LvalueExtra::Length(n)), - PrimVal::VtablePtr(ptr, vptr) => (ptr, LvalueExtra::Vtable(vptr)), - PrimVal::Ptr(ptr) => (ptr, LvalueExtra::None), - _ => bug!("can't deref non pointer types"), - }; - return Ok(Lvalue { ptr: ptr, extra: extra }); - } + Deref => match self.follow_ref(base, base_ty)? { + Value::ByRef(..) => bug!("follow_ref broken"), + // magical deref + Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr), + Value::ByVal(..) => bug!("can't deref non pointer types"), + // deref ops on fat pointers are no-ops + pair @ Value::ByValPair(..) => pair, + }, Index(ref operand) => { let elem_size = match base_ty.sty { @@ -918,7 +914,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); - base.ptr.offset(n as isize * elem_size as isize) + match base { + Value::ByRef(ptr) | + Value::ByValPair(PrimVal::Ptr(ptr), _) | + Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr.offset(n as isize * elem_size as isize)), + other => bug!("index op on {:?}", other), + } } ConstantIndex { .. } => unimplemented!(), @@ -926,8 +927,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } }; - - Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) + Ok(value) } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { @@ -958,15 +958,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(ptr, primval)?; Ok(ptr) } + + Value::ByValPair(a, b) => { + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + let ptr_size = self.memory.pointer_size() as isize; + self.memory.write_primval(ptr, a)?; + self.memory.write_primval(ptr.offset(ptr_size), b)?; + Ok(ptr) + } } } fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match value { - Value::ByRef(ptr) => self.read_primval(ptr, ty), + Value::ByRef(ptr) => match self.read_value(ptr, ty)? { + Value::ByRef(_) => bug!("read_value can't result in `ByRef`"), + Value::ByVal(primval) => Ok(primval), + Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), + }, // TODO(solson): Sanity-check the primval type against the input type. Value::ByVal(primval) => Ok(primval), + Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), } } @@ -979,10 +994,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), + Value::ByValPair(a, b) => { + self.memory.write_primval(dest, a)?; + let extra_dest = dest.offset(self.memory.pointer_size() as isize); + self.memory.write_primval(extra_dest, b) + } + } + } + + // ensures that this value isn't a `ByRef` anymore + fn follow_ref(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + match value { + Value::ByRef(ptr) => self.read_value(ptr, ty), + other => Ok(other), } } - pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match &ty.sty { &ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), @@ -1022,12 +1050,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` let extra = ptr.offset(self.memory.pointer_size() as isize); - match self.tcx.struct_tail(ty).sty { - ty::TyTrait(..) => PrimVal::VtablePtr(p, self.memory.read_ptr(extra)?), + let extra = match self.tcx.struct_tail(ty).sty { + ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => PrimVal::SlicePtr(p, self.memory.read_usize(extra)?), + ty::TyStr => self.target_usize_primval(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), - } + }; + return Ok(Value::ByValPair(PrimVal::Ptr(p), extra)); } } @@ -1052,7 +1081,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("primitive read of non-primitive type: {:?}", ty), }; - Ok(val) + Ok(Value::ByVal(val)) } fn frame(&self) -> &Frame<'a, 'tcx> { @@ -1083,13 +1112,6 @@ fn pointee_type(ptr_ty: ty::Ty) -> Option { } } -impl Lvalue { - fn to_ptr(self) -> Pointer { - assert_eq!(self.extra, LvalueExtra::None); - self.ptr - } -} - impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index efdd47e64da68..c025852bc5e45 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -2,7 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::mir::repr as mir; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; -use rustc::ty; +use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use memory::Pointer; @@ -17,6 +17,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], dest: Pointer, + dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { let args_ptrs: EvalResult> = args.iter() @@ -202,8 +203,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_name" => { let ty = substs.type_at(0); let ty_name = ty.to_string(); - let s = self.str_to_primval(&ty_name)?; - self.memory.write_primval(dest, s)?; + let s = self.str_to_value(&ty_name)?; + self.write_value(s, dest, dest_ty)?; } "type_id" => { let ty = substs.type_at(0); diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 3a4e2f5ff8424..c4415415f6e4b 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -43,13 +43,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + let discr_ptr = self.eval_lvalue(discr)?; let discr_ty = self.lvalue_ty(discr); let discr_size = self .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; - let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; + let discr_val = discr_ptr.read_uint(&self.memory, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { return Err(EvalError::InvalidChar(discr_val as u64)); @@ -165,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); let (ret, target) = destination.unwrap(); - self.call_intrinsic(def_id, substs, arg_operands, ret, layout)?; + self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout)?; self.goto_block(target); Ok(()) } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 58805ee4c5c62..6512fe9b15941 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -14,24 +14,48 @@ use primval::PrimVal; pub(super) enum Value { ByRef(Pointer), ByVal(PrimVal), + ByValPair(PrimVal, PrimVal), } impl Value { + pub(super) fn read_ptr<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), ByVal(PrimVal::Ptr(ptr)) | ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr), + ByValPair(..) => unimplemented!(), + ByVal(_other) => unimplemented!(), + } + } + + pub(super) fn read_uint<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>, size: usize) -> EvalResult<'tcx, u64> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_uint(ptr, size), + ByVal(PrimVal::U8(u)) => Ok(u as u64), + ByVal(PrimVal::U16(u)) => Ok(u as u64), + ByVal(PrimVal::U32(u)) => Ok(u as u64), + ByVal(PrimVal::U64(u)) => Ok(u as u64), + ByValPair(..) => unimplemented!(), ByVal(_other) => unimplemented!(), } } + pub(super) fn to_ptr(&self) -> Pointer { + use self::Value::*; + match *self { + ByRef(ptr) => ptr, + other => bug!("expected pointer, got {:?}", other), + } + } + pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)), - ByVal(PrimVal::VtablePtr(_, vtable)) => Ok(vtable), + ByValPair(_, PrimVal::Ptr(vtable)) => Ok(vtable), _ => unimplemented!(), } } @@ -40,7 +64,10 @@ impl Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByVal(PrimVal::SlicePtr(_, len)) => Ok(len), + ByValPair(_, PrimVal::U8(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U16(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U32(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U64(len)) => Ok(len), _ => unimplemented!(), } } diff --git a/src/memory.rs b/src/memory.rs index bdd9cd2fa0f4f..eeae014c6da1c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -550,20 +550,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(p) | PrimVal::Ptr(p) => self.write_ptr(ptr, p), - PrimVal::VtablePtr(p, v) => { - assert_eq!(layout::FAT_PTR_ADDR, 0); - assert_eq!(layout::FAT_PTR_EXTRA, 1); - self.write_ptr(ptr, p)?; - let vptr = ptr.offset(self.pointer_size() as isize); - self.write_ptr(vptr, v) - } - PrimVal::SlicePtr(p, n) => { - assert_eq!(layout::FAT_PTR_ADDR, 0); - assert_eq!(layout::FAT_PTR_EXTRA, 1); - self.write_ptr(ptr, p)?; - let nptr = ptr.offset(self.pointer_size() as isize); - self.write_usize(nptr, n) - } } } diff --git a/src/primval.rs b/src/primval.rs index 7d88e2ee7b14f..70934c2a549ac 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,8 +14,6 @@ pub enum PrimVal { Ptr(Pointer), FnPtr(Pointer), - VtablePtr(Pointer, Pointer), - SlicePtr(Pointer, u64), Char(char), F32(f32), F64(f64), From e28f87375660ca0491a57bc3af0e3da98a9cf270 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 17:49:53 +0200 Subject: [PATCH 0528/1096] actually error on failing miri-pass tests + remove a test that never succeeded --- tests/compiletest.rs | 1 + tests/run-pass/issue-33387.rs | 19 ------------------- 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 tests/run-pass/issue-33387.rs diff --git a/tests/compiletest.rs b/tests/compiletest.rs index bc112f4068546..6870161e7682a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -105,6 +105,7 @@ fn compile_test() { writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + panic!("failed to run test"); } } Err(e) => { diff --git a/tests/run-pass/issue-33387.rs b/tests/run-pass/issue-33387.rs deleted file mode 100644 index edbf2b81ce941..0000000000000 --- a/tests/run-pass/issue-33387.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::sync::Arc; - -trait Foo {} - -impl Foo for [u8; 2] {} - -fn main() { - let _: Arc = Arc::new([3, 4]); -} From d2d73a908dce98143b610ac154b7a2e35ad32847 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:14:53 +0200 Subject: [PATCH 0529/1096] reintroduce Lvalue and LvalueExtra --- src/interpreter/mod.rs | 151 +++++++++++++++--------------- src/interpreter/terminator/mod.rs | 4 +- src/interpreter/value.rs | 21 ----- 3 files changed, 80 insertions(+), 96 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index aec16a668de75..96cc8a4e8b9d7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -101,6 +101,20 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct Lvalue { + ptr: Pointer, + extra: LvalueExtra, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum LvalueExtra { + None, + Length(u64), + Vtable(Pointer), + DowncastVariant(usize), +} + #[derive(Clone)] pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), @@ -553,8 +567,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.lvalue_ty(lvalue); match ty.sty { ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?, - ty::TySlice(_) => if let Value::ByValPair(_, len) = src { - self.memory.write_primval(dest, len)?; + ty::TySlice(_) => if let LvalueExtra::Length(len) = src.extra { + self.memory.write_usize(dest, len)?; } else { bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); }, @@ -563,10 +577,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ref(_, _, ref lvalue) => { - match self.eval_lvalue(lvalue)? { - Value::ByRef(ptr) => self.memory.write_ptr(dest, ptr)?, - Value::ByVal(..) => bug!("cannot take reference of immediate"), - pair @ Value::ByValPair(..) => self.write_value(pair, dest, dest_ty)?, + let lvalue = self.eval_lvalue(lvalue)?; + self.memory.write_ptr(dest, lvalue.ptr)?; + let extra_ptr = dest.offset(self.memory.pointer_size() as isize); + match lvalue.extra { + LvalueExtra::None => {}, + LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?, + LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?, + LvalueExtra::DowncastVariant(..) => + bug!("attempted to take a reference to an enum downcast lvalue"), } } @@ -762,7 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.eval_lvalue(lvalue), + Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; @@ -802,14 +821,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; - let value = match *lvalue { - ReturnPointer => Value::ByRef(self.frame().return_ptr - .expect("ReturnPointer used in a function with no return value")), - Arg(i) => Value::ByRef(self.frame().locals[i.index()]), - Var(i) => Value::ByRef(self.frame().locals[self.frame().var_offset + i.index()]), - Temp(i) => Value::ByRef(self.frame().locals[self.frame().temp_offset + i.index()]), + let ptr = match *lvalue { + ReturnPointer => self.frame().return_ptr + .expect("ReturnPointer used in a function with no return value"), + Arg(i) => self.frame().locals[i.index()], + Var(i) => self.frame().locals[self.frame().var_offset + i.index()], + Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -818,17 +837,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - Value::ByRef(*self.statics.get(&cid).expect("static should have been cached (lvalue)")) + *self.statics.get(&cid).expect("static should have been cached (lvalue)") }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; - trace!("projection base: {:?}", base); - trace!("projection: {:?}", proj.elem); - match base { - Value::ByRef(ptr) => self.memory.dump(ptr.alloc_id), - _ => {}, - } let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -840,12 +853,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let variant = match *base_layout { Univariant { ref variant, .. } => variant, General { ref variants, .. } => { - if let Value::ByValPair(PrimVal::Ptr(ptr), variant_idx) = base { - // early exit, because enum variant field access is passed - // as a non-fat-pointer ByValPair - let idx = variant_idx.expect_uint("enum variant id not integral") as usize; - let offset = variants[idx].field_offset(field.index()).bytes() as isize; - return Ok(ByRef(ptr.offset(offset))); + if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { + &variants[variant_idx] } else { bug!("field access on enum had no variant index"); } @@ -858,21 +867,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let offset = variant.field_offset(field.index()).bytes() as isize; - use self::value::Value::*; - match base { - ByRef(ptr) => if self.type_is_fat_ptr(field_ty) { - self.read_value(ptr.offset(offset), field_ty)? - } else { - ByRef(ptr.offset(offset)) - }, - // indexing into a field of an unsized struct - ByValPair(PrimVal::Ptr(ptr), extra) => if self.type_is_sized(field_ty) { - ByRef(ptr.offset(offset)) - } else { - ByValPair(PrimVal::Ptr(ptr.offset(offset)), extra) - }, - other => bug!("expected thin ptr, got: {:?}", other), + let offset = variant.field_offset(field.index()).bytes(); + let ptr = base.ptr.offset(offset as isize); + trace!("{:?}", base); + trace!("{:?}", field_ty); + if self.type_is_sized(field_ty) { + ptr + } else { + match base.extra { + LvalueExtra::None => bug!("expected fat pointer"), + LvalueExtra::DowncastVariant(..) => bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::Vtable(_) | + LvalueExtra::Length(_) => {}, + } + return Ok(Lvalue { + ptr: ptr, + extra: base.extra, + }); } }, @@ -880,14 +891,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *base_layout { General { ref variants, .. } => { - use self::value::Value::*; - match base { - ByRef(ptr) => return Ok(ByValPair( - PrimVal::Ptr(ptr.offset(variants[variant].field_offset(1).bytes() as isize)), - PrimVal::U64(variant as u64), - )), - other => bug!("bad downcast base: {:?}", other), - } + return Ok(Lvalue { + ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), + extra: LvalueExtra::DowncastVariant(variant), + }); } RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { return Ok(base); @@ -896,14 +903,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, - Deref => match self.follow_ref(base, base_ty)? { - Value::ByRef(..) => bug!("follow_ref broken"), - // magical deref - Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr), - Value::ByVal(..) => bug!("can't deref non pointer types"), - // deref ops on fat pointers are no-ops - pair @ Value::ByValPair(..) => pair, - }, + Deref => { + use primval::PrimVal::*; + use interpreter::value::Value::*; + let (ptr, extra) = match self.read_value(base.ptr, base_ty)? { + ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), + ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), + ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None), + _ => bug!("can't deref non pointer types"), + }; + return Ok(Lvalue { ptr: ptr, extra: extra }); + } Index(ref operand) => { let elem_size = match base_ty.sty { @@ -914,12 +924,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); - match base { - Value::ByRef(ptr) | - Value::ByValPair(PrimVal::Ptr(ptr), _) | - Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr.offset(n as isize * elem_size as isize)), - other => bug!("index op on {:?}", other), - } + base.ptr.offset(n as isize * elem_size as isize) } ConstantIndex { .. } => unimplemented!(), @@ -927,7 +932,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } }; - Ok(value) + + Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { @@ -1002,14 +1008,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // ensures that this value isn't a `ByRef` anymore - fn follow_ref(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - match value { - Value::ByRef(ptr) => self.read_value(ptr, ty), - other => Ok(other), - } - } - fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match &ty.sty { @@ -1112,6 +1110,13 @@ fn pointee_type(ptr_ty: ty::Ty) -> Option { } } +impl Lvalue { + fn to_ptr(self) -> Pointer { + assert_eq!(self.extra, LvalueExtra::None); + self.ptr + } +} + impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index c4415415f6e4b..6e2f138119369 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -43,13 +43,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?; + let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_ty = self.lvalue_ty(discr); let discr_size = self .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; - let discr_val = discr_ptr.read_uint(&self.memory, discr_size)?; + let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { return Err(EvalError::InvalidChar(discr_val as u64)); diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 6512fe9b15941..87a0e15cf75e7 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -30,27 +30,6 @@ impl Value { } } - pub(super) fn read_uint<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>, size: usize) -> EvalResult<'tcx, u64> { - use self::Value::*; - match *self { - ByRef(ptr) => mem.read_uint(ptr, size), - ByVal(PrimVal::U8(u)) => Ok(u as u64), - ByVal(PrimVal::U16(u)) => Ok(u as u64), - ByVal(PrimVal::U32(u)) => Ok(u as u64), - ByVal(PrimVal::U64(u)) => Ok(u as u64), - ByValPair(..) => unimplemented!(), - ByVal(_other) => unimplemented!(), - } - } - - pub(super) fn to_ptr(&self) -> Pointer { - use self::Value::*; - match *self { - ByRef(ptr) => ptr, - other => bug!("expected pointer, got {:?}", other), - } - } - pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { From 7b70f4fe2cfb49acd98ff295264d47de0456dc42 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:33:47 +0200 Subject: [PATCH 0530/1096] typecheck `write_value` for `ByValPair` --- src/interpreter/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 96cc8a4e8b9d7..bdd812782df2d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1002,7 +1002,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(primval) => self.memory.write_primval(dest, primval), Value::ByValPair(a, b) => { self.memory.write_primval(dest, a)?; - let extra_dest = dest.offset(self.memory.pointer_size() as isize); + let layout = self.type_layout(dest_ty); + let offset = match *layout { + Layout::Univariant { ref variant, .. } => { + bug!("I don't think this can ever happen until we have custom fat pointers"); + //variant.field_offset(1).bytes() as isize + }, + Layout::FatPointer { .. } => self.memory.pointer_size() as isize, + _ => bug!("tried to write value pair of non-fat pointer type: {:?}", layout), + }; + let extra_dest = dest.offset(offset); self.memory.write_primval(extra_dest, b) } } From 055c1b14f753cb4cb1099596f668393e4c9bbb3f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:36:44 +0200 Subject: [PATCH 0531/1096] remove FIXME cleared up in the PR --- src/interpreter/cast.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 03e62176881df..f53d22699ca07 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -37,7 +37,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(..) | ty::TyRawPtr(_) => Ok(Ptr(ptr)), ty::TyFnPtr(_) => Ok(FnPtr(ptr)), - // FIXME: can truncation happen here? ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)), ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)), ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)), From 7e29392ac4114029a589b42cc117266a99da61e3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:39:14 +0200 Subject: [PATCH 0532/1096] fix warnings --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bdd812782df2d..c9b51a6213a2d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1004,7 +1004,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(dest, a)?; let layout = self.type_layout(dest_ty); let offset = match *layout { - Layout::Univariant { ref variant, .. } => { + Layout::Univariant { .. } => { bug!("I don't think this can ever happen until we have custom fat pointers"); //variant.field_offset(1).bytes() as isize }, From d6f1ba89ce7ebb7589e69903b6f3c92fb9b5d2fa Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 11:10:25 +0200 Subject: [PATCH 0533/1096] fix matching on chars fixes #63 --- src/interpreter/terminator/mod.rs | 21 +++++---------------- tests/run-pass/match_slice.rs | 8 ++++++++ tests/run-pass/rust-lang-org.rs | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 tests/run-pass/match_slice.rs create mode 100644 tests/run-pass/rust-lang-org.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 6e2f138119369..2189a15cfafff 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -1,5 +1,4 @@ use rustc::hir::def_id::DefId; -use rustc::middle::const_val::ConstVal; use rustc::mir::repr as mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; @@ -45,26 +44,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_ty = self.lvalue_ty(discr); - let discr_size = self - .type_layout(discr_ty) - .size(&self.tcx.data_layout) - .bytes() as usize; - let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; - if let ty::TyChar = discr_ty.sty { - if ::std::char::from_u32(discr_val as u32).is_none() { - return Err(EvalError::InvalidChar(discr_val as u64)); - } - } + let discr_val = self.read_value(discr_ptr, discr_ty)?; + let discr_prim = self.value_to_primval(discr_val, discr_ty)?; // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; for (index, const_val) in values.iter().enumerate() { - let val = match const_val { - &ConstVal::Integral(i) => i.to_u64_unchecked(), - _ => bug!("TerminatorKind::SwitchInt branch constant was not an integer"), - }; - if discr_val == val { + let val = self.const_to_value(const_val)?; + let prim = self.value_to_primval(val, discr_ty)?; + if discr_prim == prim { target_block = targets[index]; break; } diff --git a/tests/run-pass/match_slice.rs b/tests/run-pass/match_slice.rs new file mode 100644 index 0000000000000..568a1a1c88182 --- /dev/null +++ b/tests/run-pass/match_slice.rs @@ -0,0 +1,8 @@ +fn main() { + let x = "hello"; + match x { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} diff --git a/tests/run-pass/rust-lang-org.rs b/tests/run-pass/rust-lang-org.rs new file mode 100644 index 0000000000000..7ba68e6b239c0 --- /dev/null +++ b/tests/run-pass/rust-lang-org.rs @@ -0,0 +1,21 @@ +// This code is editable and runnable! +fn main() { + // A simple integer calculator: + // `+` or `-` means add or subtract by 1 + // `*` or `/` means multiply or divide by 2 + + let program = "+ + * - /"; + let mut accumulator = 0; + + for token in program.chars() { + match token { + '+' => accumulator += 1, + '-' => accumulator -= 1, + '*' => accumulator *= 2, + '/' => accumulator /= 2, + _ => { /* ignore everything else */ } + } + } + + assert_eq!(accumulator, 1); +} From 69aeaea01f9dfd9f0b0f11acb8619246f2928acf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 16:59:48 +0200 Subject: [PATCH 0534/1096] rustup --- src/interpreter/vtable.rs | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 726c6ce3913a3..54001bbc71226 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -112,35 +112,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.populate_implementations_for_trait_if_necessary(trait_id); - let trait_item_def_ids = self.tcx.trait_item_def_ids(trait_id); + let trait_item_def_ids = self.tcx.impl_or_trait_items(trait_id); trait_item_def_ids .iter() // Filter out non-method items. - .filter_map(|item_def_id| { - match *item_def_id { - ty::MethodTraitItemId(def_id) => Some(def_id), - _ => None, - } - }) - - // Now produce pointers for each remaining method. If the - // method could never be called from this object, just supply - // null. - .map(|trait_method_def_id| { + .filter_map(|&trait_method_def_id| { + let trait_method_type = match self.tcx.impl_or_trait_item(trait_method_def_id) { + ty::MethodTraitItem(trait_method_type) => trait_method_type, + _ => return None, + }; debug!("get_vtable_methods: trait_method_def_id={:?}", trait_method_def_id); - let trait_method_type = match self.tcx.impl_or_trait_item(trait_method_def_id) { - ty::MethodTraitItem(m) => m, - _ => bug!("should be a method, not other assoc item"), - }; let name = trait_method_type.name; // Some methods cannot be called on an object; skip those. if !self.tcx.is_vtable_safe_method(trait_id, &trait_method_type) { debug!("get_vtable_methods: not vtable safe"); - return None; + return Some(None); } debug!("get_vtable_methods: trait_method_type={:?}", @@ -167,11 +157,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let predicates = mth.method.predicates.predicates.subst(self.tcx, mth.substs); if !self.normalize_and_test_predicates(predicates) { debug!("get_vtable_methods: predicates do not hold"); - return None; + return Some(None); } } - Some(mth) + Some(Some(mth)) }) .collect() } From db8185e4398f8ae1adfb97c325eb5d2769b1ee2f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 17:01:06 +0200 Subject: [PATCH 0535/1096] print stacktrace when miri can't find the MIR for something --- src/error.rs | 4 ++++ src/interpreter/mod.rs | 21 ++++++++++++--------- src/interpreter/step.rs | 2 +- src/interpreter/terminator/mod.rs | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/error.rs b/src/error.rs index fde3db15968cf..a465ad62b9d42 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ use syntax::codemap::Span; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), + NoMirFor(String), DanglingPointerDeref, InvalidMemoryAccess, InvalidFunctionPointer, @@ -82,6 +83,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::NoMirFor(..) => + "mir not found", EvalError::InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", EvalError::OutOfMemory{..} => @@ -113,6 +116,7 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}", ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) }, + EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), EvalError::FunctionPointerTyMismatch(expected, got) => write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c9b51a6213a2d..14f522d33705f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -267,22 +267,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - pub fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> { + pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, CachedMir<'a, 'tcx>> { + trace!("load mir {:?}", def_id); if def_id.is_local() { - CachedMir::Ref(self.mir_map.map.get(&def_id).unwrap()) + Ok(CachedMir::Ref(self.mir_map.map.get(&def_id).unwrap())) } else { let mut mir_cache = self.mir_cache.borrow_mut(); if let Some(mir) = mir_cache.get(&def_id) { - return CachedMir::Owned(mir.clone()); + return Ok(CachedMir::Owned(mir.clone())); } let cs = &self.tcx.sess.cstore; - let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap_or_else(|| { - panic!("no mir for `{}`", self.tcx.item_path_str(def_id)); - }); - let cached = Rc::new(mir); - mir_cache.insert(def_id, cached.clone()); - CachedMir::Owned(cached) + match cs.maybe_get_item_mir(self.tcx, def_id) { + Some(mir) => { + let cached = Rc::new(mir); + mir_cache.insert(def_id, cached.clone()); + Ok(CachedMir::Owned(cached)) + }, + None => Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id))), + } } } diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index f87f5e43a9605..beecece0cb9d2 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -125,8 +125,8 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { if self.ecx.statics.contains_key(&cid) { return; } - let mir = self.ecx.load_mir(def_id); self.try(|this| { + let mir = this.ecx.load_mir(def_id)?; let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 2189a15cfafff..cd024d4c007dc 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -184,7 +184,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs) }; - let mir = self.load_mir(resolved_def_id); + let mir = self.load_mir(resolved_def_id)?; let (return_ptr, return_to_block) = match destination { Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), None => (None, StackPopCleanup::None), From f4516e738b1e697aa940706ff32ca4bd96bf763a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 17:02:04 +0200 Subject: [PATCH 0536/1096] be able to find statics in other crates --- benches/helpers/miri_helper.rs | 2 +- src/bin/miri.rs | 2 +- src/interpreter/mod.rs | 16 +++++++++++++--- src/interpreter/step.rs | 23 +++++++++++++++-------- tests/compile-fail/rc.rs | 15 +++++++++++++++ 5 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 tests/compile-fail/rc.rs diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index cdd1412204f99..ff4087cb14abd 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -58,7 +58,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id); }); + bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id, state.session); }); state.session.abort_if_errors(); }); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index cc0132e4090d9..8bf6adde14b1d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -75,7 +75,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { mir_map_copy.map.insert(def_id, mir_map.map.get(&def_id).unwrap().clone()); } run_mir_passes(tcx, &mut mir_map_copy); - eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit); + eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit, state.session); state.session.abort_if_errors(); }); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 14f522d33705f..e27ea18e2f499 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -6,6 +6,7 @@ use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::session::Session; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; @@ -31,6 +32,10 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, + /// The Session, from rustc. + /// Used to extract info from other crates + session: &'a Session, + /// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items. mir_map: &'a MirMap<'tcx>, @@ -154,7 +159,7 @@ pub enum StackPopCleanup { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize, session: &'a Session) -> Self { EvalContext { tcx: tcx, mir_map: mir_map, @@ -163,6 +168,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { statics: HashMap::new(), stack: Vec::new(), stack_limit: stack_limit, + session: session, } } @@ -522,7 +528,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .chain(nonnull.offset_after_field.iter().map(|s| s.bytes())); try!(self.assign_fields(dest, offsets, operands)); } else { - assert_eq!(operands.len(), 0); + for operand in operands { + let operand_ty = self.operand_ty(operand); + assert_eq!(self.type_size(operand_ty), 0); + } let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; let dest = dest.offset(offset.bytes() as isize); try!(self.memory.write_isize(dest, 0)); @@ -1146,9 +1155,10 @@ pub fn eval_main<'a, 'tcx: 'a>( memory_size: usize, step_limit: u64, stack_limit: usize, + session: &'a Session, ) { let mir = mir_map.map.get(&def_id).expect("no mir for main function"); - let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); + let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit, session); let substs = subst::Substs::empty(tcx); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index beecece0cb9d2..cd1e9a1811743 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -200,18 +200,25 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if let mir::Lvalue::Static(def_id) = *lvalue { let substs = subst::Substs::empty(self.ecx.tcx); let span = self.span; - let node_item = self.ecx.tcx.map.get_if_local(def_id).expect("static not found"); - if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { - if let hir::ItemStatic(_, m, _) = *node { - self.global_item(def_id, substs, span, m == hir::MutImmutable); - return; + if let Some(node_item) = self.ecx.tcx.map.get_if_local(def_id) { + if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { + if let hir::ItemStatic(_, m, _) = *node { + self.global_item(def_id, substs, span, m == hir::MutImmutable); + return; + } else { + bug!("static def id doesn't point to static"); + } } else { - bug!("static def id doesn't point to static"); + bug!("static def id doesn't point to item"); } } else { - bug!("static def id doesn't point to item"); + let def = self.ecx.session.cstore.describe_def(def_id).expect("static not found"); + if let hir::def::Def::Static(_, mutable) = def { + self.global_item(def_id, substs, span, !mutable); + } else { + bug!("static found but isn't a static: {:?}", def); + } } - self.global_item(def_id, substs, span, false); } } } diff --git a/tests/compile-fail/rc.rs b/tests/compile-fail/rc.rs new file mode 100644 index 0000000000000..001dec5b42747 --- /dev/null +++ b/tests/compile-fail/rc.rs @@ -0,0 +1,15 @@ +//error-pattern: no mir for `std::result::unwrap_failed::__STATIC_FMTSTR` + +use std::cell::RefCell; +use std::rc::Rc; + +fn rc_refcell() -> i32 { + let r = Rc::new(RefCell::new(42)); + *r.borrow_mut() += 10; + let x = *r.borrow(); + x +} + +fn main() { + rc_refcell(); +} From 622d407e0ef6d8f605167e1190d79d946a399bd6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 17:02:24 +0200 Subject: [PATCH 0537/1096] don't abort on the first failed test --- tests/compiletest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 6870161e7682a..e5058df95ea5d 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -105,7 +105,6 @@ fn compile_test() { writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); - panic!("failed to run test"); } } Err(e) => { @@ -116,5 +115,6 @@ fn compile_test() { } let stderr = std::io::stderr(); writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); + assert_eq!(failed, 0, "some tests failed"); }); } From 5b89f3fb942e46cd421f1c9dd80d34dd8944d6eb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 18:01:33 +0200 Subject: [PATCH 0538/1096] implement Arc -> Arc unsizing --- src/interpreter/mod.rs | 144 ++++++++++++++++++++++------------ tests/run-pass/issue-33387.rs | 19 +++++ 2 files changed, 112 insertions(+), 51 deletions(-) create mode 100644 tests/run-pass/issue-33387.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e27ea18e2f499..8fb899572c4e3 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -295,6 +295,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub fn monomorphize_field_ty(&self, f: ty::FieldDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { + let substituted = &f.ty(self.tcx, substs); + self.tcx.normalize_associated_type(&substituted) + } + pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, substs); self.tcx.normalize_associated_type(&substituted) @@ -608,56 +613,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_ptr(dest, ptr)?; } - Cast(kind, ref operand, dest_ty) => { + Cast(kind, ref operand, cast_ty) => { + debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); use rustc::mir::repr::CastKind::*; match kind { Unsize => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - let dest_ty = self.monomorphize(dest_ty, self.substs()); - // FIXME: cases where dest_ty is not a fat pointer. e.g. Arc -> Arc - assert!(self.type_is_fat_ptr(dest_ty)); - let src_pointee_ty = pointee_type(src_ty).unwrap(); - let dest_pointee_ty = pointee_type(dest_ty).unwrap(); - - // A -> A conversion - let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(src_pointee_ty, dest_pointee_ty); - - match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { - (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let ptr = src.read_ptr(&self.memory)?; - self.memory.write_ptr(dest, ptr)?; - let ptr_size = self.memory.pointer_size() as isize; - let dest_extra = dest.offset(ptr_size); - self.memory.write_usize(dest_extra, length as u64)?; - } - (&ty::TyTrait(_), &ty::TyTrait(_)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - self.write_value(src, dest, dest_ty)?; - }, - (_, &ty::TyTrait(ref data)) => { - let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); - let trait_ref = self.tcx.erase_regions(&trait_ref); - let vtable = self.get_vtable(trait_ref)?; - let ptr = src.read_ptr(&self.memory)?; - - self.memory.write_ptr(dest, ptr)?; - let ptr_size = self.memory.pointer_size() as isize; - let dest_extra = dest.offset(ptr_size); - self.memory.write_ptr(dest_extra, vtable)?; - }, - - _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), - } + self.unsize_into(src, src_ty, dest, dest_ty)?; } Misc => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - // FIXME: dest_ty should already be monomorphized - let dest_ty = self.monomorphize(dest_ty, self.substs()); if self.type_is_fat_ptr(src_ty) { trace!("misc cast: {:?}", src); let ptr_size = self.memory.pointer_size(); @@ -772,9 +740,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *layout { - Univariant { .. } => { - assert_eq!(field_index, 0); - Ok(Size::from_bytes(0)) + Univariant { ref variant, .. } => { + Ok(variant.field_offset(field_index)) } FatPointer { .. } => { let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size(); @@ -1118,16 +1085,91 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn substs(&self) -> &'tcx Substs<'tcx> { self.frame().substs } -} -fn pointee_type(ptr_ty: ty::Ty) -> Option { - match ptr_ty.sty { - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyBox(ty) => { - Some(ty) + fn unsize_into( + &mut self, + src: Value, + src_ty: Ty<'tcx>, + dest: Pointer, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, ()> { + match (&src_ty.sty, &dest_ty.sty) { + (&ty::TyBox(sty), &ty::TyBox(dty)) | + (&ty::TyRef(_, ty::TypeAndMut { ty: sty, .. }), &ty::TyRef(_, ty::TypeAndMut { ty: dty, .. })) | + (&ty::TyRef(_, ty::TypeAndMut { ty: sty, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: dty, .. })) | + (&ty::TyRawPtr(ty::TypeAndMut { ty: sty, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: dty, .. })) => { + // A -> A conversion + let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty); + + match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { + (&ty::TyArray(_, length), &ty::TySlice(_)) => { + let ptr = src.read_ptr(&self.memory)?; + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_usize(dest_extra, length as u64)?; + } + (&ty::TyTrait(_), &ty::TyTrait(_)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + self.write_value(src, dest, dest_ty)?; + }, + (_, &ty::TyTrait(ref data)) => { + let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); + let trait_ref = self.tcx.erase_regions(&trait_ref); + let vtable = self.get_vtable(trait_ref)?; + let ptr = src.read_ptr(&self.memory)?; + + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_ptr(dest_extra, vtable)?; + }, + + _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), + } + } + (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => { + // unsizing of generic struct with pointer fields + // Example: `Arc` -> `Arc` + // here we need to increase the size of every &T thin ptr field to a fat ptr + + assert_eq!(def_a, def_b); + + let src_fields = def_a.variants[0].fields.iter(); + let dst_fields = def_b.variants[0].fields.iter(); + + //let src = adt::MaybeSizedValue::sized(src); + //let dst = adt::MaybeSizedValue::sized(dst); + let src_ptr = match src { + Value::ByRef(ptr) => ptr, + _ => panic!("expected pointer, got {:?}", src), + }; + + let iter = src_fields.zip(dst_fields).enumerate(); + for (i, (src_f, dst_f)) in iter { + let src_fty = self.monomorphize_field_ty(src_f, substs_a); + let dst_fty = self.monomorphize_field_ty(dst_f, substs_b); + if self.type_size(dst_fty) == 0 { + continue; + } + let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; + let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes() as isize; + let src_f_ptr = src_ptr.offset(src_field_offset); + let dst_f_ptr = dest.offset(dst_field_offset); + if src_fty == dst_fty { + self.move_(src_f_ptr, dst_f_ptr, src_fty)?; + } else { + self.unsize_into(Value::ByRef(src_f_ptr), src_fty, dst_f_ptr, dst_fty)?; + } + } + } + _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", + src_ty, + dest_ty), } - _ => None, + Ok(()) } } diff --git a/tests/run-pass/issue-33387.rs b/tests/run-pass/issue-33387.rs new file mode 100644 index 0000000000000..edbf2b81ce941 --- /dev/null +++ b/tests/run-pass/issue-33387.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::Arc; + +trait Foo {} + +impl Foo for [u8; 2] {} + +fn main() { + let _: Arc = Arc::new([3, 4]); +} From f1f62051454eb5eef047cf7c02b17d018548513c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 18:06:51 +0200 Subject: [PATCH 0539/1096] we can get the Session from the TyCtxt --- benches/helpers/miri_helper.rs | 2 +- src/bin/miri.rs | 2 +- src/interpreter/mod.rs | 11 ++--------- src/interpreter/step.rs | 2 +- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index ff4087cb14abd..cdd1412204f99 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -58,7 +58,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id, state.session); }); + bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id); }); state.session.abort_if_errors(); }); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8bf6adde14b1d..cc0132e4090d9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -75,7 +75,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { mir_map_copy.map.insert(def_id, mir_map.map.get(&def_id).unwrap().clone()); } run_mir_passes(tcx, &mut mir_map_copy); - eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit, state.session); + eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit); state.session.abort_if_errors(); }); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8fb899572c4e3..e6c4c4eb022f7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -6,7 +6,6 @@ use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::session::Session; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; @@ -32,10 +31,6 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, - /// The Session, from rustc. - /// Used to extract info from other crates - session: &'a Session, - /// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items. mir_map: &'a MirMap<'tcx>, @@ -159,7 +154,7 @@ pub enum StackPopCleanup { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize, session: &'a Session) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self { EvalContext { tcx: tcx, mir_map: mir_map, @@ -168,7 +163,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { statics: HashMap::new(), stack: Vec::new(), stack_limit: stack_limit, - session: session, } } @@ -1197,10 +1191,9 @@ pub fn eval_main<'a, 'tcx: 'a>( memory_size: usize, step_limit: u64, stack_limit: usize, - session: &'a Session, ) { let mir = mir_map.map.get(&def_id).expect("no mir for main function"); - let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit, session); + let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); let substs = subst::Substs::empty(tcx); let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index cd1e9a1811743..fe386ea5fb606 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -212,7 +212,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { bug!("static def id doesn't point to item"); } } else { - let def = self.ecx.session.cstore.describe_def(def_id).expect("static not found"); + let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found"); if let hir::def::Def::Static(_, mutable) = def { self.global_item(def_id, substs, span, !mutable); } else { From 9e9d05e3ef7e32271c122b44438204e02326926a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Sep 2016 14:53:11 +0200 Subject: [PATCH 0540/1096] run compile-fail tests after run-pass tests it's annoying when debugging miri to have compile-fail tests fail due to some temporary assertions or panics. --- tests/compiletest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index e5058df95ea5d..ad83ea005f92c 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -54,7 +54,6 @@ fn compile_test() { .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") .to_owned(), }; - compile_fail(&sysroot); run_pass(); for_all_targets(&sysroot, |target| { let files = std::fs::read_dir("tests/run-pass").unwrap(); @@ -117,4 +116,5 @@ fn compile_test() { writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); assert_eq!(failed, 0, "some tests failed"); }); + compile_fail(&sysroot); } From 73f6d6e418dfc3f00dff441db0176481d0dfaaad Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Sep 2016 14:53:43 +0200 Subject: [PATCH 0541/1096] fix run-pass test error message parsing --- tests/compiletest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ad83ea005f92c..e8f43bbbe019a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -91,7 +91,7 @@ fn compile_test() { }, Ok(output) => { let output_err = std::str::from_utf8(&output.stderr).unwrap(); - if let Some(text) = output_err.splitn(2, "thread 'main' panicked at 'no mir for `").nth(1) { + if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { mir_not_found += 1; let end = text.find('`').unwrap(); writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); From 1c18f6ddfa5a390184b67b66124b372c9d49eda2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Sep 2016 18:22:09 +0200 Subject: [PATCH 0542/1096] implement slice patterns --- src/interpreter/mod.rs | 51 +++++++++++++++++-------- tests/run-pass/issue-15080.rs | 33 ++++++++++++++++ tests/run-pass/issue-17877.rs | 24 ++++++++++++ tests/run-pass/vec-matching-fold.rs | 58 +++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 tests/run-pass/issue-15080.rs create mode 100644 tests/run-pass/issue-17877.rs create mode 100644 tests/run-pass/vec-matching-fold.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e6c4c4eb022f7..cf43c33b5fcb2 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -576,15 +576,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Len(ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); - match ty.sty { - ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?, - ty::TySlice(_) => if let LvalueExtra::Length(len) = src.extra { - self.memory.write_usize(dest, len)?; - } else { - bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); - }, - _ => bug!("Rvalue::Len expected array or slice, got {:?}", ty), - } + let (_, len) = src.elem_ty_and_len(ty); + self.memory.write_usize(dest, len)?; } Ref(_, _, ref lvalue) => { @@ -889,19 +882,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Index(ref operand) => { - let elem_size = match base_ty.sty { - ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty), - _ => bug!("indexing expected an array or slice, got {:?}", base_ty), - }; + let (elem_ty, len) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); + assert!(n < len); base.ptr.offset(n as isize * elem_size as isize) } - ConstantIndex { .. } => unimplemented!(), - Subslice { .. } => unimplemented!(), + ConstantIndex { offset, min_length, from_end } => { + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); + assert!(n >= min_length as u64); + if from_end { + base.ptr.offset((n as isize - offset as isize) * elem_size as isize) + } else { + base.ptr.offset(offset as isize * elem_size as isize) + } + }, + Subslice { from, to } => { + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); + assert!((from as u64) <= n - (to as u64)); + return Ok(Lvalue { + ptr: base.ptr.offset(from as isize * elem_size as isize), + extra: LvalueExtra::Length(n - to as u64 - from as u64), + }) + }, } } }; @@ -1172,6 +1180,17 @@ impl Lvalue { assert_eq!(self.extra, LvalueExtra::None); self.ptr } + fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { + match ty.sty { + ty::TyArray(elem, n) => (elem, n as u64), + ty::TySlice(elem) => if let LvalueExtra::Length(len) = self.extra { + (elem, len) + } else { + bug!("elem_ty_and_len called on a slice given non-slice lvalue: {:?}", self); + }, + _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), + } + } } impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { diff --git a/tests/run-pass/issue-15080.rs b/tests/run-pass/issue-15080.rs new file mode 100644 index 0000000000000..cee0caeb465f5 --- /dev/null +++ b/tests/run-pass/issue-15080.rs @@ -0,0 +1,33 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![feature(slice_patterns)] + +fn main() { + let mut x: &[_] = &[1, 2, 3, 4]; + + let mut result = vec!(); + loop { + x = match *x { + [1, n, 3, ref rest..] => { + result.push(n); + rest + } + [n, ref rest..] => { + result.push(n); + rest + } + [] => + break + } + } + assert_eq!(result, [2, 4]); +} diff --git a/tests/run-pass/issue-17877.rs b/tests/run-pass/issue-17877.rs new file mode 100644 index 0000000000000..6c87e8d35fbf0 --- /dev/null +++ b/tests/run-pass/issue-17877.rs @@ -0,0 +1,24 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![feature(slice_patterns)] + +fn main() { + assert_eq!(match [0u8; 1024] { + _ => 42_usize, + }, 42_usize); + + assert_eq!(match [0u8; 1024] { + [1, _..] => 0_usize, + [0, _..] => 1_usize, + _ => 2_usize + }, 1_usize); +} diff --git a/tests/run-pass/vec-matching-fold.rs b/tests/run-pass/vec-matching-fold.rs new file mode 100644 index 0000000000000..ac80a4211ada6 --- /dev/null +++ b/tests/run-pass/vec-matching-fold.rs @@ -0,0 +1,58 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![feature(advanced_slice_patterns)] +#![feature(slice_patterns)] + +use std::fmt::Debug; + +fn foldl(values: &[T], + initial: U, + mut function: F) + -> U where + U: Clone+Debug, T:Debug, + F: FnMut(U, &T) -> U, +{ match values { + &[ref head, ref tail..] => + foldl(tail, function(initial, head), function), + &[] => { + // FIXME: call guards + let res = initial.clone(); res + } + } +} + +fn foldr(values: &[T], + initial: U, + mut function: F) + -> U where + U: Clone, + F: FnMut(&T, U) -> U, +{ + match values { + &[ref head.., ref tail] => + foldr(head, function(tail, initial), function), + &[] => { + // FIXME: call guards + let res = initial.clone(); res + } + } +} + +pub fn main() { + let x = &[1, 2, 3, 4, 5]; + + let product = foldl(x, 1, |a, b| a * *b); + assert_eq!(product, 120); + + let sum = foldr(x, 0, |a, b| *a + b); + assert_eq!(sum, 15); +} From 51abf19e123b2c74114e44962f77e147e021ac4f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Sep 2016 18:22:25 +0200 Subject: [PATCH 0543/1096] don't panic on asm! --- src/error.rs | 5 ++++- src/interpreter/mod.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index a465ad62b9d42..cee5d7d26906e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,6 +45,7 @@ pub enum EvalError<'tcx> { VtableForArgumentlessMethod, ModifiedConstantMemory, AssumptionNotHeld, + Assembler, } pub type EvalResult<'tcx, T> = Result>; @@ -102,7 +103,9 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::ModifiedConstantMemory => "tried to modify constant memory", EvalError::AssumptionNotHeld => - "`assume` argument was false" + "`assume` argument was false", + EvalError::Assembler => + "cannot evaluate assembler code", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index cf43c33b5fcb2..c9d18507337d6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -660,7 +660,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - InlineAsm { .. } => unimplemented!(), + InlineAsm { .. } => return Err(EvalError::Assembler), } Ok(()) From 787feaad4b9d03805e2e6aa5a4266cbd5b140f85 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Sep 2016 18:22:53 +0200 Subject: [PATCH 0544/1096] allow tuple field indexing into anonymous tuples --- src/interpreter/mod.rs | 9 +++- .../send-is-not-static-par-for.rs | 43 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/send-is-not-static-par-for.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c9d18507337d6..70b7806706fc8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,5 +1,6 @@ use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; +use rustc::hir::map::definitions::DefPathData; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; use rustc::traits::Reveal; @@ -712,13 +713,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } + ty::TyTuple(fields) => Ok(fields[field_index]), + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { assert_eq!(field_index, 0); Ok(ty) } - _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}", ty))), + _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } } @@ -1255,6 +1258,10 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { }; let mut err = tcx.sess.struct_span_err(span, &e.to_string()); for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { + if tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { + err.span_note(span, "inside call to closure"); + continue; + } // FIXME(solson): Find a way to do this without this Display impl hack. use rustc::util::ppaux; use std::fmt; diff --git a/tests/compile-fail/send-is-not-static-par-for.rs b/tests/compile-fail/send-is-not-static-par-for.rs new file mode 100644 index 0000000000000..bee05ecd7fae3 --- /dev/null +++ b/tests/compile-fail/send-is-not-static-par-for.rs @@ -0,0 +1,43 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//error-pattern: no mir for `std::panicking::panicking` + +use std::sync::Mutex; + +fn par_for(iter: I, f: F) + where I: Iterator, + I::Item: Send, + F: Fn(I::Item) + Sync +{ + for item in iter { + f(item) + } +} + +fn sum(x: &[i32]) { + let sum_lengths = Mutex::new(0); + par_for(x.windows(4), |x| { + *sum_lengths.lock().unwrap() += x.len() + }); + + assert_eq!(*sum_lengths.lock().unwrap(), (x.len() - 3) * 4); +} + +fn main() { + let mut elements = [0; 20]; + + // iterators over references into this stack frame + par_for(elements.iter_mut().enumerate(), |(i, x)| { + *x = i as i32 + }); + + sum(&elements) +} From 870bb4d862ee3c5b23cb428e7a697a13917ec8d8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 28 Sep 2016 11:48:43 -0600 Subject: [PATCH 0545/1096] Reword inline assembly error. --- src/error.rs | 6 +++--- src/interpreter/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index cee5d7d26906e..44179eafabbab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,7 +45,7 @@ pub enum EvalError<'tcx> { VtableForArgumentlessMethod, ModifiedConstantMemory, AssumptionNotHeld, - Assembler, + InlineAsm, } pub type EvalResult<'tcx, T> = Result>; @@ -104,8 +104,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to modify constant memory", EvalError::AssumptionNotHeld => "`assume` argument was false", - EvalError::Assembler => - "cannot evaluate assembler code", + EvalError::InlineAsm => + "cannot evaluate inline assembly", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 70b7806706fc8..41dfa6806fb52 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -661,7 +661,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - InlineAsm { .. } => return Err(EvalError::Assembler), + InlineAsm { .. } => return Err(EvalError::InlineAsm), } Ok(()) From f1c5bf2281d6fe39632d6c73a1bf682ee3c103cb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 29 Sep 2016 15:58:26 +0200 Subject: [PATCH 0546/1096] fix intrinsics and implement more of them --- src/interpreter/terminator/intrinsics.rs | 52 ++++++++++- tests/run-pass/intrinsics-integer.rs | 105 +++++++++++++++++++++++ 2 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/intrinsics-integer.rs diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index c025852bc5e45..f07688202ef70 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -67,15 +67,61 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "ctpop" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.value_to_primval(args_ptrs[2], elem_ty)?.expect_int("ctpop second arg not integral"); - let num = num.count_ones(); + let num = self.value_to_primval(args_ptrs[0], elem_ty)?; + let num = match num { + PrimVal::I8(i) => i.count_ones(), + PrimVal::U8(i) => i.count_ones(), + PrimVal::I16(i) => i.count_ones(), + PrimVal::U16(i) => i.count_ones(), + PrimVal::I32(i) => i.count_ones(), + PrimVal::U32(i) => i.count_ones(), + PrimVal::I64(i) => i.count_ones(), + PrimVal::U64(i) => i.count_ones(), + _ => bug!("ctpop called with non-integer type"), + }; + self.memory.write_uint(dest, num.into(), elem_size)?; + } + + "bswap" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.value_to_primval(args_ptrs[0], elem_ty)?; + let num = match num { + PrimVal::I8(i) => i.swap_bytes() as u64, + PrimVal::U8(i) => i.swap_bytes() as u64, + PrimVal::I16(i) => i.swap_bytes() as u64, + PrimVal::U16(i) => i.swap_bytes() as u64, + PrimVal::I32(i) => i.swap_bytes() as u64, + PrimVal::U32(i) => i.swap_bytes() as u64, + PrimVal::I64(i) => i.swap_bytes() as u64, + PrimVal::U64(i) => i.swap_bytes(), + _ => bug!("bswap called with non-integer type"), + }; + self.memory.write_uint(dest, num, elem_size)?; + } + + "cttz" => { + let elem_ty = substs.type_at(0); + let elem_size = self.type_size(elem_ty); + let num = self.value_to_primval(args_ptrs[0], elem_ty)?; + let num = match num { + PrimVal::I8(i) => i.trailing_zeros(), + PrimVal::U8(i) => i.trailing_zeros(), + PrimVal::I16(i) => i.trailing_zeros(), + PrimVal::U16(i) => i.trailing_zeros(), + PrimVal::I32(i) => i.trailing_zeros(), + PrimVal::U32(i) => i.trailing_zeros(), + PrimVal::I64(i) => i.trailing_zeros(), + PrimVal::U64(i) => i.trailing_zeros(), + _ => bug!("cttz called with non-integer type"), + }; self.memory.write_uint(dest, num.into(), elem_size)?; } "ctlz" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.value_to_primval(args_ptrs[2], elem_ty)?; + let num = self.value_to_primval(args_ptrs[0], elem_ty)?; let num = match num { PrimVal::I8(i) => i.leading_zeros(), PrimVal::U8(i) => i.leading_zeros(), diff --git a/tests/run-pass/intrinsics-integer.rs b/tests/run-pass/intrinsics-integer.rs new file mode 100644 index 0000000000000..759dc515456de --- /dev/null +++ b/tests/run-pass/intrinsics-integer.rs @@ -0,0 +1,105 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(intrinsics)] + +mod rusti { + extern "rust-intrinsic" { + pub fn ctpop(x: T) -> T; + pub fn ctlz(x: T) -> T; + pub fn cttz(x: T) -> T; + pub fn bswap(x: T) -> T; + } +} + +pub fn main() { + unsafe { + use rusti::*; + + assert_eq!(ctpop(0u8), 0); assert_eq!(ctpop(0i8), 0); + assert_eq!(ctpop(0u16), 0); assert_eq!(ctpop(0i16), 0); + assert_eq!(ctpop(0u32), 0); assert_eq!(ctpop(0i32), 0); + assert_eq!(ctpop(0u64), 0); assert_eq!(ctpop(0i64), 0); + + assert_eq!(ctpop(1u8), 1); assert_eq!(ctpop(1i8), 1); + assert_eq!(ctpop(1u16), 1); assert_eq!(ctpop(1i16), 1); + assert_eq!(ctpop(1u32), 1); assert_eq!(ctpop(1i32), 1); + assert_eq!(ctpop(1u64), 1); assert_eq!(ctpop(1i64), 1); + + assert_eq!(ctpop(10u8), 2); assert_eq!(ctpop(10i8), 2); + assert_eq!(ctpop(10u16), 2); assert_eq!(ctpop(10i16), 2); + assert_eq!(ctpop(10u32), 2); assert_eq!(ctpop(10i32), 2); + assert_eq!(ctpop(10u64), 2); assert_eq!(ctpop(10i64), 2); + + assert_eq!(ctpop(100u8), 3); assert_eq!(ctpop(100i8), 3); + assert_eq!(ctpop(100u16), 3); assert_eq!(ctpop(100i16), 3); + assert_eq!(ctpop(100u32), 3); assert_eq!(ctpop(100i32), 3); + assert_eq!(ctpop(100u64), 3); assert_eq!(ctpop(100i64), 3); + + assert_eq!(ctpop(-1i8 as u8), 8); assert_eq!(ctpop(-1i8), 8); + assert_eq!(ctpop(-1i16 as u16), 16); assert_eq!(ctpop(-1i16), 16); + assert_eq!(ctpop(-1i32 as u32), 32); assert_eq!(ctpop(-1i32), 32); + assert_eq!(ctpop(-1i64 as u64), 64); assert_eq!(ctpop(-1i64), 64); + + assert_eq!(ctlz(0u8), 8); assert_eq!(ctlz(0i8), 8); + assert_eq!(ctlz(0u16), 16); assert_eq!(ctlz(0i16), 16); + assert_eq!(ctlz(0u32), 32); assert_eq!(ctlz(0i32), 32); + assert_eq!(ctlz(0u64), 64); assert_eq!(ctlz(0i64), 64); + + assert_eq!(ctlz(1u8), 7); assert_eq!(ctlz(1i8), 7); + assert_eq!(ctlz(1u16), 15); assert_eq!(ctlz(1i16), 15); + assert_eq!(ctlz(1u32), 31); assert_eq!(ctlz(1i32), 31); + assert_eq!(ctlz(1u64), 63); assert_eq!(ctlz(1i64), 63); + + assert_eq!(ctlz(10u8), 4); assert_eq!(ctlz(10i8), 4); + assert_eq!(ctlz(10u16), 12); assert_eq!(ctlz(10i16), 12); + assert_eq!(ctlz(10u32), 28); assert_eq!(ctlz(10i32), 28); + assert_eq!(ctlz(10u64), 60); assert_eq!(ctlz(10i64), 60); + + assert_eq!(ctlz(100u8), 1); assert_eq!(ctlz(100i8), 1); + assert_eq!(ctlz(100u16), 9); assert_eq!(ctlz(100i16), 9); + assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25); + assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57); + + assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0); + assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0); + assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0); + assert_eq!(cttz(-1i64 as u64), 0); assert_eq!(cttz(-1i64), 0); + + assert_eq!(cttz(0u8), 8); assert_eq!(cttz(0i8), 8); + assert_eq!(cttz(0u16), 16); assert_eq!(cttz(0i16), 16); + assert_eq!(cttz(0u32), 32); assert_eq!(cttz(0i32), 32); + assert_eq!(cttz(0u64), 64); assert_eq!(cttz(0i64), 64); + + assert_eq!(cttz(1u8), 0); assert_eq!(cttz(1i8), 0); + assert_eq!(cttz(1u16), 0); assert_eq!(cttz(1i16), 0); + assert_eq!(cttz(1u32), 0); assert_eq!(cttz(1i32), 0); + assert_eq!(cttz(1u64), 0); assert_eq!(cttz(1i64), 0); + + assert_eq!(cttz(10u8), 1); assert_eq!(cttz(10i8), 1); + assert_eq!(cttz(10u16), 1); assert_eq!(cttz(10i16), 1); + assert_eq!(cttz(10u32), 1); assert_eq!(cttz(10i32), 1); + assert_eq!(cttz(10u64), 1); assert_eq!(cttz(10i64), 1); + + assert_eq!(cttz(100u8), 2); assert_eq!(cttz(100i8), 2); + assert_eq!(cttz(100u16), 2); assert_eq!(cttz(100i16), 2); + assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2); + assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2); + + assert_eq!(bswap(0x0Au8), 0x0A); // no-op + assert_eq!(bswap(0x0Ai8), 0x0A); // no-op + assert_eq!(bswap(0x0A0Bu16), 0x0B0A); + assert_eq!(bswap(0x0A0Bi16), 0x0B0A); + assert_eq!(bswap(0x0ABBCC0Du32), 0x0DCCBB0A); + assert_eq!(bswap(0x0ABBCC0Di32), 0x0DCCBB0A); + assert_eq!(bswap(0x0122334455667708u64), 0x0877665544332201); + assert_eq!(bswap(0x0122334455667708i64), 0x0877665544332201); + } +} From 18c8c852e484dc0d16ba999438cd63b4f3a7883f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 29 Sep 2016 16:42:01 +0200 Subject: [PATCH 0547/1096] factor out shared code --- src/interpreter/terminator/intrinsics.rs | 126 +++++++++++------------ 1 file changed, 58 insertions(+), 68 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index f07688202ef70..817ded7273d61 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -31,7 +31,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let f32 = self.tcx.types.f32; let f64 = self.tcx.types.f64; - match &self.tcx.item_name(def_id).as_str()[..] { + let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..]; + match intrinsic_name { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, @@ -64,76 +65,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } - "ctpop" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.value_to_primval(args_ptrs[0], elem_ty)?; - let num = match num { - PrimVal::I8(i) => i.count_ones(), - PrimVal::U8(i) => i.count_ones(), - PrimVal::I16(i) => i.count_ones(), - PrimVal::U16(i) => i.count_ones(), - PrimVal::I32(i) => i.count_ones(), - PrimVal::U32(i) => i.count_ones(), - PrimVal::I64(i) => i.count_ones(), - PrimVal::U64(i) => i.count_ones(), - _ => bug!("ctpop called with non-integer type"), - }; - self.memory.write_uint(dest, num.into(), elem_size)?; - } - + "ctpop" | + "cttz" | + "ctlz" | "bswap" => { let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); let num = self.value_to_primval(args_ptrs[0], elem_ty)?; - let num = match num { - PrimVal::I8(i) => i.swap_bytes() as u64, - PrimVal::U8(i) => i.swap_bytes() as u64, - PrimVal::I16(i) => i.swap_bytes() as u64, - PrimVal::U16(i) => i.swap_bytes() as u64, - PrimVal::I32(i) => i.swap_bytes() as u64, - PrimVal::U32(i) => i.swap_bytes() as u64, - PrimVal::I64(i) => i.swap_bytes() as u64, - PrimVal::U64(i) => i.swap_bytes(), - _ => bug!("bswap called with non-integer type"), - }; - self.memory.write_uint(dest, num, elem_size)?; - } - - "cttz" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.value_to_primval(args_ptrs[0], elem_ty)?; - let num = match num { - PrimVal::I8(i) => i.trailing_zeros(), - PrimVal::U8(i) => i.trailing_zeros(), - PrimVal::I16(i) => i.trailing_zeros(), - PrimVal::U16(i) => i.trailing_zeros(), - PrimVal::I32(i) => i.trailing_zeros(), - PrimVal::U32(i) => i.trailing_zeros(), - PrimVal::I64(i) => i.trailing_zeros(), - PrimVal::U64(i) => i.trailing_zeros(), - _ => bug!("cttz called with non-integer type"), - }; - self.memory.write_uint(dest, num.into(), elem_size)?; - } - - "ctlz" => { - let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); - let num = self.value_to_primval(args_ptrs[0], elem_ty)?; - let num = match num { - PrimVal::I8(i) => i.leading_zeros(), - PrimVal::U8(i) => i.leading_zeros(), - PrimVal::I16(i) => i.leading_zeros(), - PrimVal::U16(i) => i.leading_zeros(), - PrimVal::I32(i) => i.leading_zeros(), - PrimVal::U32(i) => i.leading_zeros(), - PrimVal::I64(i) => i.leading_zeros(), - PrimVal::U64(i) => i.leading_zeros(), - _ => bug!("ctlz called with non-integer type"), - }; - self.memory.write_uint(dest, num.into(), elem_size)?; + let num = numeric_intrinsic(intrinsic_name, num); + self.memory.write_primval(dest, num)?; } "discriminant_value" => { @@ -396,3 +335,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) } } + +fn numeric_intrinsic(name: &str, val: PrimVal) -> PrimVal { + use primval::PrimVal::*; + match name { + "ctpop" => match val { + I8(i) => I8(i.count_ones() as i8), + U8(i) => U8(i.count_ones() as u8), + I16(i) => I16(i.count_ones() as i16), + U16(i) => U16(i.count_ones() as u16), + I32(i) => I32(i.count_ones() as i32), + U32(i) => U32(i.count_ones() as u32), + I64(i) => I64(i.count_ones() as i64), + U64(i) => U64(i.count_ones() as u64), + other => bug!("invalid `ctpop` argument: {:?}", other), + }, + "cttz" => match val { + I8(i) => I8(i.trailing_zeros() as i8), + U8(i) => U8(i.trailing_zeros() as u8), + I16(i) => I16(i.trailing_zeros() as i16), + U16(i) => U16(i.trailing_zeros() as u16), + I32(i) => I32(i.trailing_zeros() as i32), + U32(i) => U32(i.trailing_zeros() as u32), + I64(i) => I64(i.trailing_zeros() as i64), + U64(i) => U64(i.trailing_zeros() as u64), + other => bug!("invalid `cttz` argument: {:?}", other), + }, + "ctlz" => match val { + I8(i) => I8(i.leading_zeros() as i8), + U8(i) => U8(i.leading_zeros() as u8), + I16(i) => I16(i.leading_zeros() as i16), + U16(i) => U16(i.leading_zeros() as u16), + I32(i) => I32(i.leading_zeros() as i32), + U32(i) => U32(i.leading_zeros() as u32), + I64(i) => I64(i.leading_zeros() as i64), + U64(i) => U64(i.leading_zeros() as u64), + other => bug!("invalid `ctlz` argument: {:?}", other), + }, + "bswap" => match val { + I8(i) => I8(i.swap_bytes() as i8), + U8(i) => U8(i.swap_bytes() as u8), + I16(i) => I16(i.swap_bytes() as i16), + U16(i) => U16(i.swap_bytes() as u16), + I32(i) => I32(i.swap_bytes() as i32), + U32(i) => U32(i.swap_bytes() as u32), + I64(i) => I64(i.swap_bytes() as i64), + U64(i) => U64(i.swap_bytes() as u64), + other => bug!("invalid `bswap` argument: {:?}", other), + }, + _ => bug!("not a numeric intrinsic: {}", name), + } +} From 8c666b30edb36109d0f18fee9c589c220eaa7213 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 30 Sep 2016 10:45:13 +0200 Subject: [PATCH 0548/1096] remove some debug output --- src/interpreter/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 41dfa6806fb52..9a15e54bf60aa 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -838,8 +838,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = variant.field_offset(field.index()).bytes(); let ptr = base.ptr.offset(offset as isize); - trace!("{:?}", base); - trace!("{:?}", field_ty); if self.type_is_sized(field_ty) { ptr } else { From c9914cd3aeaaeb59e1067205df9b33a3f1c1aa79 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 30 Sep 2016 10:45:52 +0200 Subject: [PATCH 0549/1096] fix enum variants with multiple fields --- src/interpreter/mod.rs | 17 +- tests/run-pass/deriving-associated-types.rs | 208 ++++++++++++++++++++ tests/run-pass/enums.rs | 34 ++++ 3 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 tests/run-pass/deriving-associated-types.rs create mode 100644 tests/run-pass/enums.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9a15e54bf60aa..573b5c6f8490b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -819,11 +819,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Field(field, field_ty) => { let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; - let variant = match *base_layout { - Univariant { ref variant, .. } => variant, + let field = field.index(); + let offset = match *base_layout { + Univariant { ref variant, .. } => variant.field_offset(field), General { ref variants, .. } => { if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { - &variants[variant_idx] + // +1 for the discriminant, which is field 0 + variants[variant_idx].field_offset(field + 1) } else { bug!("field access on enum had no variant index"); } @@ -832,12 +834,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(field.index(), 0); return Ok(base); } - StructWrappedNullablePointer { ref nonnull, .. } => nonnull, + StructWrappedNullablePointer { ref nonnull, .. } => nonnull.field_offset(field), _ => bug!("field access on non-product type: {:?}", base_layout), }; - let offset = variant.field_offset(field.index()).bytes(); - let ptr = base.ptr.offset(offset as isize); + let ptr = base.ptr.offset(offset.bytes() as isize); if self.type_is_sized(field_ty) { ptr } else { @@ -857,9 +858,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Downcast(_, variant) => { use rustc::ty::layout::Layout::*; match *base_layout { - General { ref variants, .. } => { + General { .. } => { return Ok(Lvalue { - ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), + ptr: base.ptr, extra: LvalueExtra::DowncastVariant(variant), }); } diff --git a/tests/run-pass/deriving-associated-types.rs b/tests/run-pass/deriving-associated-types.rs new file mode 100644 index 0000000000000..b67ef85acf62d --- /dev/null +++ b/tests/run-pass/deriving-associated-types.rs @@ -0,0 +1,208 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait DeclaredTrait { + type Type; +} + +impl DeclaredTrait for i32 { + type Type = i32; +} + +pub trait WhereTrait { + type Type; +} + +impl WhereTrait for i32 { + type Type = i32; +} + +// Make sure we don't add a bound that just shares a name with an associated +// type. +pub mod module { + pub type Type = i32; +} + +#[derive(PartialEq, Debug)] +struct PrivateStruct(T); + +#[derive(PartialEq, Debug)] +struct TupleStruct( + module::Type, + Option, + A, + PrivateStruct, + B, + B::Type, + Option, + ::Type, + Option<::Type>, + C, + C::Type, + Option, + ::Type, + Option<::Type>, + ::Type, +) where C: WhereTrait; + +#[derive(PartialEq, Debug)] +pub struct Struct where C: WhereTrait { + m1: module::Type, + m2: Option, + a1: A, + a2: PrivateStruct, + b: B, + b1: B::Type, + b2: Option, + b3: ::Type, + b4: Option<::Type>, + c: C, + c1: C::Type, + c2: Option, + c3: ::Type, + c4: Option<::Type>, + d: ::Type, +} + +#[derive(PartialEq, Debug)] +enum Enum where C: WhereTrait { + Unit, + Seq( + module::Type, + Option, + A, + PrivateStruct, + B, + B::Type, + Option, + ::Type, + Option<::Type>, + C, + C::Type, + Option, + ::Type, + Option<::Type>, + ::Type, + ), + Map { + m1: module::Type, + m2: Option, + a1: A, + a2: PrivateStruct, + b: B, + b1: B::Type, + b2: Option, + b3: ::Type, + b4: Option<::Type>, + c: C, + c1: C::Type, + c2: Option, + c3: ::Type, + c4: Option<::Type>, + d: ::Type, + }, +} + +fn main() { + + let e: Enum< + i32, + i32, + i32, + > = Enum::Seq( + 0, + None, + 0, + PrivateStruct(0), + 0, + 0, + None, + 0, + None, + 0, + 0, + None, + 0, + None, + 0, + ); + assert_eq!(e, e); + + let e: Enum< + i32, + i32, + i32, + > = Enum::Map { + m1: 0, + m2: None, + a1: 0, + a2: PrivateStruct(0), + b: 0, + b1: 0, + b2: None, + b3: 0, + b4: None, + c: 0, + c1: 0, + c2: None, + c3: 0, + c4: None, + d: 0, + }; + assert_eq!(e, e); + let e: TupleStruct< + i32, + i32, + i32, + > = TupleStruct( + 0, + None, + 0, + PrivateStruct(0), + 0, + 0, + None, + 0, + None, + 0, + 0, + None, + 0, + None, + 0, + ); + assert_eq!(e, e); + + let e: Struct< + i32, + i32, + i32, + > = Struct { + m1: 0, + m2: None, + a1: 0, + a2: PrivateStruct(0), + b: 0, + b1: 0, + b2: None, + b3: 0, + b4: None, + c: 0, + c1: 0, + c2: None, + c3: 0, + c4: None, + d: 0, + }; + assert_eq!(e, e); + + let e = Enum::Unit::; + assert_eq!(e, e); +} diff --git a/tests/run-pass/enums.rs b/tests/run-pass/enums.rs new file mode 100644 index 0000000000000..1f27292904f42 --- /dev/null +++ b/tests/run-pass/enums.rs @@ -0,0 +1,34 @@ +enum MyEnum { + MyEmptyVariant, + MyNewtypeVariant(i32), + MyTupleVariant(i32, i32), + MyStructVariant { + my_first_field: i32, + my_second_field: i32, + } +} + +fn test(me: MyEnum) { + match me { + MyEnum::MyEmptyVariant => {}, + MyEnum::MyNewtypeVariant(ref val) => assert_eq!(val, &42), + MyEnum::MyTupleVariant(ref a, ref b) => { + assert_eq!(a, &43); + assert_eq!(b, &44); + }, + MyEnum::MyStructVariant { ref my_first_field, ref my_second_field } => { + assert_eq!(my_first_field, &45); + assert_eq!(my_second_field, &46); + }, + } +} + +fn main() { + test(MyEnum::MyEmptyVariant); + test(MyEnum::MyNewtypeVariant(42)); + test(MyEnum::MyTupleVariant(43, 44)); + test(MyEnum::MyStructVariant{ + my_first_field: 45, + my_second_field: 46, + }); +} From d9680dbb100442c9bad2712972bb50097089023d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 1 Oct 2016 15:30:29 +0200 Subject: [PATCH 0550/1096] bump compiletest --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d85ee67cf3e1f..906d7031214d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -137,7 +137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9" "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" -"checksum compiletest_rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bcddebf582c5c035cce855a89596eb686cc40b9e77da1026fba735dcca2fbd3" +"checksum compiletest_rs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ac936b073036755b7d176f16d4c660f4d6f1390cbe556316af9cb9724117059" "checksum env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "82dcb9ceed3868a03b335657b85a159736c961900f7e7747d3b0b97b9ccb5ccb" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" diff --git a/Cargo.toml b/Cargo.toml index fea568e244d43..a770bea069499 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ log = "0.3.6" log_settings = "0.1.1" [dev-dependencies] -compiletest_rs = "0.2" +compiletest_rs = "0.2.3" From de38015e47c3d0c12995e378436a04a92844a0b7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 1 Oct 2016 15:33:07 +0200 Subject: [PATCH 0551/1096] rustup --- src/bin/miri.rs | 2 +- src/interpreter/mod.rs | 53 ++++++++++--------------------- src/interpreter/step.rs | 4 +-- src/interpreter/terminator/mod.rs | 7 ++-- src/memory.rs | 17 +++++++--- 5 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index cc0132e4090d9..515b54e4bfd5f 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -138,5 +138,5 @@ fn main() { args.push(find_sysroot()); } - rustc_driver::run_compiler(&args, &mut MiriCompilerCalls); + rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 573b5c6f8490b..722167a4175a0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -73,23 +73,13 @@ pub struct Frame<'a, 'tcx: 'a> { // Return pointer and local allocations //////////////////////////////////////////////////////////////////////////////// - /// A pointer for writing the return value of the current call if it's not a diverging call. - pub return_ptr: Option, - /// The block to return to when returning from the current stack frame pub return_to_block: StackPopCleanup, /// The list of locals for the current function, stored in order as - /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` - /// and the temporaries at `self.temp_offset`. + /// `[return_ptr, arguments..., variables..., temporaries...]`. pub locals: Vec, - /// The offset of the first variable in `self.locals`. - pub var_offset: usize, - - /// The offset of the first temporary in `self.locals`. - pub temp_offset: usize, - //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// @@ -336,32 +326,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, - return_ptr: Option, + return_ptr: Pointer, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx, ()> { - let arg_tys = mir.arg_decls.iter().map(|a| a.ty); - let var_tys = mir.var_decls.iter().map(|v| v.ty); - let temp_tys = mir.temp_decls.iter().map(|t| t.ty); - - let num_args = mir.arg_decls.len(); - let num_vars = mir.var_decls.len(); + let local_tys = mir.local_decls.iter().map(|a| a.ty); ::log_settings::settings().indentation += 1; - let locals: EvalResult<'tcx, Vec> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + // directly change the first allocation (the return value) to *be* the allocation where the + // caller stores the result + let locals: EvalResult<'tcx, Vec> = iter::once(Ok(return_ptr)).chain(local_tys.skip(1).map(|ty| { let size = self.type_size_with_substs(ty, substs); let align = self.type_align_with_substs(ty, substs); self.memory.allocate(size, align) - }).collect(); + })).collect(); self.stack.push(Frame { mir: mir.clone(), block: mir::START_BLOCK, - return_ptr: return_ptr, return_to_block: return_to_block, locals: locals?, - var_offset: num_args, - temp_offset: num_args + num_vars, span: span, def_id: def_id, substs: substs, @@ -793,11 +777,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - ReturnPointer => self.frame().return_ptr - .expect("ReturnPointer used in a function with no return value"), - Arg(i) => self.frame().locals[i.index()], - Var(i) => self.frame().locals[self.frame().var_offset + i.index()], - Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], + Local(i) => self.frame().locals[i.index()], Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -1219,18 +1199,17 @@ pub fn eval_main<'a, 'tcx: 'a>( let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr), StackPopCleanup::None) + ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, return_ptr, StackPopCleanup::None) .expect("could not allocate first stack frame"); - if mir.arg_decls.len() == 2 { + // FIXME: this is a horrible and wrong way to detect the start function, but overwriting the first two locals shouldn't do much + if mir.local_decls.len() > 2 { // start function - let ptr_size = ecx.memory().pointer_size(); - let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs"); - ecx.memory_mut().write_usize(nargs, 0).unwrap(); - let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer"); - ecx.memory_mut().write_usize(args, 0).unwrap(); - ecx.frame_mut().locals[0] = nargs; - ecx.frame_mut().locals[1] = args; + let nargs = ecx.frame_mut().locals[1]; + let args = ecx.frame_mut().locals[2]; + // ignore errors, if the locals are too small this is not the start function + let _ = ecx.memory_mut().write_usize(nargs, 0); + let _ = ecx.memory_mut().write_usize(args, 0); } for _ in 0..step_limit { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index fe386ea5fb606..77350504306b6 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -134,7 +134,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr), cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, ptr, cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -183,7 +183,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { constant.span, mir, this.substs, - Some(return_ptr), + return_ptr, StackPopCleanup::Freeze(return_ptr.alloc_id)) }); } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index cd024d4c007dc..e0e6e3996c400 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -186,13 +186,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = self.load_mir(resolved_def_id)?; let (return_ptr, return_to_block) = match destination { - Some((ptr, block)) => (Some(ptr), StackPopCleanup::Goto(block)), - None => (None, StackPopCleanup::None), + Some((ptr, block)) => (ptr, StackPopCleanup::Goto(block)), + None => (Pointer::never_ptr(), StackPopCleanup::None), }; self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { - let dest = self.frame().locals[i]; + // argument start at index 1, since index 0 is reserved for the return allocation + let dest = self.frame().locals[i + 1]; self.write_value(arg_val, dest, arg_ty)?; } diff --git a/src/memory.rs b/src/memory.rs index eeae014c6da1c..da1214acfb8ad 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -56,10 +56,10 @@ impl Pointer { self.alloc_id == ZST_ALLOC_ID } pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> { - if self.points_to_zst() { - Ok(self.offset) - } else { - Err(EvalError::ReadPointerAsBytes) + match self.alloc_id { + NEVER_ALLOC_ID | + ZST_ALLOC_ID => Ok(self.offset), + _ => Err(EvalError::ReadPointerAsBytes), } } pub fn from_int(i: usize) -> Self { @@ -74,6 +74,12 @@ impl Pointer { offset: 0, } } + pub fn never_ptr() -> Self { + Pointer { + alloc_id: NEVER_ALLOC_ID, + offset: 0, + } + } } #[derive(Debug, Clone, Hash, Eq, PartialEq)] @@ -115,6 +121,7 @@ pub struct Memory<'a, 'tcx> { } const ZST_ALLOC_ID: AllocId = AllocId(0); +const NEVER_ALLOC_ID: AllocId = AllocId(1); impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { @@ -122,7 +129,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), - next_id: AllocId(1), + next_id: AllocId(2), layout: layout, memory_size: max_memory, memory_usage: 0, From 911e46fb0efcb7bd278b6c2807c1c63cd2212466 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 3 Oct 2016 20:45:50 -0600 Subject: [PATCH 0552/1096] Update for changes in rustc. --- src/interpreter/mod.rs | 41 +++++++++--------------- src/interpreter/terminator/intrinsics.rs | 16 +++------ src/interpreter/terminator/mod.rs | 5 +-- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 722167a4175a0..b5d339dd9a7d5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -387,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; - let offset = tup_layout.field_offset(1).bytes() as isize; + let offset = tup_layout.offsets[1].bytes() as isize; self.memory.write_bool(dest.offset(offset), overflowed) } @@ -460,8 +460,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *dest_layout { Univariant { ref variant, .. } => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter().map(|s| s.bytes())); + let offsets = variant.offsets.iter().map(|s| s.bytes()); self.assign_fields(dest, offsets, operands)?; } @@ -478,11 +477,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let discr_size = discr.size().bytes() as usize; - self.memory.write_uint(dest, discr_val, discr_size)?; + let discr_offset = variants[variant].offsets[0].bytes() as isize; + let discr_dest = dest.offset(discr_offset); + self.memory.write_uint(discr_dest, discr_val, discr_size)?; - let offsets = variants[variant].offset_after_field.iter() + // Don't include the first offset; it's for the discriminant. + let field_offsets = variants[variant].offsets.iter().skip(1) .map(|s| s.bytes()); - self.assign_fields(dest, offsets, operands)?; + self.assign_fields(dest, field_offsets, operands)?; } else { bug!("tried to assign {:?} to Layout::General", kind); } @@ -508,8 +510,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield } => { if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { if nndiscr == variant as u64 { - let offsets = iter::once(0) - .chain(nonnull.offset_after_field.iter().map(|s| s.bytes())); + let offsets = nonnull.offsets.iter().map(|s| s.bytes()); try!(self.assign_fields(dest, offsets, operands)); } else { for operand in operands { @@ -715,7 +716,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *layout { Univariant { ref variant, .. } => { - Ok(variant.field_offset(field_index)) + Ok(variant.offsets[field_index]) } FatPointer { .. } => { let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size(); @@ -801,11 +802,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; let field = field.index(); let offset = match *base_layout { - Univariant { ref variant, .. } => variant.field_offset(field), + Univariant { ref variant, .. } => variant.offsets[field], General { ref variants, .. } => { if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { // +1 for the discriminant, which is field 0 - variants[variant_idx].field_offset(field + 1) + variants[variant_idx].offsets[field + 1] } else { bug!("field access on enum had no variant index"); } @@ -814,7 +815,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(field.index(), 0); return Ok(base); } - StructWrappedNullablePointer { ref nonnull, .. } => nonnull.field_offset(field), + StructWrappedNullablePointer { ref nonnull, .. } => { + nonnull.offsets[field] + } _ => bug!("field access on non-product type: {:?}", base_layout), }; @@ -1289,17 +1292,3 @@ impl IntegerExt for layout::Integer { } } } - -trait StructExt { - fn field_offset(&self, index: usize) -> Size; -} - -impl StructExt for layout::Struct { - fn field_offset(&self, index: usize) -> Size { - if index == 0 { - Size::from_bytes(0) - } else { - self.offset_after_field[index - 1] - } - } -} diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 817ded7273d61..31e337924adaa 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -246,20 +246,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let layout = self.type_layout(ty); debug!("DST {} layout: {:?}", ty, layout); - // Returns size in bytes of all fields except the last one - // (we will be recursing on the last one). - fn local_prefix_bytes(variant: &ty::layout::Struct) -> u64 { - let fields = variant.offset_after_field.len(); - if fields > 1 { - variant.offset_after_field[fields - 2].bytes() - } else { - 0 - } - } - let (sized_size, sized_align) = match *layout { ty::layout::Layout::Univariant { ref variant, .. } => { - (local_prefix_bytes(variant), variant.align.abi()) + // The offset of the start of the last field gives the size of the + // sized part of the type. + let size = variant.offsets.last().map_or(0, |f| f.bytes()); + (size, variant.align.abi()) } _ => { bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index e0e6e3996c400..5c9123ba91e9c 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -5,7 +5,6 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use std::iter; use std::rc::Rc; use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; @@ -343,9 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { - let offsets = iter::once(0) - .chain(variant.offset_after_field.iter() - .map(|s| s.bytes())); + let offsets = variant.offsets.iter().map(|s| s.bytes()); let last_ptr = match last { Value::ByRef(ptr) => ptr, _ => bug!("rust-call ABI tuple argument wasn't Value::ByRef"), From cb23b8d0a79b598d4a56d992fbb4c62a74d58b68 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 9 Oct 2016 17:50:01 -0600 Subject: [PATCH 0553/1096] `cargo update` to fix compiletest build. --- Cargo.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 906d7031214d5..d0c7b1a011ee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,15 +3,15 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -33,11 +33,11 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.75 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -56,7 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -77,18 +77,18 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "0.1.75" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -108,12 +108,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thread_local" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -135,21 +135,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9" +"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" -"checksum compiletest_rs 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ac936b073036755b7d176f16d4c660f4d6f1390cbe556316af9cb9724117059" -"checksum env_logger 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "82dcb9ceed3868a03b335657b85a159736c961900f7e7747d3b0b97b9ccb5ccb" +"checksum compiletest_rs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "28d60af0dbee4912f00dda79ac3b06d1ca44b641d69359e6f1d4df7c985521d2" +"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" -"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" +"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum regex 0.1.75 (registry+https://github.com/rust-lang/crates.io-index)" = "f62414f9d3b0f53e827ac46d6f8ce2ff6a91afd724225a5986e54e81e170693c" +"checksum regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)" = "64b03446c466d35b42f2a8b203c8e03ed8b91c0f17b56e1f84f7210a257aa665" "checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd" "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" -"checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d" +"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" From 5f65ee2713325683af32c956f7a33e3d6d7d9d90 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 14 Oct 2016 03:31:45 -0600 Subject: [PATCH 0554/1096] Refactor in preparation for `Value` locals. Turning locals into `Vec` will allow writing `PrimVal` results directly into the locals array without creating `memory::Allocation`s for every local. This will entail passing around a generalized kind of `Lvalue` instead of `Pointer`s for the destinations of operations. Replacing `Pointer` with `Lvalue` is mostly done with this commit, but expanding `Lvalue` will come later. This commit turns every local from `Pointer` into `Value::ByRef(ptr)`. Locals which are `Value::ByVal(prim_val)` will come in a later commit. --- src/interpreter/mod.rs | 204 ++++++++++++++++------- src/interpreter/step.rs | 13 +- src/interpreter/terminator/intrinsics.rs | 102 ++++++++---- src/interpreter/terminator/mod.rs | 47 ++++-- src/interpreter/value.rs | 13 +- src/primval.rs | 22 +++ tests/run-pass/start_fn.rs | 9 - 7 files changed, 283 insertions(+), 127 deletions(-) delete mode 100644 tests/run-pass/start_fn.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b5d339dd9a7d5..dc787a7608c7d 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -78,7 +78,7 @@ pub struct Frame<'a, 'tcx: 'a> { /// The list of locals for the current function, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. - pub locals: Vec, + pub locals: Vec, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -93,7 +93,7 @@ pub struct Frame<'a, 'tcx: 'a> { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct Lvalue { +pub struct Lvalue { ptr: Pointer, extra: LvalueExtra, } @@ -157,7 +157,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn alloc_ret_ptr(&mut self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Pointer> { + pub fn alloc_ptr( + &mut self, + ty: Ty<'tcx>, + substs: &'tcx Substs<'tcx> + ) -> EvalResult<'tcx, Pointer> { let size = self.type_size_with_substs(ty, substs); let align = self.type_align_with_substs(ty, substs); self.memory.allocate(size, align) @@ -175,7 +179,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - fn target_isize_primval(&self, n: i64) -> PrimVal { + fn isize_primval(&self, n: i64) -> PrimVal { match self.memory.pointer_size() { 1 => PrimVal::I8(n as i8), 2 => PrimVal::I16(n as i16), @@ -185,7 +189,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn target_usize_primval(&self, n: u64) -> PrimVal { + fn usize_primval(&self, n: u64) -> PrimVal { match self.memory.pointer_size() { 1 => PrimVal::U8(n as u8), 2 => PrimVal::U16(n as u16), @@ -200,7 +204,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.usize_primval(s.len() as u64))) } fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -326,19 +330,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, - return_ptr: Pointer, + return_lvalue: Lvalue, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx, ()> { let local_tys = mir.local_decls.iter().map(|a| a.ty); ::log_settings::settings().indentation += 1; + // FIXME(solson) + let return_ptr = return_lvalue.to_ptr(); + // directly change the first allocation (the return value) to *be* the allocation where the // caller stores the result - let locals: EvalResult<'tcx, Vec> = iter::once(Ok(return_ptr)).chain(local_tys.skip(1).map(|ty| { + let locals: EvalResult<'tcx, Vec> = iter::once(Ok(Value::ByRef(return_ptr))).chain(local_tys.skip(1).map(|ty| { let size = self.type_size_with_substs(ty, substs); let align = self.type_align_with_substs(ty, substs); - self.memory.allocate(size, align) + + // FIXME(solson) + self.memory.allocate(size, align).map(Value::ByRef) })).collect(); self.stack.push(Frame { @@ -377,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - dest: Pointer, + dest: Lvalue, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { use rustc::ty::layout::Layout::*; @@ -387,6 +396,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; + + // FIXME(solson) + let dest = dest.to_ptr(); + let offset = tup_layout.offsets[1].bytes() as isize; self.memory.write_bool(dest.offset(offset), overflowed) } @@ -398,26 +411,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - dest: Pointer, + dest: Lvalue, ) -> EvalResult<'tcx, bool> { let left_primval = self.eval_operand_to_primval(left)?; let right_primval = self.eval_operand_to_primval(right)?; let (val, overflow) = primval::binary_op(op, left_primval, right_primval)?; - self.memory.write_primval(dest, val)?; + self.write_primval(dest, val)?; Ok(overflow) } fn assign_fields>( &mut self, - dest: Pointer, + dest: Lvalue, offsets: I, operands: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, ()> { + // FIXME(solson) + let dest = dest.to_ptr(); + for (offset, operand) in offsets.into_iter().zip(operands) { let value = self.eval_operand(operand)?; let value_ty = self.operand_ty(operand); let field_dest = dest.offset(offset as isize); - self.write_value(value, field_dest, value_ty)?; + self.write_value_to_ptr(value, field_dest, value_ty)?; } Ok(()) } @@ -431,7 +447,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { rvalue: &mir::Rvalue<'tcx>, lvalue: &mir::Lvalue<'tcx>, ) -> EvalResult<'tcx, ()> { - let dest = self.eval_lvalue(lvalue)?.to_ptr(); + let dest = self.eval_lvalue(lvalue)?; let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty); @@ -453,7 +469,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; - self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?; + self.write_primval(dest, primval::unary_op(un_op, val)?)?; } Aggregate(ref kind, ref operands) => { @@ -478,7 +494,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let discr_size = discr.size().bytes() as usize; let discr_offset = variants[variant].offsets[0].bytes() as isize; - let discr_dest = dest.offset(discr_offset); + + // FIXME(solson) + let discr_dest = (dest.to_ptr()).offset(discr_offset); + self.memory.write_uint(discr_dest, discr_val, discr_size)?; // Don't include the first offset; it's for the discriminant. @@ -500,7 +519,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(value, dest, value_ty)?; } else { assert_eq!(operands.len(), 0); - self.memory.write_isize(dest, 0)?; + let zero = self.isize_primval(0); + self.write_primval(dest, zero)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -518,6 +538,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(self.type_size(operand_ty), 0); } let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; + + // FIXME(solson) + let dest = dest.to_ptr(); + let dest = dest.offset(offset.bytes() as isize); try!(self.memory.write_isize(dest, 0)); } @@ -532,6 +556,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let size = discr.size().bytes() as usize; + // FIXME(solson) + let dest = dest.to_ptr(); + if signed { self.memory.write_int(dest, val as i64, size)?; } else { @@ -553,9 +580,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let elem_size = self.type_size(elem_ty); let value = self.eval_operand(operand)?; + + // FIXME(solson) + let dest = dest.to_ptr(); + for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.write_value(value, elem_dest, elem_ty)?; + self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -563,10 +594,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); - self.memory.write_usize(dest, len)?; + let len_val = self.usize_primval(len); + self.write_primval(dest, len_val)?; } Ref(_, _, ref lvalue) => { + // FIXME(solson) + let dest = dest.to_ptr(); + let lvalue = self.eval_lvalue(lvalue)?; self.memory.write_ptr(dest, lvalue.ptr)?; let extra_ptr = dest.offset(self.memory.pointer_size() as isize); @@ -580,6 +615,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Box(ty) => { + // FIXME(solson) + let dest = dest.to_ptr(); + let size = self.type_size(ty); let align = self.type_align(ty); let ptr = self.memory.allocate(size, align)?; @@ -587,6 +625,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Cast(kind, ref operand, cast_ty) => { + // FIXME(solson) + let dest = dest.to_ptr(); + debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); use rustc::mir::repr::CastKind::*; match kind { @@ -778,7 +819,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; let ptr = match *lvalue { - Local(i) => self.frame().locals[i.index()], + Local(i) => { + match self.frame().locals[i.index()] { + Value::ByRef(p) => p, + _ => bug!(), + } + } Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -910,7 +956,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } - fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); self.memory.copy(src, dest, size, align)?; @@ -957,14 +1003,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + fn write_primval( + &mut self, + dest: Lvalue, + val: PrimVal, + ) -> EvalResult<'tcx, ()> { + // FIXME(solson) + let dest = dest.to_ptr(); + + self.memory.write_primval(dest, val) + } + fn write_value( + &mut self, + value: Value, + dest: Lvalue, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, ()> { + // FIXME(solson) + let dest = dest.to_ptr(); + self.write_value_to_ptr(value, dest, dest_ty) + } + + fn write_value_to_ptr( &mut self, value: Value, dest: Pointer, - dest_ty: Ty<'tcx> + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match value { - Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), + Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), Value::ByValPair(a, b) => { self.memory.write_primval(dest, a)?; @@ -984,7 +1052,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - use syntax::ast::{IntTy, UintTy, FloatTy}; + use syntax::ast::FloatTy; + let val = match &ty.sty { &ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), &ty::TyChar => { @@ -995,17 +1064,31 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - &ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), - &ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), - &ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), - &ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), - &ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), - &ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), - &ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), - &ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + &ty::TyInt(int_ty) => { + use syntax::ast::IntTy::*; + let size = match int_ty { + I8 => 1, + I16 => 2, + I32 => 4, + I64 => 8, + Is => self.memory.pointer_size(), + }; + let n = self.memory.read_int(ptr, size)?; + PrimVal::int_with_size(n, size) + } - &ty::TyInt(IntTy::Is) => self.target_isize_primval(self.memory.read_isize(ptr)?), - &ty::TyUint(UintTy::Us) => self.target_usize_primval(self.memory.read_usize(ptr)?), + &ty::TyUint(uint_ty) => { + use syntax::ast::UintTy::*; + let size = match uint_ty { + U8 => 1, + U16 => 2, + U32 => 4, + U64 => 8, + Us => self.memory.pointer_size(), + }; + let n = self.memory.read_uint(ptr, size)?; + PrimVal::uint_with_size(n, size) + } &ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), &ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), @@ -1026,7 +1109,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let extra = match self.tcx.struct_tail(ty).sty { ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => self.target_usize_primval(self.memory.read_usize(extra)?), + ty::TyStr => self.usize_primval(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; return Ok(Value::ByValPair(PrimVal::Ptr(p), extra)); @@ -1036,16 +1119,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty) { - match (discr.size().bytes(), signed) { - (1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), - (2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), - (4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), - (8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), - (1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), - (2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), - (4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), - (8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), - (size, _) => bug!("CEnum discr size {}", size), + let size = discr.size().bytes() as usize; + if signed { + let n = self.memory.read_int(ptr, size)?; + PrimVal::int_with_size(n, size) + } else { + let n = self.memory.read_uint(ptr, size)?; + PrimVal::uint_with_size(n, size) } } else { bug!("primitive read of non-clike enum: {:?}", ty); @@ -1100,7 +1180,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - self.write_value(src, dest, dest_ty)?; + self.write_value_to_ptr(src, dest, dest_ty)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); @@ -1146,7 +1226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset); let dst_f_ptr = dest.offset(dst_field_offset); if src_fty == dst_fty { - self.move_(src_f_ptr, dst_f_ptr, src_fty)?; + self.copy(src_f_ptr, dst_f_ptr, src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, dst_f_ptr, dst_fty)?; } @@ -1161,10 +1241,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl Lvalue { + fn from_ptr(ptr: Pointer) -> Self { + Lvalue { ptr: ptr, extra: LvalueExtra::None } + } + fn to_ptr(self) -> Pointer { assert_eq!(self.extra, LvalueExtra::None); self.ptr } + fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { match ty.sty { ty::TyArray(elem, n) => (elem, n as u64), @@ -1199,27 +1284,22 @@ pub fn eval_main<'a, 'tcx: 'a>( let mir = mir_map.map.get(&def_id).expect("no mir for main function"); let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); let substs = subst::Substs::empty(tcx); - let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs) + let return_ptr = ecx.alloc_ptr(mir.return_ty, substs) .expect("should at least be able to allocate space for the main function's return value"); - ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, return_ptr, StackPopCleanup::None) - .expect("could not allocate first stack frame"); - - // FIXME: this is a horrible and wrong way to detect the start function, but overwriting the first two locals shouldn't do much - if mir.local_decls.len() > 2 { - // start function - let nargs = ecx.frame_mut().locals[1]; - let args = ecx.frame_mut().locals[2]; - // ignore errors, if the locals are too small this is not the start function - let _ = ecx.memory_mut().write_usize(nargs, 0); - let _ = ecx.memory_mut().write_usize(args, 0); - } + ecx.push_stack_frame( + def_id, + mir.span, + CachedMir::Ref(mir), + substs, + Lvalue::from_ptr(return_ptr), // FIXME(solson) + StackPopCleanup::None + ).expect("could not allocate first stack frame"); for _ in 0..step_limit { match ecx.step() { Ok(true) => {} Ok(false) => return, - // FIXME: diverging functions can end up here in some future miri Err(e) => { report(tcx, &ecx, e); return; diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 77350504306b6..7888257141caa 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -6,6 +6,7 @@ use super::{ CachedMir, ConstantId, EvalContext, + Lvalue, ConstantKind, StackPopCleanup, }; @@ -104,7 +105,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // WARNING: make sure that any methods implemented on this type don't ever access ecx.stack // this includes any method that might access the stack -// basically don't call anything other than `load_mir`, `alloc_ret_ptr`, `push_stack_frame` +// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame` // The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, @@ -127,14 +128,15 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } self.try(|this| { let mir = this.ecx.load_mir(def_id)?; - let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?; + // FIXME(solson): Don't allocate a pointer unconditionally. + let ptr = this.ecx.alloc_ptr(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { StackPopCleanup::Freeze(ptr.alloc_id) } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, ptr, cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::from_ptr(ptr), cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -176,14 +178,15 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; self.try(|this| { - let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?; + // FIXME(solson): Don't allocate a pointer unconditionally. + let return_ptr = this.ecx.alloc_ptr(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, - return_ptr, + Lvalue::from_ptr(return_ptr), StackPopCleanup::Freeze(return_ptr.alloc_id)) }); } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 31e337924adaa..5fd96426b798c 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -5,10 +5,9 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; -use memory::Pointer; -use interpreter::EvalContext; -use primval::{self, PrimVal}; use interpreter::value::Value; +use interpreter::{EvalContext, Lvalue}; +use primval::{self, PrimVal}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -16,7 +15,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], - dest: Pointer, + dest: Lvalue, dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { @@ -24,7 +23,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .map(|arg| self.eval_operand(arg)) .collect(); let args_ptrs = args_ptrs?; - let pointer_size = self.memory.pointer_size(); let i32 = self.tcx.types.i32; let isize = self.tcx.types.isize; let usize = self.tcx.types.usize; @@ -33,15 +31,41 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..]; match intrinsic_name { - "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, - "sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?, - "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, + "add_with_overflow" => { + self.intrinsic_with_overflow( + mir::BinOp::Add, + &args[0], + &args[1], + dest, + dest_layout, + )? + } + + "sub_with_overflow" => { + self.intrinsic_with_overflow( + mir::BinOp::Sub, + &args[0], + &args[1], + dest, + dest_layout, + )? + } + + "mul_with_overflow" => { + self.intrinsic_with_overflow( + mir::BinOp::Mul, + &args[0], + &args[1], + dest, + dest_layout, + )? + } "arith_offset" => { let ptr = args_ptrs[0].read_ptr(&self.memory)?; let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); - self.memory.write_ptr(dest, new_ptr)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr))?; } "assume" => { @@ -72,24 +96,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_ty = substs.type_at(0); let num = self.value_to_primval(args_ptrs[0], elem_ty)?; let num = numeric_intrinsic(intrinsic_name, num); - self.memory.write_primval(dest, num)?; + self.write_primval(dest, num)?; } "discriminant_value" => { let ty = substs.type_at(0); let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.memory.write_uint(dest, discr_val, 8)?; + self.write_primval(dest, PrimVal::U64(discr_val))?; } "fabsf32" => { let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32"); - self.memory.write_f32(dest, f.abs())?; + self.write_primval(dest, PrimVal::F32(f.abs()))?; } "fabsf64" => { let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64"); - self.memory.write_f64(dest, f.abs())?; + self.write_primval(dest, PrimVal::F64(f.abs()))?; } "fadd_fast" => { @@ -97,37 +121,47 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let a = self.value_to_primval(args_ptrs[0], ty)?; let b = self.value_to_primval(args_ptrs[0], ty)?; let result = primval::binary_op(mir::BinOp::Add, a, b)?; - self.memory.write_primval(dest, result.0)?; + self.write_primval(dest, result.0)?; } "likely" | "unlikely" | "forget" => {} - "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, + "init" => { + // FIXME(solson) + let dest = dest.to_ptr(); + + let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; + self.memory.write_repeat(dest, 0, size)?; + } "min_align_of" => { let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty); - self.memory.write_uint(dest, elem_align as u64, pointer_size)?; + let align_val = self.usize_primval(elem_align as u64); + self.write_primval(dest, align_val)?; } "pref_align_of" => { let ty = substs.type_at(0); let layout = self.type_layout(ty); let align = layout.align(&self.tcx.data_layout).pref(); - self.memory.write_uint(dest, align, pointer_size)?; + let align_val = self.usize_primval(align); + self.write_primval(dest, align_val)?; } "move_val_init" => { let ty = substs.type_at(0); let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value(args_ptrs[1], ptr, ty)?; + self.write_value_to_ptr(args_ptrs[1], ptr, ty)?; } "needs_drop" => { let ty = substs.type_at(0); - self.memory.write_bool(dest, self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()))?; + let env = self.tcx.empty_parameter_environment(); + let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); + self.write_primval(dest, PrimVal::Bool(needs_drop))?; } "offset" => { @@ -137,7 +171,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = args_ptrs[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); - self.memory.write_ptr(dest, result_ptr)?; + self.write_primval(dest, PrimVal::Ptr(result_ptr))?; } "overflowing_sub" => { @@ -155,35 +189,37 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "powif32" => { let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("powif32 first arg not f32"); let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif32 second arg not i32"); - self.memory.write_f32(dest, f.powi(i as i32))?; + self.write_primval(dest, PrimVal::F32(f.powi(i as i32)))?; } "powif64" => { let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("powif64 first arg not f64"); let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif64 second arg not i32"); - self.memory.write_f64(dest, f.powi(i as i32))?; + self.write_primval(dest, PrimVal::F64(f.powi(i as i32)))?; } "sqrtf32" => { let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32"); - self.memory.write_f32(dest, f.sqrt())?; + self.write_primval(dest, PrimVal::F32(f.sqrt()))?; } "sqrtf64" => { let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64"); - self.memory.write_f64(dest, f.sqrt())?; + self.write_primval(dest, PrimVal::F64(f.sqrt()))?; } "size_of" => { let ty = substs.type_at(0); let size = self.type_size(ty) as u64; - self.memory.write_uint(dest, size, pointer_size)?; + let size_val = self.usize_primval(size); + self.write_primval(dest, size_val)?; } "size_of_val" => { let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?; - self.memory.write_uint(dest, size, pointer_size)?; + let size_val = self.usize_primval(size); + self.write_primval(dest, size_val)?; } "type_name" => { let ty = substs.type_at(0); @@ -194,7 +230,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); - self.memory.write_uint(dest, n, 8)?; + self.write_primval(dest, PrimVal::U64(n))?; } "transmute" => { @@ -202,20 +238,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(args_ptrs[0], dest, ty)?; } - "try" => unimplemented!(), + "uninit" => { + // FIXME(solson) + let dest = dest.to_ptr(); - "uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?, + let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; + self.memory.mark_definedness(dest, size, false)?; + } "volatile_load" => { let ty = substs.type_at(0); let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.move_(ptr, dest, ty)?; + self.write_value(Value::ByRef(ptr), dest, ty)?; } "volatile_store" => { let ty = substs.type_at(0); let dest = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value(args_ptrs[1], dest, ty)?; + self.write_value_to_ptr(args_ptrs[1], dest, ty)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 5c9123ba91e9c..b455967040553 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -12,7 +12,7 @@ use syntax::{ast, attr}; use error::{EvalError, EvalResult}; use memory::Pointer; use primval::PrimVal; -use super::{EvalContext, IntegerExt, StackPopCleanup}; +use super::{EvalContext, Lvalue, IntegerExt, StackPopCleanup}; use super::value::Value; mod intrinsics; @@ -76,7 +76,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Call { ref func, ref args, ref destination, .. } => { let destination = match *destination { - Some((ref lv, target)) => Some((self.eval_lvalue(lv)?.to_ptr(), target)), + Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)), None => None, }; @@ -143,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy, - destination: Option<(Pointer, mir::BasicBlock)>, + destination: Option<(Lvalue, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx, ()> { @@ -184,15 +184,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let mir = self.load_mir(resolved_def_id)?; - let (return_ptr, return_to_block) = match destination { - Some((ptr, block)) => (ptr, StackPopCleanup::Goto(block)), - None => (Pointer::never_ptr(), StackPopCleanup::None), + let (return_lvalue, return_to_block) = match destination { + Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), + None => { + // FIXME(solson) + let lvalue = Lvalue::from_ptr(Pointer::never_ptr()); + (lvalue, StackPopCleanup::None) + } }; - self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?; + + self.push_stack_frame( + resolved_def_id, + span, + mir, + resolved_substs, + return_lvalue, + return_to_block + )?; for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { // argument start at index 1, since index 0 is reserved for the return allocation let dest = self.frame().locals[i + 1]; + + // FIXME(solson) + let dest = match dest { + Value::ByRef(p) => Lvalue::from_ptr(p), + _ => bug!("all locals should be ByRef until I finish refactoring"), + }; + self.write_value(arg_val, dest, arg_ty)?; } @@ -245,7 +264,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, def_id: DefId, args: &[mir::Operand<'tcx>], - dest: Pointer, + dest: Lvalue, dest_size: usize, ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); @@ -269,10 +288,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { - let size = self.value_to_primval(args[0], usize)?.expect_uint("__rust_allocate first arg not usize"); - let align = self.value_to_primval(args[1], usize)?.expect_uint("__rust_allocate second arg not usize"); + let size = self.value_to_primval(args[0], usize)? + .expect_uint("__rust_allocate first arg not usize"); + let align = self.value_to_primval(args[1], usize)? + .expect_uint("__rust_allocate second arg not usize"); let ptr = self.memory.allocate(size as usize, align as usize)?; - self.memory.write_ptr(dest, ptr)?; + self.write_primval(dest, PrimVal::Ptr(ptr))?; } "__rust_reallocate" => { @@ -280,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; - self.memory.write_ptr(dest, new_ptr)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr))?; } "memcmp" => { @@ -300,7 +321,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - self.memory.write_int(dest, result, dest_size)?; + self.write_primval(dest, PrimVal::int_with_size(result, dest_size))?; } _ => { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 87a0e15cf75e7..178dbac840198 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,5 +1,5 @@ -use memory::{Memory, Pointer}; use error::EvalResult; +use memory::{Memory, Pointer}; use primval::PrimVal; /// A `Value` represents a single self-contained Rust value. @@ -11,15 +11,14 @@ use primval::PrimVal; /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug)] -pub(super) enum Value { +pub enum Value { ByRef(Pointer), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } -impl Value { - - pub(super) fn read_ptr<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { +impl<'a, 'tcx: 'a> Value { + pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), @@ -30,7 +29,7 @@ impl Value { } } - pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + pub(super) fn expect_vtable(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)), @@ -39,7 +38,7 @@ impl Value { } } - pub(super) fn expect_slice_len<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + pub(super) fn expect_slice_len(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { use self::Value::*; match *self { ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), diff --git a/src/primval.rs b/src/primval.rs index 70934c2a549ac..26c1d715c4ce4 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -60,6 +60,28 @@ impl PrimVal { _ => bug!("{}", error_msg), } } + + pub fn uint_with_size(n: u64, size: usize) -> Self { + use self::PrimVal::*; + match size { + 1 => U8(n as u8), + 2 => U16(n as u16), + 4 => U32(n as u32), + 8 => U64(n), + _ => bug!("can't make uint ({}) with size {}", n, size), + } + } + + pub fn int_with_size(n: i64, size: usize) -> Self { + use self::PrimVal::*; + match size { + 1 => I8(n as i8), + 2 => I16(n as i16), + 4 => I32(n as i32), + 8 => I64(n), + _ => bug!("can't make int ({}) with size {}", n, size), + } + } } /// returns the result of the operation and whether the operation overflowed diff --git a/tests/run-pass/start_fn.rs b/tests/run-pass/start_fn.rs deleted file mode 100644 index 8b884e22fedf7..0000000000000 --- a/tests/run-pass/start_fn.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(start)] - -#[start] -fn foo(nargs: isize, args: *const *const u8) -> isize { - if nargs > 0 { - assert!(unsafe{*args} as usize != 0); - } - 0 -} From 8143a05812c56cf8b728f05aff68d2e207bde82a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 14 Oct 2016 03:49:02 -0600 Subject: [PATCH 0555/1096] Implement atomic_{load,store}. --- src/interpreter/terminator/intrinsics.rs | 26 +++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 5fd96426b798c..dbf99cad52dc6 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -75,6 +75,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + "atomic_load" | + "volatile_load" => { + let ty = substs.type_at(0); + let ptr = args_ptrs[0].read_ptr(&self.memory)?; + self.write_value(Value::ByRef(ptr), dest, ty)?; + } + + "atomic_store" | + "volatile_store" => { + let ty = substs.type_at(0); + let dest = args_ptrs[0].read_ptr(&self.memory)?; + self.write_value_to_ptr(args_ptrs[1], dest, ty)?; + } + "breakpoint" => unimplemented!(), // halt miri "copy" | @@ -246,18 +260,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.mark_definedness(dest, size, false)?; } - "volatile_load" => { - let ty = substs.type_at(0); - let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value(Value::ByRef(ptr), dest, ty)?; - } - - "volatile_store" => { - let ty = substs.type_at(0); - let dest = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value_to_ptr(args_ptrs[1], dest, ty)?; - } - name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), } From 3d6dbb89ddc3f60dc589e4a22e8ad5e4ba4889a7 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 14 Oct 2016 03:52:23 -0600 Subject: [PATCH 0556/1096] Fix some long lines. --- src/interpreter/terminator/intrinsics.rs | 39 +++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index dbf99cad52dc6..b25fa7d4bf117 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -63,16 +63,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let ptr = args_ptrs[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("arith_offset second arg not isize"); + let offset = self.value_to_primval(args_ptrs[1], isize)? + .expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); self.write_primval(dest, PrimVal::Ptr(new_ptr))?; } "assume" => { let bool = self.tcx.types.bool; - if !self.value_to_primval(args_ptrs[0], bool)?.expect_bool("assume arg not bool") { - return Err(EvalError::AssumptionNotHeld); - } + let cond = self.value_to_primval(args_ptrs[0], bool)? + .expect_bool("assume arg not bool"); + if !cond { return Err(EvalError::AssumptionNotHeld); } } "atomic_load" | @@ -99,7 +100,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_align = self.type_align(elem_ty); let src = args_ptrs[0].read_ptr(&self.memory)?; let dest = args_ptrs[1].read_ptr(&self.memory)?; - let count = self.value_to_primval(args_ptrs[2], usize)?.expect_uint("arith_offset second arg not isize"); + let count = self.value_to_primval(args_ptrs[2], usize)? + .expect_uint("arith_offset second arg not isize"); self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } @@ -121,12 +123,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fabsf32" => { - let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32"); + let f = self.value_to_primval(args_ptrs[2], f32)? + .expect_f32("fabsf32 read non f32"); self.write_primval(dest, PrimVal::F32(f.abs()))?; } "fabsf64" => { - let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64"); + let f = self.value_to_primval(args_ptrs[2], f64)? + .expect_f64("fabsf64 read non f64"); self.write_primval(dest, PrimVal::F64(f.abs()))?; } @@ -181,7 +185,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); let pointee_size = self.type_size(pointee_ty) as isize; - let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("offset second arg not isize"); + let offset = self.value_to_primval(args_ptrs[1], isize)? + .expect_int("offset second arg not isize"); let ptr = args_ptrs[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); @@ -201,24 +206,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "powif32" => { - let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("powif32 first arg not f32"); - let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif32 second arg not i32"); + let f = self.value_to_primval(args_ptrs[0], f32)? + .expect_f32("powif32 first arg not f32"); + let i = self.value_to_primval(args_ptrs[1], i32)? + .expect_int("powif32 second arg not i32"); self.write_primval(dest, PrimVal::F32(f.powi(i as i32)))?; } "powif64" => { - let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("powif64 first arg not f64"); - let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif64 second arg not i32"); + let f = self.value_to_primval(args_ptrs[0], f64)? + .expect_f64("powif64 first arg not f64"); + let i = self.value_to_primval(args_ptrs[1], i32)? + .expect_int("powif64 second arg not i32"); self.write_primval(dest, PrimVal::F64(f.powi(i as i32)))?; } "sqrtf32" => { - let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32"); + let f = self.value_to_primval(args_ptrs[0], f32)? + .expect_f32("sqrtf32 first arg not f32"); self.write_primval(dest, PrimVal::F32(f.sqrt()))?; } "sqrtf64" => { - let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64"); + let f = self.value_to_primval(args_ptrs[0], f64)? + .expect_f64("sqrtf64 first arg not f64"); self.write_primval(dest, PrimVal::F64(f.sqrt()))?; } From 65d3be0084bed17730ddabee06e1e72b88a7e87a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 14 Oct 2016 22:10:06 -0600 Subject: [PATCH 0557/1096] Add support for local `Value`s in `Lvalue`. The new `Lvalue` has an additional form, `Lvalue::Local`, with the old form being `Lvalue::Ptr`. In an upcoming commit, we will start producing the new form for locals, and enable reading and writing of primitive locals without ever touching `Memory`. Statics should be able to get a similar treatment, where primitive statics can be stored and accessed without touching `Memory`. --- src/interpreter/mod.rs | 282 ++++++++++++++++++++++++----------------- 1 file changed, 166 insertions(+), 116 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index dc787a7608c7d..77d4c3d560cc6 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -42,6 +42,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. + // FIXME(solson): Change from Pointer to Value. statics: HashMap, Pointer>, /// The virtual call stack. @@ -93,13 +94,24 @@ pub struct Frame<'a, 'tcx: 'a> { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Lvalue { - ptr: Pointer, - extra: LvalueExtra, +pub enum Lvalue { + /// An lvalue referring to a value allocated in the `Memory` system. + Ptr { + ptr: Pointer, + extra: LvalueExtra, + }, + + /// An lvalue referring to a value on the stack. + Local { + frame: usize, + local: usize, + } + + // TODO(solson): Static/Const? None/Never? } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum LvalueExtra { +pub enum LvalueExtra { None, Length(u64), Vtable(Pointer), @@ -603,9 +615,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = dest.to_ptr(); let lvalue = self.eval_lvalue(lvalue)?; - self.memory.write_ptr(dest, lvalue.ptr)?; + + // FIXME(solson) + let (ptr, extra) = lvalue.to_ptr_and_extra(); + + self.memory.write_ptr(dest, ptr)?; let extra_ptr = dest.offset(self.memory.pointer_size() as isize); - match lvalue.extra { + match extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?, LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?, @@ -818,12 +834,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; - let ptr = match *lvalue { + match *lvalue { Local(i) => { - match self.frame().locals[i.index()] { + // FIXME(solson): Switch to the following code to start enabling lvalues referring + // to `Value`s placed on the locals stack instead of in `Memory`: + // + // let frame_index = self.stack.len(); + // Ok(Lvalue::Local { frame: frame_index, local: i.index() }) + // + let ptr = match self.frame().locals[i.index()] { Value::ByRef(p) => p, _ => bug!(), - } + }; + Ok(Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }) } Static(def_id) => { @@ -833,119 +856,132 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - *self.statics.get(&cid).expect("static should have been cached (lvalue)") - }, + let ptr = *self.statics.get(&cid) + .expect("static should have been cached (lvalue)"); + Ok(Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }) + } - Projection(ref proj) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty); - - use rustc::mir::repr::ProjectionElem::*; - match proj.elem { - Field(field, field_ty) => { - let field_ty = self.monomorphize(field_ty, self.substs()); - use rustc::ty::layout::Layout::*; - let field = field.index(); - let offset = match *base_layout { - Univariant { ref variant, .. } => variant.offsets[field], - General { ref variants, .. } => { - if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { - // +1 for the discriminant, which is field 0 - variants[variant_idx].offsets[field + 1] - } else { - bug!("field access on enum had no variant index"); - } - } - RawNullablePointer { .. } => { - assert_eq!(field.index(), 0); - return Ok(base); - } - StructWrappedNullablePointer { ref nonnull, .. } => { - nonnull.offsets[field] - } - _ => bug!("field access on non-product type: {:?}", base_layout), - }; + Projection(ref proj) => self.eval_lvalue_projection(proj), + } + } - let ptr = base.ptr.offset(offset.bytes() as isize); - if self.type_is_sized(field_ty) { - ptr + fn eval_lvalue_projection( + &mut self, + proj: &mir::LvalueProjection<'tcx>, + ) -> EvalResult<'tcx, Lvalue> { + let base = self.eval_lvalue(&proj.base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty); + + use rustc::mir::repr::ProjectionElem::*; + let (ptr, extra) = match proj.elem { + Field(field, field_ty) => { + let field_ty = self.monomorphize(field_ty, self.substs()); + let field = field.index(); + + use rustc::ty::layout::Layout::*; + let offset = match *base_layout { + Univariant { ref variant, .. } => variant.offsets[field], + + General { ref variants, .. } => { + if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { + // +1 for the discriminant, which is field 0 + variants[variant_idx].offsets[field + 1] } else { - match base.extra { - LvalueExtra::None => bug!("expected fat pointer"), - LvalueExtra::DowncastVariant(..) => bug!("Rust doesn't support unsized fields in enum variants"), - LvalueExtra::Vtable(_) | - LvalueExtra::Length(_) => {}, - } - return Ok(Lvalue { - ptr: ptr, - extra: base.extra, - }); + bug!("field access on enum had no variant index"); } - }, + } - Downcast(_, variant) => { - use rustc::ty::layout::Layout::*; - match *base_layout { - General { .. } => { - return Ok(Lvalue { - ptr: base.ptr, - extra: LvalueExtra::DowncastVariant(variant), - }); - } - RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { - return Ok(base); - } - _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), - } - }, + RawNullablePointer { .. } => { + assert_eq!(field.index(), 0); + return Ok(base); + } - Deref => { - use primval::PrimVal::*; - use interpreter::value::Value::*; - let (ptr, extra) = match self.read_value(base.ptr, base_ty)? { - ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), - ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), - ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None), - _ => bug!("can't deref non pointer types"), - }; - return Ok(Lvalue { ptr: ptr, extra: extra }); + StructWrappedNullablePointer { ref nonnull, .. } => { + nonnull.offsets[field] } - Index(ref operand) => { - let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); - let n_ptr = self.eval_operand(operand)?; - let usize = self.tcx.types.usize; - let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); - assert!(n < len); - base.ptr.offset(n as isize * elem_size as isize) + _ => bug!("field access on non-product type: {:?}", base_layout), + }; + + let ptr = base_ptr.offset(offset.bytes() as isize); + let extra = if self.type_is_sized(field_ty) { + LvalueExtra::None + } else { + match base_extra { + LvalueExtra::None => bug!("expected fat pointer"), + LvalueExtra::DowncastVariant(..) => + bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::Vtable(_) | + LvalueExtra::Length(_) => {}, } + base_extra + }; - ConstantIndex { offset, min_length, from_end } => { - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); - assert!(n >= min_length as u64); - if from_end { - base.ptr.offset((n as isize - offset as isize) * elem_size as isize) - } else { - base.ptr.offset(offset as isize * elem_size as isize) - } - }, - Subslice { from, to } => { - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); - assert!((from as u64) <= n - (to as u64)); - return Ok(Lvalue { - ptr: base.ptr.offset(from as isize * elem_size as isize), - extra: LvalueExtra::Length(n - to as u64 - from as u64), - }) - }, + (ptr, extra) + } + + Downcast(_, variant) => { + use rustc::ty::layout::Layout::*; + let extra = match *base_layout { + General { .. } => LvalueExtra::DowncastVariant(variant), + RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra, + _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), + }; + (base_ptr, extra) + } + + Deref => { + use primval::PrimVal::*; + use interpreter::value::Value::*; + match self.read_value(base_ptr, base_ty)? { + ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), + ByValPair(Ptr(ptr), n) => + (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), + ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None), + _ => bug!("can't deref non pointer types"), } } + + Index(ref operand) => { + let (elem_ty, len) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); + let n_ptr = self.eval_operand(operand)?; + let usize = self.tcx.types.usize; + let n = self.value_to_primval(n_ptr, usize)? + .expect_uint("Projection::Index expected usize"); + assert!(n < len); + let ptr = base_ptr.offset(n as isize * elem_size as isize); + (ptr, LvalueExtra::None) + } + + ConstantIndex { offset, min_length, from_end } => { + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); + assert!(n >= min_length as u64); + + let index = if from_end { + n as isize - offset as isize + } else { + offset as isize + }; + + let ptr = base_ptr.offset(index * elem_size as isize); + (ptr, LvalueExtra::None) + } + + Subslice { from, to } => { + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty); + assert!((from as u64) <= n - (to as u64)); + let ptr = base_ptr.offset(from as isize * elem_size as isize); + let extra = LvalueExtra::Length(n - to as u64 - from as u64); + (ptr, extra) + } }; - Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) + Ok(Lvalue::Ptr { ptr: ptr, extra: extra }) } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { @@ -1242,22 +1278,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { impl Lvalue { fn from_ptr(ptr: Pointer) -> Self { - Lvalue { ptr: ptr, extra: LvalueExtra::None } + Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + } + + fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { + if let Lvalue::Ptr { ptr, extra } = self { + (ptr, extra) + } else { + // FIXME(solson): This isn't really a bug, but it's unhandled until I finish + // refactoring. + bug!("from_ptr: Not an Lvalue::Ptr"); + } } fn to_ptr(self) -> Pointer { - assert_eq!(self.extra, LvalueExtra::None); - self.ptr + let (ptr, extra) = self.to_ptr_and_extra(); + assert_eq!(extra, LvalueExtra::None); + ptr } fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { match ty.sty { ty::TyArray(elem, n) => (elem, n as u64), - ty::TySlice(elem) => if let LvalueExtra::Length(len) = self.extra { - (elem, len) - } else { - bug!("elem_ty_and_len called on a slice given non-slice lvalue: {:?}", self); - }, + + ty::TySlice(elem) => { + match self { + Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len), + _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self), + } + } + _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), } } From 00ae07be075d313f077c383927bc1daa45c752b0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 14 Oct 2016 22:59:50 -0600 Subject: [PATCH 0558/1096] Update for changes in rustc. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8598a8e7c5719..2f2894a59ee20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ btree_range, collections, collections_bound, - question_mark, rustc_private, pub_restricted, )] From 6c463b7562f6ff15db3bc90717c3c2da3d6967dd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 15 Oct 2016 19:48:30 -0600 Subject: [PATCH 0559/1096] Hold an Lvalue for the return pointer in a frame. Previously ReturnPointer was just the first slot in the locals array, which had type `Vec`. But after my recent refactoring, locals is `Vec` and it became increasingly hacky to pull a pointer out of the first slot to be the value. Besides, that hack wouldn't allow ReturnPointer to ever be an `Lvalue::Local`, referring directly to a local on a higher stack frame. Now ReturnPointer has no presence in the locals array, instead being upgraded to its own field on `Frame`. This introduces a couple of new hacks, detailed by some of my FIXME comments, so that I could get the tests passing again and commit. More commits coming soon should clean up these hacks without much trouble, and overall I feel that the code is converging on a cleaner, more efficient design. --- src/interpreter/mod.rs | 217 +++++++++++++++++------ src/interpreter/terminator/intrinsics.rs | 4 +- src/interpreter/terminator/mod.rs | 25 ++- tests/compile-fail/oom2.rs | 14 +- 4 files changed, 186 insertions(+), 74 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 77d4c3d560cc6..7691d2a047149 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -12,7 +12,6 @@ use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; -use std::iter; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -71,14 +70,18 @@ pub struct Frame<'a, 'tcx: 'a> { pub span: codemap::Span, //////////////////////////////////////////////////////////////////////////////// - // Return pointer and local allocations + // Return lvalue and locals //////////////////////////////////////////////////////////////////////////////// /// The block to return to when returning from the current stack frame pub return_to_block: StackPopCleanup, - /// The list of locals for the current function, stored in order as - /// `[return_ptr, arguments..., variables..., temporaries...]`. + /// The location where the result of the current stack frame should be written to. + pub return_lvalue: Lvalue, + + /// The list of locals for this stack frame, stored in order as + /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which + /// can either directly contain `PrimVal` or refer to some part of an `Allocation`. pub locals: Vec, //////////////////////////////////////////////////////////////////////////////// @@ -101,10 +104,11 @@ pub enum Lvalue { extra: LvalueExtra, }, - /// An lvalue referring to a value on the stack. + /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with + /// a Mir local index. Local { frame: usize, - local: usize, + local: mir::Local, } // TODO(solson): Static/Const? None/Never? @@ -345,33 +349,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_lvalue: Lvalue, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx, ()> { - let local_tys = mir.local_decls.iter().map(|a| a.ty); - ::log_settings::settings().indentation += 1; - // FIXME(solson) - let return_ptr = return_lvalue.to_ptr(); + // Skip 1 because we don't make a slot for the ReturnPointer, which is local number zero. + // The ReturnPointer is represented by `return_lvalue`, and points to an allocation or a + // local in a higher stack frame. + // + // FIXME(solson): Write this in a way that doesn't assume ReturnPointer is local 0. + let local_tys = mir.local_decls.iter().map(|a| a.ty).skip(1); - // directly change the first allocation (the return value) to *be* the allocation where the - // caller stores the result - let locals: EvalResult<'tcx, Vec> = iter::once(Ok(Value::ByRef(return_ptr))).chain(local_tys.skip(1).map(|ty| { + let locals: EvalResult<'tcx, Vec> = local_tys.map(|ty| { let size = self.type_size_with_substs(ty, substs); let align = self.type_align_with_substs(ty, substs); // FIXME(solson) self.memory.allocate(size, align).map(Value::ByRef) - })).collect(); + }).collect(); self.stack.push(Frame { mir: mir.clone(), block: mir::START_BLOCK, return_to_block: return_to_block, + return_lvalue: return_lvalue, locals: locals?, span: span, def_id: def_id, substs: substs, stmt: 0, }); + if self.stack.len() > self.stack_limit { Err(EvalError::StackFrameLimitReached) } else { @@ -410,7 +416,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let offset = tup_layout.offsets[1].bytes() as isize; self.memory.write_bool(dest.offset(offset), overflowed) @@ -439,7 +445,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { operands: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, ()> { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); for (offset, operand) in offsets.into_iter().zip(operands) { let value = self.eval_operand(operand)?; @@ -508,6 +514,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let discr_offset = variants[variant].offsets[0].bytes() as isize; // FIXME(solson) + let dest = self.force_allocation(dest)?; let discr_dest = (dest.to_ptr()).offset(discr_offset); self.memory.write_uint(discr_dest, discr_val, discr_size)?; @@ -552,7 +559,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); try!(self.memory.write_isize(dest, 0)); @@ -568,8 +575,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = adt_def.variants[variant].disr_val.to_u64_unchecked(); let size = discr.size().bytes() as usize; - // FIXME(solson) - let dest = dest.to_ptr(); + // easy FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); if signed { self.memory.write_int(dest, val as i64, size)?; @@ -594,7 +601,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); @@ -612,12 +619,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let lvalue = self.eval_lvalue(lvalue)?; // FIXME(solson) - let (ptr, extra) = lvalue.to_ptr_and_extra(); + let (ptr, extra) = self.force_allocation(lvalue)?.to_ptr_and_extra(); self.memory.write_ptr(dest, ptr)?; let extra_ptr = dest.offset(self.memory.pointer_size() as isize); @@ -632,7 +639,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let size = self.type_size(ty); let align = self.type_align(ty); @@ -642,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Cast(kind, ref operand, cast_ty) => { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); use rustc::mir::repr::CastKind::*; @@ -792,7 +799,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), + Consume(ref lvalue) => { + let val = match self.eval_lvalue(lvalue)? { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + Value::ByRef(ptr) + } + Lvalue::Local { frame, local } => { + self.stack[frame].get_local(local) + } + }; + Ok(val) + } Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; @@ -835,18 +853,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; match *lvalue { - Local(i) => { - // FIXME(solson): Switch to the following code to start enabling lvalues referring - // to `Value`s placed on the locals stack instead of in `Memory`: - // - // let frame_index = self.stack.len(); - // Ok(Lvalue::Local { frame: frame_index, local: i.index() }) - // - let ptr = match self.frame().locals[i.index()] { - Value::ByRef(p) => p, - _ => bug!(), - }; - Ok(Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }) + Local(i) if self.frame().mir.local_kind(i) == mir::LocalKind::ReturnPointer => { + Ok(self.frame().return_lvalue) + } + + Local(local) => { + let frame = self.stack.len() - 1; + Ok(Lvalue::Local { frame: frame, local: local }) } Static(def_id) => { @@ -870,6 +883,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { proj: &mir::LvalueProjection<'tcx>, ) -> EvalResult<'tcx, Lvalue> { let base = self.eval_lvalue(&proj.base)?; + + // FIXME(solson): Is this always necessary? + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -999,6 +1016,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn force_allocation(&mut self, lvalue: Lvalue) -> EvalResult<'tcx, Lvalue> { + let new_lvalue = match lvalue { + Lvalue::Local { frame, local } => { + let ptr = match self.stack[frame].get_local(local) { + Value::ByRef(ptr) => ptr, + val => { + let ty = self.stack[frame].mir.local_decls[local].ty; + let substs = self.stack[frame].substs; + let ptr = self.alloc_ptr(ty, substs)?; + self.write_value_to_ptr(val, ptr, ty)?; + self.stack[frame].set_local(local, Value::ByRef(ptr)); + ptr + } + }; + Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + } + Lvalue::Ptr { .. } => lvalue, + }; + Ok(new_lvalue) + } + // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can // remove it as soon as PrimVal can represent fat pointers. fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { @@ -1033,8 +1071,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), }, - // TODO(solson): Sanity-check the primval type against the input type. - Value::ByVal(primval) => Ok(primval), + // FIXME(solson): This unnecessarily allocates to work around a new issue my `Value` + // locals refactoring introduced. There is code that calls this function and expects to + // get a PrimVal reflecting the specific type that it asked for, e.g. `PrimVal::Bool` + // when it was asking for `TyBool`. This used to always work because it would go + // through `read_value` which does the right thing. + // + // This is the comment and implementation from before my refactor: + // + // TODO(solson): Sanity-check the primval type against the input type. + // Value::ByVal(primval) => Ok(primval), + // + // Turns out sanity-checking isn't enough now, and we need conversion. + // + // Now that we can possibly be reading a `ByVal` straight out of the locals vec, if the + // user did something tricky like transmuting a `u8` to a `bool`, then we'll have a + // `PrimVal::U8` and need to convert to `PrimVal::Bool`. + // + // I want to avoid handling the full set of conversions between `PrimVal`s, so for now + // I will use this hack. I have a plan to change the representation of `PrimVal` to be + // more like a small piece of memory tagged with a `PrimValKind`, which should make the + // conversion easy and make the problem solveable using code already in `Memory`. + Value::ByVal(primval) => { + let substs = self.substs(); + let ptr = self.alloc_ptr(ty, substs)?; + self.memory.write_primval(ptr, primval)?; + self.value_to_primval(Value::ByRef(ptr), ty) + } + Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), } } @@ -1044,10 +1108,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue, val: PrimVal, ) -> EvalResult<'tcx, ()> { - // FIXME(solson) - let dest = dest.to_ptr(); - - self.memory.write_primval(dest, val) + match dest { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + self.memory.write_primval(ptr, val) + } + Lvalue::Local { frame, local } => { + self.stack[frame].set_local(local, Value::ByVal(val)); + Ok(()) + } + } } fn write_value( @@ -1056,9 +1126,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { - // FIXME(solson) - let dest = dest.to_ptr(); - self.write_value_to_ptr(value, dest, dest_ty) + match dest { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + self.write_value_to_ptr(value, ptr, dest_ty)?; + } + Lvalue::Local { frame, local } => { + if let Value::ByRef(src_ptr) = value { + let dest_ptr = if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { + ptr + } else { + let substs = self.substs(); + let ptr = self.alloc_ptr(dest_ty, substs)?; + self.stack[frame].set_local(local, Value::ByRef(ptr)); + ptr + }; + self.copy(src_ptr, dest_ptr, dest_ty)?; + } else { + // FIXME(solson): Is it safe to free the existing local here? + self.stack[frame].set_local(local, value); + } + } + } + Ok(()) } fn write_value_to_ptr( @@ -1276,18 +1366,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } +impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { + fn get_local(&self, local: mir::Local) -> Value { + // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. + self.locals[local.index() - 1] + } + + fn set_local(&mut self, local: mir::Local, value: Value) { + // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. + self.locals[local.index() - 1] = value; + } +} + impl Lvalue { fn from_ptr(ptr: Pointer) -> Self { Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } } fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { - if let Lvalue::Ptr { ptr, extra } = self { - (ptr, extra) - } else { - // FIXME(solson): This isn't really a bug, but it's unhandled until I finish - // refactoring. - bug!("from_ptr: Not an Lvalue::Ptr"); + match self { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), + } } @@ -1348,7 +1448,18 @@ pub fn eval_main<'a, 'tcx: 'a>( for _ in 0..step_limit { match ecx.step() { - Ok(true) => {} + Ok(true) => { + let limit = 5; + for (frame_index, frame) in ecx.stack.iter().enumerate() { + trace!("frame[{}]:", frame_index); + for (i, v) in frame.locals.iter().enumerate().take(limit) { + trace!(" _{}: {:?}", i + 1, v); + } + if frame.locals.len() > limit { + trace!(" ..."); + } + } + } Ok(false) => return, Err(e) => { report(tcx, &ecx, e); diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index b25fa7d4bf117..236bb5ca00bf7 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -148,7 +148,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; self.memory.write_repeat(dest, 0, size)?; @@ -265,7 +265,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "uninit" => { // FIXME(solson) - let dest = dest.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr(); let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; self.memory.mark_definedness(dest, size, false)?; diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index b455967040553..171ba9590e5c3 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -41,7 +41,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + // FIXME(solson) + let lvalue = self.eval_lvalue(discr)?; + let lvalue = self.force_allocation(lvalue)?; + + let discr_ptr = lvalue.to_ptr(); let discr_ty = self.lvalue_ty(discr); let discr_val = self.read_value(discr_ptr, discr_ty)?; let discr_prim = self.value_to_primval(discr_val, discr_ty)?; @@ -62,7 +66,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Switch { ref discr, ref targets, adt_def } => { - let adt_ptr = self.eval_lvalue(discr)?.to_ptr(); + // FIXME(solson) + let lvalue = self.eval_lvalue(discr)?; + let lvalue = self.force_allocation(lvalue)?; + + let adt_ptr = lvalue.to_ptr(); let adt_ty = self.lvalue_ty(discr); let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; let matching = adt_def.variants.iter() @@ -102,7 +110,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Drop { ref location, target, .. } => { - let ptr = self.eval_lvalue(location)?.to_ptr(); + // FIXME(solson) + let lvalue = self.eval_lvalue(location)?; + let lvalue = self.force_allocation(lvalue)?; + + let ptr = lvalue.to_ptr(); let ty = self.lvalue_ty(location); self.drop(ptr, ty)?; self.goto_block(target); @@ -202,9 +214,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block )?; - for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { - // argument start at index 1, since index 0 is reserved for the return allocation - let dest = self.frame().locals[i + 1]; + let arg_locals = self.frame().mir.args_iter(); + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + // FIXME(solson) + let dest = self.frame().get_local(arg_local); // FIXME(solson) let dest = match dest { diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index ac4b3a6674484..d6eb3f3ae0283 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -3,7 +3,7 @@ fn bar(i: i32) { if i < 1000 { - bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory + bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 1000 byte memory //~^NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar @@ -25,18 +25,6 @@ fn bar(i: i32) { //~|NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar } } From 49e6c57ef9085bc5d741db120b2a79dbc1296e31 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 15 Oct 2016 23:31:42 -0600 Subject: [PATCH 0560/1096] Do not pre-allocate local variables. Thanks to the `Value` locals refactoring, now primitive locals (ints, floats, chars, bools, and the like) will not require `Allocation`s at all, and locals that are never initialized at all because of conditional control flow won't be wasting memory. --- src/interpreter/mod.rs | 69 ++++++++++++++----------------- src/interpreter/terminator/mod.rs | 10 +---- src/memory.rs | 5 +++ tests/compile-fail/oom.rs | 8 +--- tests/compile-fail/oom2.rs | 10 ++++- 5 files changed, 48 insertions(+), 54 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7691d2a047149..3e4d3fb1ab50a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -82,7 +82,9 @@ pub struct Frame<'a, 'tcx: 'a> { /// The list of locals for this stack frame, stored in order as /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which /// can either directly contain `PrimVal` or refer to some part of an `Allocation`. - pub locals: Vec, + /// + /// Before being initialized, a local is simply marked as None. + pub locals: Vec>, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -351,27 +353,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation += 1; - // Skip 1 because we don't make a slot for the ReturnPointer, which is local number zero. - // The ReturnPointer is represented by `return_lvalue`, and points to an allocation or a - // local in a higher stack frame. - // - // FIXME(solson): Write this in a way that doesn't assume ReturnPointer is local 0. - let local_tys = mir.local_decls.iter().map(|a| a.ty).skip(1); - - let locals: EvalResult<'tcx, Vec> = local_tys.map(|ty| { - let size = self.type_size_with_substs(ty, substs); - let align = self.type_align_with_substs(ty, substs); - - // FIXME(solson) - self.memory.allocate(size, align).map(Value::ByRef) - }).collect(); + // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local + // `Value` for that. + let num_locals = mir.local_decls.len() - 1; + let locals = vec![None; num_locals]; self.stack.push(Frame { mir: mir.clone(), block: mir::START_BLOCK, return_to_block: return_to_block, return_lvalue: return_lvalue, - locals: locals?, + locals: locals, span: span, def_id: def_id, substs: substs, @@ -800,16 +792,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::Operand::*; match *op { Consume(ref lvalue) => { - let val = match self.eval_lvalue(lvalue)? { + match self.eval_lvalue(lvalue)? { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Value::ByRef(ptr) + Ok(Value::ByRef(ptr)) } Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local) + self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) } - }; - Ok(val) + } } Constant(mir::Constant { ref literal, ty, .. }) => { @@ -850,16 +841,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { + fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; - match *lvalue { - Local(i) if self.frame().mir.local_kind(i) == mir::LocalKind::ReturnPointer => { - Ok(self.frame().return_lvalue) - } + let lvalue = match *mir_lvalue { + Local(mir::RETURN_POINTER) => self.frame().return_lvalue, Local(local) => { - let frame = self.stack.len() - 1; - Ok(Lvalue::Local { frame: frame, local: local }) + Lvalue::Local { + frame: self.stack.len() - 1, + local: local, + } } Static(def_id) => { @@ -871,11 +862,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let ptr = *self.statics.get(&cid) .expect("static should have been cached (lvalue)"); - Ok(Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None }) + Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } } - Projection(ref proj) => self.eval_lvalue_projection(proj), - } + Projection(ref proj) => return self.eval_lvalue_projection(proj), + }; + Ok(lvalue) } fn eval_lvalue_projection( @@ -1020,13 +1012,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let new_lvalue = match lvalue { Lvalue::Local { frame, local } => { let ptr = match self.stack[frame].get_local(local) { - Value::ByRef(ptr) => ptr, - val => { + Some(Value::ByRef(ptr)) => ptr, + opt_val => { let ty = self.stack[frame].mir.local_decls[local].ty; let substs = self.stack[frame].substs; let ptr = self.alloc_ptr(ty, substs)?; - self.write_value_to_ptr(val, ptr, ty)?; self.stack[frame].set_local(local, Value::ByRef(ptr)); + if let Some(val) = opt_val { + self.write_value_to_ptr(val, ptr, ty)?; + } ptr } }; @@ -1133,7 +1127,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Lvalue::Local { frame, local } => { if let Value::ByRef(src_ptr) = value { - let dest_ptr = if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { + let dest_val = self.stack[frame].get_local(local); + let dest_ptr = if let Some(Value::ByRef(ptr)) = dest_val { ptr } else { let substs = self.substs(); @@ -1367,14 +1362,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { - fn get_local(&self, local: mir::Local) -> Value { + fn get_local(&self, local: mir::Local) -> Option { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1] } fn set_local(&mut self, local: mir::Local, value: Value) { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] = value; + self.locals[local.index() - 1] = Some(value); } } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 171ba9590e5c3..4a1e02c39cc80 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -216,15 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_locals = self.frame().mir.args_iter(); for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { - // FIXME(solson) - let dest = self.frame().get_local(arg_local); - - // FIXME(solson) - let dest = match dest { - Value::ByRef(p) => Lvalue::from_ptr(p), - _ => bug!("all locals should be ByRef until I finish refactoring"), - }; - + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; } diff --git a/src/memory.rs b/src/memory.rs index da1214acfb8ad..a1d430d96a84c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -52,9 +52,11 @@ impl Pointer { pub fn offset(self, i: isize) -> Self { Pointer { offset: (self.offset as isize + i) as usize, ..self } } + pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } + pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> { match self.alloc_id { NEVER_ALLOC_ID | @@ -62,18 +64,21 @@ impl Pointer { _ => Err(EvalError::ReadPointerAsBytes), } } + pub fn from_int(i: usize) -> Self { Pointer { alloc_id: ZST_ALLOC_ID, offset: i, } } + fn zst_ptr() -> Self { Pointer { alloc_id: ZST_ALLOC_ID, offset: 0, } } + pub fn never_ptr() -> Self { Pointer { alloc_id: NEVER_ALLOC_ID, diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index be56240af4767..1fd2c4b2bd6aa 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -1,11 +1,7 @@ #![feature(custom_attribute, attr_literals)] #![miri(memory_size=0)] -fn bar() { - let x = 5; - assert_eq!(x, 6); -} - fn main() { - bar(); //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory + let _x = [42; 10]; + //~^ERROR tried to allocate 40 more bytes, but only 0 bytes are free of the 0 byte memory } diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index d6eb3f3ae0283..b89231e451581 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -2,8 +2,8 @@ #![miri(memory_size=1000)] fn bar(i: i32) { - if i < 1000 { - bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 1000 byte memory + if i < 1000 { //~ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory + bar(i + 1) //~^NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar @@ -25,6 +25,12 @@ fn bar(i: i32) { //~|NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar } } From 754dcc401d6262309d53c16ca640b1922ffea9ca Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 15 Oct 2016 23:59:01 -0600 Subject: [PATCH 0561/1096] Do not force_allocate SwitchInt discrs. --- src/interpreter/mod.rs | 24 +++++++++++++----------- src/interpreter/terminator/mod.rs | 7 +------ 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3e4d3fb1ab50a..bbba52de52728 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -791,17 +791,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => { - match self.eval_lvalue(lvalue)? { - Lvalue::Ptr { ptr, extra } => { - assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) - } - Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) - } - } - } + Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; @@ -841,6 +831,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + match self.eval_lvalue(lvalue)? { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + Ok(Value::ByRef(ptr)) + } + Lvalue::Local { frame, local } => { + self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) + } + } + } + fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; let lvalue = match *mir_lvalue { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 4a1e02c39cc80..132aa101f87ef 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -41,13 +41,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - // FIXME(solson) - let lvalue = self.eval_lvalue(discr)?; - let lvalue = self.force_allocation(lvalue)?; - - let discr_ptr = lvalue.to_ptr(); + let discr_val = self.eval_and_read_lvalue(discr)?; let discr_ty = self.lvalue_ty(discr); - let discr_val = self.read_value(discr_ptr, discr_ty)?; let discr_prim = self.value_to_primval(discr_val, discr_ty)?; // Branch to the `otherwise` case by default, if no match is found. From 197e89bbb095d61e6f7c89421789bd7cb4122852 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 00:12:11 -0600 Subject: [PATCH 0562/1096] Refactor alloc_ptr. --- src/interpreter/mod.rs | 22 +++++++++++----------- src/interpreter/step.rs | 4 ++-- src/memory.rs | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bbba52de52728..4f702c68dc553 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -175,7 +175,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn alloc_ptr( + pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { + let substs = self.substs(); + self.alloc_ptr_with_substs(ty, substs) + } + + pub fn alloc_ptr_with_substs( &mut self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx> @@ -1018,7 +1023,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { opt_val => { let ty = self.stack[frame].mir.local_decls[local].ty; let substs = self.stack[frame].substs; - let ptr = self.alloc_ptr(ty, substs)?; + let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].set_local(local, Value::ByRef(ptr)); if let Some(val) = opt_val { self.write_value_to_ptr(val, ptr, ty)?; @@ -1089,8 +1094,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // more like a small piece of memory tagged with a `PrimValKind`, which should make the // conversion easy and make the problem solveable using code already in `Memory`. Value::ByVal(primval) => { - let substs = self.substs(); - let ptr = self.alloc_ptr(ty, substs)?; + let ptr = self.alloc_ptr(ty)?; self.memory.write_primval(ptr, primval)?; self.value_to_primval(Value::ByRef(ptr), ty) } @@ -1133,8 +1137,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_ptr = if let Some(Value::ByRef(ptr)) = dest_val { ptr } else { - let substs = self.substs(); - let ptr = self.alloc_ptr(dest_ty, substs)?; + let ptr = self.alloc_ptr(dest_ty)?; self.stack[frame].set_local(local, Value::ByRef(ptr)); ptr }; @@ -1430,16 +1433,13 @@ pub fn eval_main<'a, 'tcx: 'a>( ) { let mir = mir_map.map.get(&def_id).expect("no mir for main function"); let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); - let substs = subst::Substs::empty(tcx); - let return_ptr = ecx.alloc_ptr(mir.return_ty, substs) - .expect("should at least be able to allocate space for the main function's return value"); ecx.push_stack_frame( def_id, mir.span, CachedMir::Ref(mir), - substs, - Lvalue::from_ptr(return_ptr), // FIXME(solson) + subst::Substs::empty(tcx), + Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None ).expect("could not allocate first stack frame"); diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 7888257141caa..c109b7cd4895d 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -129,7 +129,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let mir = this.ecx.load_mir(def_id)?; // FIXME(solson): Don't allocate a pointer unconditionally. - let ptr = this.ecx.alloc_ptr(mir.return_ty, substs)?; + let ptr = this.ecx.alloc_ptr_with_substs(mir.return_ty, substs)?; this.ecx.statics.insert(cid.clone(), ptr); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { StackPopCleanup::Freeze(ptr.alloc_id) @@ -179,7 +179,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let return_ty = mir.return_ty; self.try(|this| { // FIXME(solson): Don't allocate a pointer unconditionally. - let return_ptr = this.ecx.alloc_ptr(return_ty, cid.substs)?; + let return_ptr = this.ecx.alloc_ptr_with_substs(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); this.ecx.statics.insert(cid.clone(), return_ptr); this.ecx.push_stack_frame(this.def_id, diff --git a/src/memory.rs b/src/memory.rs index a1d430d96a84c..a212f25fedd0a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -72,7 +72,7 @@ impl Pointer { } } - fn zst_ptr() -> Self { + pub fn zst_ptr() -> Self { Pointer { alloc_id: ZST_ALLOC_ID, offset: 0, From abf3e048ad28780374bf2fa3b8f56c3a3bd526ec Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 00:12:27 -0600 Subject: [PATCH 0563/1096] Do not force_allocate Box destination. --- src/interpreter/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4f702c68dc553..461e4d602af72 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -635,13 +635,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Box(ty) => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - self.memory.write_ptr(dest, ptr)?; + let ptr = self.alloc_ptr(ty)?; + self.write_primval(dest, PrimVal::Ptr(ptr))?; } Cast(kind, ref operand, cast_ty) => { From 268bf9c185c59063d0847d4817a82c6c71723971 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 00:41:25 -0600 Subject: [PATCH 0564/1096] Do not force_allocate CEnum destination. --- src/interpreter/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 461e4d602af72..64626db741d9a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -569,17 +569,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { discr, signed, .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let val = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let n = adt_def.variants[variant].disr_val.to_u64_unchecked(); let size = discr.size().bytes() as usize; - // easy FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - - if signed { - self.memory.write_int(dest, val as i64, size)?; + let val = if signed { + PrimVal::int_with_size(n as i64, size) } else { - self.memory.write_uint(dest, val, size)?; - } + PrimVal::uint_with_size(n, size) + }; + + self.write_primval(dest, val)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); } From c1b97f1440f370e23de861e9d20745cd829abc27 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 02:12:26 -0600 Subject: [PATCH 0565/1096] Pass thin self ptr to virtual calls. --- src/interpreter/terminator/intrinsics.rs | 2 +- src/interpreter/terminator/mod.rs | 3 ++- src/interpreter/value.rs | 15 +++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 236bb5ca00bf7..49cef471dab15 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -352,7 +352,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } ty::TyTrait(..) => { - let vtable = value.expect_vtable(&self.memory)?; + let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?; let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?; diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 132aa101f87ef..1555f22efb23c 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -452,7 +452,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { - let vtable = first_arg.expect_vtable(&self.memory)?; + let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; + *first_arg = Value::ByVal(PrimVal::Ptr(self_ptr)); let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 178dbac840198..b89210c065d38 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -29,12 +29,19 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn expect_vtable(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + pub(super) fn expect_ptr_vtable_pair( + &self, + mem: &Memory<'a, 'tcx> + ) -> EvalResult<'tcx, (Pointer, Pointer)> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)), - ByValPair(_, PrimVal::Ptr(vtable)) => Ok(vtable), - _ => unimplemented!(), + ByRef(ptr) => { + let ptr = mem.read_ptr(ptr)?; + let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?; + Ok((ptr, vtable)) + } + ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)) => Ok((ptr, vtable)), + _ => bug!("expected ptr and vtable, got {:?}", self), } } From e4f5b4b39a3cbbf87ecd639a9966fba9d0f71c3c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 02:12:46 -0600 Subject: [PATCH 0566/1096] Do not force_allocate Ref destination. --- src/interpreter/mod.rs | 62 +++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 64626db741d9a..261b40ede015e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -614,23 +614,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ref(_, _, ref lvalue) => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - - let lvalue = self.eval_lvalue(lvalue)?; + let src = self.eval_lvalue(lvalue)?; + let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); + let ptr = PrimVal::Ptr(raw_ptr); - // FIXME(solson) - let (ptr, extra) = self.force_allocation(lvalue)?.to_ptr_and_extra(); - - self.memory.write_ptr(dest, ptr)?; - let extra_ptr = dest.offset(self.memory.pointer_size() as isize); - match extra { - LvalueExtra::None => {}, - LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?, - LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?, + let val = match extra { + LvalueExtra::None => Value::ByVal(ptr), + LvalueExtra::Length(len) => Value::ByValPair(ptr, self.usize_primval(len)), + LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), - } + }; + + self.write_value(val, dest, dest_ty)?; } Box(ty) => { @@ -774,10 +770,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(variant.offsets[field_index]) } FatPointer { .. } => { - let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size(); + let bytes = field_index * self.memory.pointer_size(); Ok(Size::from_bytes(bytes as u64)) } - _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))), + _ => { + let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); + Err(EvalError::Unimplemented(msg)) + } + } + } + + fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { + let layout = self.type_layout(ty); + + use rustc::ty::layout::Layout::*; + match *layout { + Univariant { ref variant, .. } => Ok(variant.offsets.len()), + FatPointer { .. } => Ok(2), + _ => { + let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); + Err(EvalError::Unimplemented(msg)) + } } } @@ -1155,18 +1168,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), Value::ByValPair(a, b) => { - self.memory.write_primval(dest, a)?; - let layout = self.type_layout(dest_ty); - let offset = match *layout { - Layout::Univariant { .. } => { - bug!("I don't think this can ever happen until we have custom fat pointers"); - //variant.field_offset(1).bytes() as isize - }, - Layout::FatPointer { .. } => self.memory.pointer_size() as isize, - _ => bug!("tried to write value pair of non-fat pointer type: {:?}", layout), - }; - let extra_dest = dest.offset(offset); - self.memory.write_primval(extra_dest, b) + assert_eq!(self.get_field_count(dest_ty)?, 2); + let field_0 = self.get_field_offset(dest_ty, 0)?.bytes() as isize; + let field_1 = self.get_field_offset(dest_ty, 1)?.bytes() as isize; + self.memory.write_primval(dest.offset(field_0), a)?; + self.memory.write_primval(dest.offset(field_1), b) } } } From 55f2164bcdbf2748aca988d867ab2b767fb83547 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 02:57:59 -0600 Subject: [PATCH 0567/1096] Do not force_allocate Deref base. This makes `eval_lvalue` a bit less DRY for now, but it will be easier to remove force_allocate in more places piecewise. --- src/interpreter/mod.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 261b40ede015e..a681e64d32c0a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -889,17 +889,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { proj: &mir::LvalueProjection<'tcx>, ) -> EvalResult<'tcx, Lvalue> { let base = self.eval_lvalue(&proj.base)?; - - // FIXME(solson): Is this always necessary? - let base = self.force_allocation(base)?; - - let (base_ptr, base_extra) = base.to_ptr_and_extra(); let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; let (ptr, extra) = match proj.elem { Field(field, field_ty) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let field_ty = self.monomorphize(field_ty, self.substs()); let field = field.index(); @@ -946,6 +945,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Downcast(_, variant) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + use rustc::ty::layout::Layout::*; let extra = match *base_layout { General { .. } => LvalueExtra::DowncastVariant(variant), @@ -958,7 +961,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { use primval::PrimVal::*; use interpreter::value::Value::*; - match self.read_value(base_ptr, base_ty)? { + + let val = match self.eval_and_read_lvalue(&proj.base)? { + ByRef(ptr) => self.read_value(ptr, base_ty)?, + v => v, + }; + + match val { ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), @@ -968,6 +977,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Index(ref operand) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + let (elem_ty, len) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty); let n_ptr = self.eval_operand(operand)?; @@ -980,6 +993,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ConstantIndex { offset, min_length, from_end } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty); assert!(n >= min_length as u64); @@ -995,6 +1012,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Subslice { from, to } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty); assert!((from as u64) <= n - (to as u64)); From b1094f6c1e59f5cc87c354c7790cbf96c377b53f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 03:21:41 -0600 Subject: [PATCH 0568/1096] Deallocate primval conversion hack allocs. It's a hack, sure, but it should learn some manners. --- src/interpreter/mod.rs | 4 +++- tests/compile-fail/oom2.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a681e64d32c0a..d9d3cba31c0d2 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1124,7 +1124,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(primval) => { let ptr = self.alloc_ptr(ty)?; self.memory.write_primval(ptr, primval)?; - self.value_to_primval(Value::ByRef(ptr), ty) + let primval = self.value_to_primval(Value::ByRef(ptr), ty)?; + self.memory.deallocate(ptr)?; + Ok(primval) } Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index b89231e451581..d249829425280 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -2,7 +2,7 @@ #![miri(memory_size=1000)] fn bar(i: i32) { - if i < 1000 { //~ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory + if i < 1000 { //~ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 1000 byte memory bar(i + 1) //~^NOTE inside call to bar //~|NOTE inside call to bar @@ -31,6 +31,15 @@ fn bar(i: i32) { //~|NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar } } From 3f67c4612c6b9a7ac6579644bea6f5ccfb0803e4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 15:31:02 -0600 Subject: [PATCH 0569/1096] Refactor writing ByValPair to pointers. --- src/interpreter/mod.rs | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d9d3cba31c0d2..0380c0b01822c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1073,20 +1073,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) => Ok(ptr), Value::ByVal(primval) => { - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; + let ptr = self.alloc_ptr(ty)?; self.memory.write_primval(ptr, primval)?; Ok(ptr) } Value::ByValPair(a, b) => { - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - let ptr_size = self.memory.pointer_size() as isize; - self.memory.write_primval(ptr, a)?; - self.memory.write_primval(ptr.offset(ptr_size), b)?; + let ptr = self.alloc_ptr(ty)?; + self.write_pair_to_ptr(a, b, ptr, ty)?; Ok(ptr) } } @@ -1190,16 +1184,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), - Value::ByValPair(a, b) => { - assert_eq!(self.get_field_count(dest_ty)?, 2); - let field_0 = self.get_field_offset(dest_ty, 0)?.bytes() as isize; - let field_1 = self.get_field_offset(dest_ty, 1)?.bytes() as isize; - self.memory.write_primval(dest.offset(field_0), a)?; - self.memory.write_primval(dest.offset(field_1), b) - } + Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest, dest_ty), } } + fn write_pair_to_ptr( + &mut self, + a: PrimVal, + b: PrimVal, + ptr: Pointer, + ty: Ty<'tcx> + ) -> EvalResult<'tcx, ()> { + assert_eq!(self.get_field_count(ty)?, 2); + let field_0 = self.get_field_offset(ty, 0)?.bytes() as isize; + let field_1 = self.get_field_offset(ty, 1)?.bytes() as isize; + self.memory.write_primval(ptr.offset(field_0), a)?; + self.memory.write_primval(ptr.offset(field_1), b)?; + Ok(()) + } + fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::FloatTy; From 7728de3e608ee0b020bd024b9ea0ae386bad3f40 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 17:18:06 -0600 Subject: [PATCH 0570/1096] Do not force_allocate checked binop destination. --- src/interpreter/mod.rs | 41 ++++++++++++------------ src/interpreter/terminator/intrinsics.rs | 34 ++++---------------- 2 files changed, 27 insertions(+), 48 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0380c0b01822c..4013d17ede37a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -394,6 +394,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn binop_with_overflow( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + ) -> EvalResult<'tcx, (PrimVal, bool)> { + let left_primval = self.eval_operand_to_primval(left)?; + let right_primval = self.eval_operand_to_primval(right)?; + primval::binary_op(op, left_primval, right_primval) + } + /// Applies the binary operation `op` to the two operands and writes a tuple of the result /// and a boolean signifying the potential overflow to the destination. fn intrinsic_with_overflow( @@ -402,25 +413,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, dest: Lvalue, - dest_layout: &'tcx Layout, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { - use rustc::ty::layout::Layout::*; - let tup_layout = match *dest_layout { - Univariant { ref variant, .. } => variant, - _ => bug!("checked bin op returns something other than a tuple"), - }; - - let overflowed = self.intrinsic_overflowing(op, left, right, dest)?; - - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - - let offset = tup_layout.offsets[1].bytes() as isize; - self.memory.write_bool(dest.offset(offset), overflowed) + let (val, overflowed) = self.binop_with_overflow(op, left, right)?; + let val = Value::ByValPair(val, PrimVal::Bool(overflowed)); + self.write_value(val, dest, dest_ty) } - /// Applies the binary operation `op` to the arguments and writes the result to the destination. - /// Returns `true` if the operation overflowed. + /// Applies the binary operation `op` to the arguments and writes the result to the + /// destination. Returns `true` if the operation overflowed. fn intrinsic_overflowing( &mut self, op: mir::BinOp, @@ -428,11 +429,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right: &mir::Operand<'tcx>, dest: Lvalue, ) -> EvalResult<'tcx, bool> { - let left_primval = self.eval_operand_to_primval(left)?; - let right_primval = self.eval_operand_to_primval(right)?; - let (val, overflow) = primval::binary_op(op, left_primval, right_primval)?; + let (val, overflowed) = self.binop_with_overflow(op, left, right)?; self.write_primval(dest, val)?; - Ok(overflow) + Ok(overflowed) } fn assign_fields>( @@ -479,7 +478,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } CheckedBinaryOp(bin_op, ref left, ref right) => { - self.intrinsic_with_overflow(bin_op, left, right, dest, dest_layout)?; + self.intrinsic_with_overflow(bin_op, left, right, dest, dest_ty)?; } UnaryOp(un_op, ref operand) => { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 49cef471dab15..85559a7efc717 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -31,35 +31,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..]; match intrinsic_name { - "add_with_overflow" => { - self.intrinsic_with_overflow( - mir::BinOp::Add, - &args[0], - &args[1], - dest, - dest_layout, - )? - } + "add_with_overflow" => + self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?, - "sub_with_overflow" => { - self.intrinsic_with_overflow( - mir::BinOp::Sub, - &args[0], - &args[1], - dest, - dest_layout, - )? - } + "sub_with_overflow" => + self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_ty)?, + + "mul_with_overflow" => + self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_ty)?, - "mul_with_overflow" => { - self.intrinsic_with_overflow( - mir::BinOp::Mul, - &args[0], - &args[1], - dest, - dest_layout, - )? - } "arith_offset" => { let ptr = args_ptrs[0].read_ptr(&self.memory)?; From 701eb3f62bca0b34fd6ff1bb245a787c33af2084 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 17:18:56 -0600 Subject: [PATCH 0571/1096] Make locals debug printing smarter. --- src/interpreter/mod.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4013d17ede37a..c600b315eabee 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1389,6 +1389,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ok(()) } + + fn dump_locals(&self, limit: usize) { + for (frame_index, frame) in self.stack.iter().enumerate() { + trace!("frame[{}]:", frame_index); + + let locals: Vec<(mir::Local, Value)> = frame.mir.local_decls + .indices() + .filter_map(|i| { + if i == mir::RETURN_POINTER { return None; } + frame.get_local(i).map(|local| (i, local)) + }) + .collect(); + + for &(i, v) in locals.iter().take(limit) { + trace!(" {:?}: {:?}", i, v); + } + if locals.len() > limit { + trace!(" ..."); + } + } + } } impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { @@ -1471,16 +1492,7 @@ pub fn eval_main<'a, 'tcx: 'a>( for _ in 0..step_limit { match ecx.step() { Ok(true) => { - let limit = 5; - for (frame_index, frame) in ecx.stack.iter().enumerate() { - trace!("frame[{}]:", frame_index); - for (i, v) in frame.locals.iter().enumerate().take(limit) { - trace!(" _{}: {:?}", i + 1, v); - } - if frame.locals.len() > limit { - trace!(" ..."); - } - } + ecx.dump_locals(5); } Ok(false) => return, Err(e) => { From 65031485896b49b4e8fec9a87495cdf26b3c31bd Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 19:58:22 -0600 Subject: [PATCH 0572/1096] Optimize reads of field projections of ByValPairs. This helps in the case of field projections of the results of checked binary operations. E.g.: _1 = CheckedAdd(const 1i32, const 2i32); assert(!(_1.1: bool), "attempt to add with overflow" -> bb1 Previously, the `_1.1` field projection lvalue would force_allocate `_1` so it could read the memory in the old-style way. Now checked math with its assertions will not allocate at all. The oom2.rs compile-fail test had to be re-written, because the old version of it no longer allocates _at all_ (yay!), so it would hit the stack depth limit instead, from recursion. --- src/interpreter/mod.rs | 11 +++++++++ tests/compile-fail/oom2.rs | 49 ++++---------------------------------- 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c600b315eabee..8667585e5c8d4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -843,6 +843,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + if let mir::Lvalue::Projection(ref proj) = *lvalue { + if let mir::Lvalue::Local(index) = proj.base { + if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) { + if let mir::ProjectionElem::Field(ref field, _) = proj.elem { + let val = [a, b][field.index()]; + return Ok(Value::ByVal(val)); + } + } + } + } + match self.eval_lvalue(lvalue)? { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index d249829425280..a87e34474cff3 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -1,49 +1,8 @@ -#![feature(custom_attribute, attr_literals)] +#![feature(box_syntax, custom_attribute, attr_literals)] #![miri(memory_size=1000)] -fn bar(i: i32) { - if i < 1000 { //~ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 1000 byte memory - bar(i + 1) - //~^NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar - //~|NOTE inside call to bar +fn main() { + loop { + ::std::mem::forget(box 42); //~ ERROR tried to allocate 4 more bytes } } - -fn main() { //~NOTE inside call to main - bar(1); - //~^NOTE inside call to bar -} From f5c0a24bb05381bb94051d4da8e9ebf66d6a4025 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 16 Oct 2016 21:08:45 -0600 Subject: [PATCH 0573/1096] Make locals debug printing configurable. --- src/interpreter/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8667585e5c8d4..df478c2bd8667 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1503,7 +1503,11 @@ pub fn eval_main<'a, 'tcx: 'a>( for _ in 0..step_limit { match ecx.step() { Ok(true) => { - ecx.dump_locals(5); + use std::env::var; + let limit_opt = var("MIRI_LOG_LOCALS_LIMIT").ok().and_then(|s| s.parse().ok()); + if let Some(limit) = limit_opt { + ecx.dump_locals(limit); + } } Ok(false) => return, Err(e) => { From 39bb1254d1eaf74f45a4e741097e33fc942168d5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 21:02:37 -0600 Subject: [PATCH 0574/1096] Fix write_value of ByVal into a ByRef. Previously, you could perform the following, if you assume we could make `Cell` into a primitive. (Alternately, you could achieve this with unsafe code): x = Cell::new(12); y = &x; // Miri locals array: // x = ByRef(alloc123); // y = ByVal(Ptr(alloc123)); // // Miri allocations: // alloc123: [12, 0, 0, 0] x.set(42); // Miri locals array: // x = ByVal(I32(42)); // y = ByVal(Ptr(alloc123)); // // Miri allocations: // alloc123: [12, 0, 0, 0] Notice how `y` still refers to the allocation that used to represent `x`. But now `x` was changed to `42` and `y` is still looking at memory containing `12`. Now, instead, we keep `x` as a `ByRef` and write the `42` constant into it. Unit test to follow in the next commit. --- src/interpreter/mod.rs | 47 ++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index df478c2bd8667..fccc90aa89ad3 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1156,29 +1156,50 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn write_value( &mut self, - value: Value, + src_val: Value, dest: Lvalue, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match dest { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(value, ptr, dest_ty)?; + self.write_value_to_ptr(src_val, ptr, dest_ty)?; } + + // The cases here can be a bit subtle. Read carefully! Lvalue::Local { frame, local } => { - if let Value::ByRef(src_ptr) = value { - let dest_val = self.stack[frame].get_local(local); - let dest_ptr = if let Some(Value::ByRef(ptr)) = dest_val { - ptr - } else { - let ptr = self.alloc_ptr(dest_ty)?; - self.stack[frame].set_local(local, Value::ByRef(ptr)); - ptr - }; + let dest_val = self.stack[frame].get_local(local); + + if let Some(Value::ByRef(dest_ptr)) = dest_val { + // If the local value is already `ByRef` (that is, backed by an `Allocation`), + // then we must write the new value into this allocation, because there may be + // other pointers into the allocation. These other pointers are logically + // pointers into the local variable, and must be able to observe the change. + // + // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we + // knew for certain that there were no outstanding pointers to this local. + self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; + + } else if let Value::ByRef(src_ptr) = src_val { + // If the local value is not `ByRef`, then we know there are no pointers to it + // and we can simply overwrite the `Value` in the locals array directly. + // + // In this specific case, where the source value is `ByRef`, we must duplicate + // the allocation, because this is a by-value operation. It would be incorrect + // if they referred to the same allocation, since then a change to one would + // implicitly change the other. + // + // TODO(solson): It would be valid to attempt reading a primitive value out of + // the source and writing that into the destination without making an + // allocation. This would be a pure optimization. + let dest_ptr = self.alloc_ptr(dest_ty)?; self.copy(src_ptr, dest_ptr, dest_ty)?; + self.stack[frame].set_local(local, Value::ByRef(dest_ptr)); + } else { - // FIXME(solson): Is it safe to free the existing local here? - self.stack[frame].set_local(local, value); + // Finally, we have the simple case where neither source nor destination are + // `ByRef`. We may simply copy the source value over the the destintion local. + self.stack[frame].set_local(local, src_val); } } } From c938553a1049bac96d43135f76189b79a051fbc1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 21:45:11 -0600 Subject: [PATCH 0575/1096] Add test for 39bb1254d. --- tests/run-pass/observed_local_mut.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/run-pass/observed_local_mut.rs diff --git a/tests/run-pass/observed_local_mut.rs b/tests/run-pass/observed_local_mut.rs new file mode 100644 index 0000000000000..a4ecf1e635d24 --- /dev/null +++ b/tests/run-pass/observed_local_mut.rs @@ -0,0 +1,21 @@ +// This test is intended to guard against the problem described in commit +// 39bb1254d1eaf74f45a4e741097e33fc942168d5. +// +// As written, it might be considered UB in compiled Rust, but of course Miri gives it a safe, +// deterministic behaviour (one that might not correspond with how an eventual Rust spec would +// defined this). +// +// An alternative way to write the test without `unsafe` would be to use `Cell`, but it would +// only surface the bug described by the above commit if `Cell` on the stack got represented +// as a primitive `PrimVal::I32` which is not yet the case. + +fn main() { + let mut x = 0; + let y: *const i32 = &x; + x = 1; + + // When the described bug is in place, this results in `0`, not observing the `x = 1` line. + assert_eq!(unsafe { *y }, 1); + + assert_eq!(x, 1); +} From 9e363952c0a7ce705fa5627a789fe6e199e6fb5f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 21:45:48 -0600 Subject: [PATCH 0576/1096] Dump local values when they are read. --- src/interpreter/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index fccc90aa89ad3..cc9835fb6e073 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -891,6 +891,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => return self.eval_lvalue_projection(proj), }; + + self.dump_local(lvalue); + Ok(lvalue) } @@ -1422,6 +1425,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn dump_local(&self, lvalue: Lvalue) { + if let Lvalue::Local { frame, local } = lvalue { + if let Some(val) = self.stack[frame].get_local(local) { + match val { + Value::ByRef(ptr) => { + trace!("frame[{}] {:?}:", frame, local); + self.memory.dump(ptr.alloc_id); + } + Value::ByVal(a) => { + trace!("frame[{}] {:?}: {:?}", frame, local, a); + } + Value::ByValPair(a, b) => { + trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, a, b); + } + } + } + } + } + fn dump_locals(&self, limit: usize) { for (frame_index, frame) in self.stack.iter().enumerate() { trace!("frame[{}]:", frame_index); From 4da533729b74769812bd4ced542da88233a058c8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 21:51:36 -0600 Subject: [PATCH 0577/1096] Dump local values on Lvalue creation. --- src/interpreter/mod.rs | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index cc9835fb6e073..51aac69d2c5d7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -892,7 +892,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => return self.eval_lvalue_projection(proj), }; - self.dump_local(lvalue); + if log_enabled!(::log::LogLevel::Debug) { + self.dump_local(lvalue); + } Ok(lvalue) } @@ -1443,27 +1445,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } - - fn dump_locals(&self, limit: usize) { - for (frame_index, frame) in self.stack.iter().enumerate() { - trace!("frame[{}]:", frame_index); - - let locals: Vec<(mir::Local, Value)> = frame.mir.local_decls - .indices() - .filter_map(|i| { - if i == mir::RETURN_POINTER { return None; } - frame.get_local(i).map(|local| (i, local)) - }) - .collect(); - - for &(i, v) in locals.iter().take(limit) { - trace!(" {:?}: {:?}", i, v); - } - if locals.len() > limit { - trace!(" ..."); - } - } - } } impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { @@ -1545,13 +1526,7 @@ pub fn eval_main<'a, 'tcx: 'a>( for _ in 0..step_limit { match ecx.step() { - Ok(true) => { - use std::env::var; - let limit_opt = var("MIRI_LOG_LOCALS_LIMIT").ok().and_then(|s| s.parse().ok()); - if let Some(limit) = limit_opt { - ecx.dump_locals(limit); - } - } + Ok(true) => {} Ok(false) => return, Err(e) => { report(tcx, &ecx, e); From 1e93f64e15ee6490b2a933de4d64f4f819647f78 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 22:31:21 -0600 Subject: [PATCH 0578/1096] Clean up read_value. --- src/interpreter/mod.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 51aac69d2c5d7..230afdb14fdc8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1242,9 +1242,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::FloatTy; - let val = match &ty.sty { - &ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), - &ty::TyChar => { + let val = match ty.sty { + ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), + ty::TyChar => { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::Char(ch), @@ -1252,7 +1252,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - &ty::TyInt(int_ty) => { + ty::TyInt(int_ty) => { use syntax::ast::IntTy::*; let size = match int_ty { I8 => 1, @@ -1265,7 +1265,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::int_with_size(n, size) } - &ty::TyUint(uint_ty) => { + ty::TyUint(uint_ty) => { use syntax::ast::UintTy::*; let size = match uint_ty { U8 => 1, @@ -1278,16 +1278,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::uint_with_size(n, size) } - &ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), - &ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), + ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), + ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), - &ty::TyFnDef(def_id, substs, fn_ty) => { + ty::TyFnDef(def_id, substs, fn_ty) => { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, - &ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, - &ty::TyBox(ty) | - &ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, + ty::TyBox(ty) | + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { PrimVal::Ptr(p) @@ -1304,7 +1304,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - &ty::TyAdt(..) => { + ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty) { let size = discr.size().bytes() as usize; @@ -1322,6 +1322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("primitive read of non-primitive type: {:?}", ty), }; + Ok(Value::ByVal(val)) } From e807f0c405f9655ee4555ab0dc1579159a65cba8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 18 Oct 2016 23:24:30 -0600 Subject: [PATCH 0579/1096] Fix local dump check. --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 230afdb14fdc8..d5d7cdb65768a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -892,7 +892,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => return self.eval_lvalue_projection(proj), }; - if log_enabled!(::log::LogLevel::Debug) { + if log_enabled!(::log::LogLevel::Trace) { self.dump_local(lvalue); } From d6b4e1aba61cda101493bcc6842a6396623541df Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 19 Oct 2016 20:27:35 -0600 Subject: [PATCH 0580/1096] Expand on "uninit" FIXME. --- src/interpreter/terminator/intrinsics.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 85559a7efc717..f352a4110ca60 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -244,7 +244,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "uninit" => { - // FIXME(solson) + // FIXME(solson): Attempt writing a None over the destination when it's an + // Lvalue::Local (that is not ByRef). Otherwise do the mark_definedness as usual. let dest = self.force_allocation(dest)?.to_ptr(); let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; From 330be7766f6d89e127a63ae81ee1a2f032c53910 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 20 Oct 2016 04:42:19 -0600 Subject: [PATCH 0581/1096] Represent PrimVals as "bitbags". Now instead of holding a native type based on the tag, all PrimVals store a u64 (the `bits`), along with a `kind` corresponding to the variant as it would be in the old PrimVal representation. This commit makes no major optimizations and attempts to not change any behaviour. There will be commits to follow that make use of this representation to eliminate unnecessary allocation hacks like in `value_to_primval`. A number of places could be even more cleaned up after this commit, particularly in `cast.rs`. --- src/interpreter/cast.rs | 149 +++--- src/interpreter/mod.rs | 147 +++--- src/interpreter/terminator/intrinsics.rs | 96 ++-- src/interpreter/terminator/mod.rs | 12 +- src/interpreter/value.rs | 27 +- src/memory.rs | 78 ++- src/primval.rs | 594 +++++++++++++---------- 7 files changed, 601 insertions(+), 502 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index f53d22699ca07..427b8aed0f191 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -8,96 +8,107 @@ use primval::PrimVal; use memory::Pointer; use rustc::ty::Ty; -use syntax::ast::{self, IntTy, UintTy}; +use syntax::ast::{FloatTy, IntTy, UintTy}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use primval::PrimVal::*; - match val { - Bool(b) => self.cast_const_int(b as u64, ty, false), - F32(f) => self.cast_const_float(f as f64, ty), - F64(f) => self.cast_const_float(f, ty), - I8(i) => self.cast_signed_int(i as i64, ty), - I16(i) => self.cast_signed_int(i as i64, ty), - I32(i) => self.cast_signed_int(i as i64, ty), - I64(i) => self.cast_signed_int(i, ty), - U8(u) => self.cast_const_int(u as u64, ty, false), - U16(u) => self.cast_const_int(u as u64, ty, false), - U32(u) => self.cast_const_int(u as u64, ty, false), - Char(c) => self.cast_const_int(c as u64, ty, false), - U64(u) => self.cast_const_int(u, ty, false), - FnPtr(ptr) | - Ptr(ptr) => self.cast_ptr(ptr, ty), - } - } + use primval::PrimValKind::*; + match val.kind { + F32 => self.cast_float(val.to_f32() as f64, ty), + F64 => self.cast_float(val.to_f64(), ty), - fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use primval::PrimVal::*; - match ty.sty { - ty::TyRef(..) | - ty::TyRawPtr(_) => Ok(Ptr(ptr)), - ty::TyFnPtr(_) => Ok(FnPtr(ptr)), - ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)), - ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)), - ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)), - ty::TyInt(IntTy::I64) => Ok(I64(ptr.to_int()? as i64)), - ty::TyUint(UintTy::U8) => Ok(U8(ptr.to_int()? as u8)), - ty::TyUint(UintTy::U16) => Ok(U16(ptr.to_int()? as u16)), - ty::TyUint(UintTy::U32) => Ok(U32(ptr.to_int()? as u32)), - ty::TyUint(UintTy::U64) => Ok(U64(ptr.to_int()? as u64)), - _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), + I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits as i64, ty), + + Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), + + FnPtr(alloc) | Ptr(alloc) => { + let ptr = Pointer::new(alloc, val.bits as usize); + self.cast_ptr(ptr, ty) + } } } fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_const_int(val as u64, ty, val < 0) + self.cast_int(val as u64, ty, val < 0) } - fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { - use primval::PrimVal::*; + fn cast_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { + use primval::PrimValKind::*; + use rustc::ty::TypeVariants::*; match ty.sty { - ty::TyBool if v == 0 => Ok(Bool(false)), - ty::TyBool if v == 1 => Ok(Bool(true)), - ty::TyBool => Err(EvalError::InvalidBool), - ty::TyInt(ast::IntTy::I8) => Ok(I8(v as i64 as i8)), - ty::TyInt(ast::IntTy::I16) => Ok(I16(v as i64 as i16)), - ty::TyInt(ast::IntTy::I32) => Ok(I32(v as i64 as i32)), - ty::TyInt(ast::IntTy::I64) => Ok(I64(v as i64)), - ty::TyInt(ast::IntTy::Is) => { + TyBool if v == 0 => Ok(PrimVal::from_bool(false)), + TyBool if v == 1 => Ok(PrimVal::from_bool(true)), + TyBool => Err(EvalError::InvalidBool), + + TyInt(IntTy::I8) => Ok(PrimVal::new(v as i64 as i8 as u64, I8)), + TyInt(IntTy::I16) => Ok(PrimVal::new(v as i64 as i16 as u64, I16)), + TyInt(IntTy::I32) => Ok(PrimVal::new(v as i64 as i32 as u64, I32)), + TyInt(IntTy::I64) => Ok(PrimVal::new(v as i64 as i64 as u64, I64)), + + TyUint(UintTy::U8) => Ok(PrimVal::new(v as u8 as u64, U8)), + TyUint(UintTy::U16) => Ok(PrimVal::new(v as u16 as u64, U16)), + TyUint(UintTy::U32) => Ok(PrimVal::new(v as u32 as u64, U32)), + TyUint(UintTy::U64) => Ok(PrimVal::new(v, U64)), + + TyInt(IntTy::Is) => { let int_ty = self.tcx.sess.target.int_type; let ty = self.tcx.mk_mach_int(int_ty); - self.cast_const_int(v, ty, negative) - }, - ty::TyUint(ast::UintTy::U8) => Ok(U8(v as u8)), - ty::TyUint(ast::UintTy::U16) => Ok(U16(v as u16)), - ty::TyUint(ast::UintTy::U32) => Ok(U32(v as u32)), - ty::TyUint(ast::UintTy::U64) => Ok(U64(v)), - ty::TyUint(ast::UintTy::Us) => { + self.cast_int(v, ty, negative) + } + + TyUint(UintTy::Us) => { let uint_ty = self.tcx.sess.target.uint_type; let ty = self.tcx.mk_mach_uint(uint_ty); - self.cast_const_int(v, ty, negative) - }, - ty::TyFloat(ast::FloatTy::F64) if negative => Ok(F64(v as i64 as f64)), - ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)), - ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)), - ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)), - ty::TyRawPtr(_) => Ok(Ptr(Pointer::from_int(v as usize))), - ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)), - ty::TyChar => Err(EvalError::InvalidChar(v)), + self.cast_int(v, ty, negative) + } + + TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i64 as f64)), + TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)), + TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i64 as f32)), + TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), + + TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v, Char)), + TyChar => Err(EvalError::InvalidChar(v)), + + TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v as usize))), + _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } } - fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use primval::PrimVal::*; + fn cast_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use rustc::ty::TypeVariants::*; match ty.sty { - // casting negative floats to unsigned integers yields zero - ty::TyUint(_) if val < 0.0 => self.cast_const_int(0, ty, false), - ty::TyInt(_) if val < 0.0 => self.cast_const_int(val as i64 as u64, ty, true), - ty::TyInt(_) | ty::TyUint(_) => self.cast_const_int(val as u64, ty, false), - ty::TyFloat(ast::FloatTy::F64) => Ok(F64(val)), - ty::TyFloat(ast::FloatTy::F32) => Ok(F32(val as f32)), + // Casting negative floats to unsigned integers yields zero. + TyUint(_) if val < 0.0 => self.cast_int(0, ty, false), + TyInt(_) if val < 0.0 => self.cast_int(val as i64 as u64, ty, true), + + TyInt(_) | ty::TyUint(_) => self.cast_int(val as u64, ty, false), + + TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)), + TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)), _ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))), } } + + fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimValKind::*; + use rustc::ty::TypeVariants::*; + match ty.sty { + TyRef(..) | TyRawPtr(_) => Ok(PrimVal::from_ptr(ptr)), + TyFnPtr(_) => Ok(PrimVal::from_fn_ptr(ptr)), + + TyInt(IntTy::I8) => Ok(PrimVal::new(ptr.to_int()? as u64, I8)), + TyInt(IntTy::I16) => Ok(PrimVal::new(ptr.to_int()? as u64, I16)), + TyInt(IntTy::I32) => Ok(PrimVal::new(ptr.to_int()? as u64, I32)), + TyInt(IntTy::I64) => Ok(PrimVal::new(ptr.to_int()? as u64, I64)), + + TyUint(UintTy::U8) => Ok(PrimVal::new(ptr.to_int()? as u64, U8)), + TyUint(UintTy::U16) => Ok(PrimVal::new(ptr.to_int()? as u64, U16)), + TyUint(UintTy::U32) => Ok(PrimVal::new(ptr.to_int()? as u64, U32)), + TyUint(UintTy::U64) => Ok(PrimVal::new(ptr.to_int()? as u64, U64)), + + _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), + } + } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d5d7cdb65768a..463b9bf006392 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -16,7 +16,7 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer, AllocId}; -use primval::{self, PrimVal}; +use primval::{self, PrimVal, PrimValKind}; use self::value::Value; use std::collections::HashMap; @@ -202,24 +202,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - fn isize_primval(&self, n: i64) -> PrimVal { - match self.memory.pointer_size() { - 1 => PrimVal::I8(n as i8), - 2 => PrimVal::I16(n as i16), - 4 => PrimVal::I32(n as i32), - 8 => PrimVal::I64(n as i64), - p => bug!("unsupported target pointer size: {}", p), - } + fn usize_primval(&self, n: u64) -> PrimVal { + PrimVal::from_uint_with_size(n, self.memory.pointer_size()) } - fn usize_primval(&self, n: u64) -> PrimVal { - match self.memory.pointer_size() { - 1 => PrimVal::U8(n as u8), - 2 => PrimVal::U16(n as u16), - 4 => PrimVal::U32(n as u32), - 8 => PrimVal::U64(n as u64), - p => bug!("unsupported target pointer size: {}", p), - } + fn isize_primval(&self, n: i64) -> PrimVal { + PrimVal::from_int_with_size(n, self.memory.pointer_size()) } fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { @@ -227,32 +215,43 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.usize_primval(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::from_ptr(ptr), self.usize_primval(s.len() as u64))) } fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; - use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; + use rustc_const_math::ConstFloat; let primval = match *const_val { - Integral(ConstInt::I8(i)) => PrimVal::I8(i), - Integral(ConstInt::U8(i)) => PrimVal::U8(i), - Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => PrimVal::I16(i), - Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => PrimVal::U16(i), - Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => PrimVal::I32(i), - Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => PrimVal::U32(i), - Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => PrimVal::I64(i), - Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => PrimVal::U64(i), - Float(ConstFloat::F32(f)) => PrimVal::F32(f), - Float(ConstFloat::F64(f)) => PrimVal::F64(f), - Bool(b) => PrimVal::Bool(b), - Char(c) => PrimVal::Char(c), + Integral(const_int) => { + use rustc_const_math::ConstInt::*; + use rustc_const_math::ConstIsize::*; + use rustc_const_math::ConstUsize::*; + + let kind = match const_int { + I8(_) => PrimValKind::I8, + I16(_) | Isize(Is16(_)) => PrimValKind::I16, + I32(_) | Isize(Is32(_)) => PrimValKind::I32, + I64(_) | Isize(Is64(_)) => PrimValKind::I64, + U8(_) => PrimValKind::U8, + U16(_) | Usize(Us16(_)) => PrimValKind::U16, + U32(_) | Usize(Us32(_)) => PrimValKind::U32, + U64(_) | Usize(Us64(_)) => PrimValKind::U64, + + Infer(_) | InferSigned(_) => + bug!("uninferred constants only exist before typeck"), + }; + + PrimVal::new(const_int.to_u64_unchecked(), kind) + } + + Float(ConstFloat::F32(f)) => PrimVal::from_f32(f), + Float(ConstFloat::F64(f)) => PrimVal::from_f64(f), + Float(ConstFloat::FInfer { .. }) => + bug!("uninferred constants only exist before typeck"), + + Bool(b) => PrimVal::from_bool(b), + Char(c) => PrimVal::from_char(c), Str(ref s) => return self.str_to_value(s), @@ -260,7 +259,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(bs.len(), 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - PrimVal::Ptr(ptr) + PrimVal::from_ptr(ptr) } Struct(_) => unimplemented!(), @@ -269,11 +268,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array(_, _) => unimplemented!(), Repeat(_, _) => unimplemented!(), Dummy => unimplemented!(), - - Float(ConstFloat::FInfer{..}) | - Integral(ConstInt::Infer(_)) | - Integral(ConstInt::InferSigned(_)) => - bug!("uninferred constants only exist before typeck"), }; Ok(Value::ByVal(primval)) @@ -416,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; - let val = Value::ByValPair(val, PrimVal::Bool(overflowed)); + let val = Value::ByValPair(val, PrimVal::from_bool(overflowed)); self.write_value(val, dest, dest_ty) } @@ -572,9 +566,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = discr.size().bytes() as usize; let val = if signed { - PrimVal::int_with_size(n as i64, size) + PrimVal::from_int_with_size(n as i64, size) } else { - PrimVal::uint_with_size(n, size) + PrimVal::from_uint_with_size(n, size) }; self.write_primval(dest, val)?; @@ -615,12 +609,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); - let ptr = PrimVal::Ptr(raw_ptr); + let ptr = PrimVal::from_ptr(raw_ptr); let val = match extra { LvalueExtra::None => Value::ByVal(ptr), LvalueExtra::Length(len) => Value::ByValPair(ptr, self.usize_primval(len)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), + LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::from_ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -630,7 +624,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let ptr = self.alloc_ptr(ty)?; - self.write_primval(dest, PrimVal::Ptr(ptr))?; + self.write_primval(dest, PrimVal::from_ptr(ptr))?; } Cast(kind, ref operand, cast_ty) => { @@ -699,6 +693,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { InlineAsm { .. } => return Err(EvalError::InlineAsm), } + if log_enabled!(::log::LogLevel::Trace) { + self.dump_local(dest); + } + Ok(()) } @@ -974,7 +972,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { - use primval::PrimVal::*; + use primval::PrimValKind::*; use interpreter::value::Value::*; let val = match self.eval_and_read_lvalue(&proj.base)? { @@ -983,10 +981,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match val { - ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), - ByValPair(Ptr(ptr), n) => - (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), - ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None), + ByValPair( + PrimVal { kind: Ptr(ptr_alloc), bits: ptr_offset }, + PrimVal { kind: Ptr(vtable_alloc), bits: vtable_offset }, + ) => { + let ptr = Pointer::new(ptr_alloc, ptr_offset as usize); + let vtable = Pointer::new(vtable_alloc, vtable_offset as usize); + (ptr, LvalueExtra::Vtable(vtable)) + } + + ByValPair(PrimVal { kind: Ptr(alloc), bits: offset }, n) => { + let ptr = Pointer::new(alloc, offset as usize); + (ptr, LvalueExtra::Length(n.expect_uint("slice length"))) + } + + ByVal(PrimVal { kind: Ptr(alloc), bits: offset }) => { + let ptr = Pointer::new(alloc, offset as usize); + (ptr, LvalueExtra::None) + } + _ => bug!("can't deref non pointer types"), } } @@ -1243,11 +1256,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::ast::FloatTy; let val = match ty.sty { - ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), + ty::TyBool => PrimVal::from_bool(self.memory.read_bool(ptr)?), ty::TyChar => { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { - Some(ch) => PrimVal::Char(ch), + Some(ch) => PrimVal::from_char(ch), None => return Err(EvalError::InvalidChar(c as u64)), } } @@ -1262,7 +1275,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Is => self.memory.pointer_size(), }; let n = self.memory.read_int(ptr, size)?; - PrimVal::int_with_size(n, size) + PrimVal::from_int_with_size(n, size) } ty::TyUint(uint_ty) => { @@ -1275,32 +1288,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Us => self.memory.pointer_size(), }; let n = self.memory.read_uint(ptr, size)?; - PrimVal::uint_with_size(n, size) + PrimVal::from_uint_with_size(n, size) } - ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?), - ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?), + ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), + ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), ty::TyFnDef(def_id, substs, fn_ty) => { - PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) + PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { - PrimVal::Ptr(p) + PrimVal::from_ptr(p) } else { // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` let extra = ptr.offset(self.memory.pointer_size() as isize); let extra = match self.tcx.struct_tail(ty).sty { - ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), + ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | ty::TyStr => self.usize_primval(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; - return Ok(Value::ByValPair(PrimVal::Ptr(p), extra)); + return Ok(Value::ByValPair(PrimVal::from_ptr(p), extra)); } } @@ -1310,10 +1323,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = discr.size().bytes() as usize; if signed { let n = self.memory.read_int(ptr, size)?; - PrimVal::int_with_size(n, size) + PrimVal::from_int_with_size(n, size) } else { let n = self.memory.read_uint(ptr, size)?; - PrimVal::uint_with_size(n, size) + PrimVal::from_uint_with_size(n, size) } } else { bug!("primitive read of non-clike enum: {:?}", ty); diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index f352a4110ca60..c3327dd76f9d0 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -7,7 +7,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use interpreter::value::Value; use interpreter::{EvalContext, Lvalue}; -use primval::{self, PrimVal}; +use primval::{self, PrimVal, PrimValKind}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(args_ptrs[1], isize)? .expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); - self.write_primval(dest, PrimVal::Ptr(new_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; } "assume" => { @@ -99,19 +99,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.write_primval(dest, PrimVal::U64(discr_val))?; + self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?; } "fabsf32" => { let f = self.value_to_primval(args_ptrs[2], f32)? .expect_f32("fabsf32 read non f32"); - self.write_primval(dest, PrimVal::F32(f.abs()))?; + self.write_primval(dest, PrimVal::from_f32(f.abs()))?; } "fabsf64" => { let f = self.value_to_primval(args_ptrs[2], f64)? .expect_f64("fabsf64 read non f64"); - self.write_primval(dest, PrimVal::F64(f.abs()))?; + self.write_primval(dest, PrimVal::from_f64(f.abs()))?; } "fadd_fast" => { @@ -159,7 +159,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); - self.write_primval(dest, PrimVal::Bool(needs_drop))?; + self.write_primval(dest, PrimVal::from_bool(needs_drop))?; } "offset" => { @@ -170,7 +170,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = args_ptrs[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); - self.write_primval(dest, PrimVal::Ptr(result_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(result_ptr))?; } "overflowing_sub" => { @@ -190,7 +190,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect_f32("powif32 first arg not f32"); let i = self.value_to_primval(args_ptrs[1], i32)? .expect_int("powif32 second arg not i32"); - self.write_primval(dest, PrimVal::F32(f.powi(i as i32)))?; + self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?; } "powif64" => { @@ -198,19 +198,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect_f64("powif64 first arg not f64"); let i = self.value_to_primval(args_ptrs[1], i32)? .expect_int("powif64 second arg not i32"); - self.write_primval(dest, PrimVal::F64(f.powi(i as i32)))?; + self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?; } "sqrtf32" => { let f = self.value_to_primval(args_ptrs[0], f32)? .expect_f32("sqrtf32 first arg not f32"); - self.write_primval(dest, PrimVal::F32(f.sqrt()))?; + self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?; } "sqrtf64" => { let f = self.value_to_primval(args_ptrs[0], f64)? .expect_f64("sqrtf64 first arg not f64"); - self.write_primval(dest, PrimVal::F64(f.sqrt()))?; + self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?; } "size_of" => { @@ -235,7 +235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); - self.write_primval(dest, PrimVal::U64(n))?; + self.write_primval(dest, PrimVal::new(n, PrimValKind::U64))?; } "transmute" => { @@ -362,53 +362,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } +macro_rules! integer_intrinsic { + ($name:expr, $val:expr, $method:ident) => ({ + let val = $val; + + use primval::PrimValKind::*; + let bits = match val.kind { + I8 => (val.bits as i8).$method() as u64, + U8 => (val.bits as u8).$method() as u64, + I16 => (val.bits as i16).$method() as u64, + U16 => (val.bits as u16).$method() as u64, + I32 => (val.bits as i32).$method() as u64, + U32 => (val.bits as u32).$method() as u64, + I64 => (val.bits as i64).$method() as u64, + U64 => (val.bits as u64).$method() as u64, + _ => bug!("invalid `{}` argument: {:?}", $name, val), + }; + + PrimVal::new(bits, val.kind) + }); +} + fn numeric_intrinsic(name: &str, val: PrimVal) -> PrimVal { - use primval::PrimVal::*; match name { - "ctpop" => match val { - I8(i) => I8(i.count_ones() as i8), - U8(i) => U8(i.count_ones() as u8), - I16(i) => I16(i.count_ones() as i16), - U16(i) => U16(i.count_ones() as u16), - I32(i) => I32(i.count_ones() as i32), - U32(i) => U32(i.count_ones() as u32), - I64(i) => I64(i.count_ones() as i64), - U64(i) => U64(i.count_ones() as u64), - other => bug!("invalid `ctpop` argument: {:?}", other), - }, - "cttz" => match val { - I8(i) => I8(i.trailing_zeros() as i8), - U8(i) => U8(i.trailing_zeros() as u8), - I16(i) => I16(i.trailing_zeros() as i16), - U16(i) => U16(i.trailing_zeros() as u16), - I32(i) => I32(i.trailing_zeros() as i32), - U32(i) => U32(i.trailing_zeros() as u32), - I64(i) => I64(i.trailing_zeros() as i64), - U64(i) => U64(i.trailing_zeros() as u64), - other => bug!("invalid `cttz` argument: {:?}", other), - }, - "ctlz" => match val { - I8(i) => I8(i.leading_zeros() as i8), - U8(i) => U8(i.leading_zeros() as u8), - I16(i) => I16(i.leading_zeros() as i16), - U16(i) => U16(i.leading_zeros() as u16), - I32(i) => I32(i.leading_zeros() as i32), - U32(i) => U32(i.leading_zeros() as u32), - I64(i) => I64(i.leading_zeros() as i64), - U64(i) => U64(i.leading_zeros() as u64), - other => bug!("invalid `ctlz` argument: {:?}", other), - }, - "bswap" => match val { - I8(i) => I8(i.swap_bytes() as i8), - U8(i) => U8(i.swap_bytes() as u8), - I16(i) => I16(i.swap_bytes() as i16), - U16(i) => U16(i.swap_bytes() as u16), - I32(i) => I32(i.swap_bytes() as i32), - U32(i) => U32(i.swap_bytes() as u32), - I64(i) => I64(i.swap_bytes() as i64), - U64(i) => U64(i.swap_bytes() as u64), - other => bug!("invalid `bswap` argument: {:?}", other), - }, - _ => bug!("not a numeric intrinsic: {}", name), + "bswap" => integer_intrinsic!("bswap", val, swap_bytes), + "ctlz" => integer_intrinsic!("ctlz", val, leading_zeros), + "ctpop" => integer_intrinsic!("ctpop", val, count_ones), + "cttz" => integer_intrinsic!("cttz", val, trailing_zeros), + _ => bug!("not a numeric intrinsic: {}", name), } } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 1555f22efb23c..be6147f8b742c 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -87,7 +87,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)? - .expect_fn_ptr("TyFnPtr callee did not evaluate to PrimVal::FnPtr"); + .expect_fn_ptr("TyFnPtr callee did not evaluate to FnPtr"); let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); @@ -293,7 +293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let align = self.value_to_primval(args[1], usize)? .expect_uint("__rust_allocate second arg not usize"); let ptr = self.memory.allocate(size as usize, align as usize)?; - self.write_primval(dest, PrimVal::Ptr(ptr))?; + self.write_primval(dest, PrimVal::from_ptr(ptr))?; } "__rust_reallocate" => { @@ -301,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; } "memcmp" => { @@ -321,7 +321,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - self.write_primval(dest, PrimVal::int_with_size(result, dest_size))?; + self.write_primval(dest, PrimVal::from_int_with_size(result, dest_size))?; } _ => { @@ -430,7 +430,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: this is a memory leak, should probably add the pointer to the // current stack. let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?; - args[0].0 = Value::ByVal(PrimVal::Ptr(first)); + args[0].0 = Value::ByVal(PrimVal::from_ptr(first)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } @@ -453,7 +453,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; - *first_arg = Value::ByVal(PrimVal::Ptr(self_ptr)); + *first_arg = Value::ByVal(PrimVal::from_ptr(self_ptr)); let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index b89210c065d38..09b0f8345d878 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,6 +1,6 @@ use error::EvalResult; use memory::{Memory, Pointer}; -use primval::PrimVal; +use primval::{PrimVal, PrimValKind}; /// A `Value` represents a single self-contained Rust value. /// @@ -22,8 +22,13 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - ByVal(PrimVal::Ptr(ptr)) | - ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr), + + ByVal(PrimVal { kind: PrimValKind::Ptr(alloc), bits: offset }) | + ByVal(PrimVal { kind: PrimValKind::FnPtr(alloc), bits: offset }) => { + let ptr = Pointer::new(alloc, offset as usize); + Ok(ptr) + } + ByValPair(..) => unimplemented!(), ByVal(_other) => unimplemented!(), } @@ -40,7 +45,16 @@ impl<'a, 'tcx: 'a> Value { let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?; Ok((ptr, vtable)) } - ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)) => Ok((ptr, vtable)), + + ByValPair( + PrimVal { kind: PrimValKind::Ptr(ptr_alloc), bits: ptr_offset }, + PrimVal { kind: PrimValKind::Ptr(vtable_alloc), bits: vtable_offset }, + ) => { + let ptr = Pointer::new(ptr_alloc, ptr_offset as usize); + let vtable = Pointer::new(vtable_alloc, vtable_offset as usize); + Ok((ptr, vtable)) + } + _ => bug!("expected ptr and vtable, got {:?}", self), } } @@ -49,10 +63,7 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByValPair(_, PrimVal::U8(len)) => Ok(len as u64), - ByValPair(_, PrimVal::U16(len)) => Ok(len as u64), - ByValPair(_, PrimVal::U32(len)) => Ok(len as u64), - ByValPair(_, PrimVal::U64(len)) => Ok(len), + ByValPair(_, val) if val.kind.is_int() => Ok(val.bits), _ => unimplemented!(), } } diff --git a/src/memory.rs b/src/memory.rs index a212f25fedd0a..6979d7ef3397e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -49,8 +49,13 @@ pub struct Pointer { } impl Pointer { + pub fn new(alloc_id: AllocId, offset: usize) -> Self { + Pointer { alloc_id: alloc_id, offset: offset } + } + pub fn offset(self, i: isize) -> Self { - Pointer { offset: (self.offset as isize + i) as usize, ..self } + let new_offset = (self.offset as isize + i) as usize; + Pointer::new(self.alloc_id, new_offset) } pub fn points_to_zst(&self) -> bool { @@ -65,25 +70,18 @@ impl Pointer { } } + // FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger + // than host usize. pub fn from_int(i: usize) -> Self { - Pointer { - alloc_id: ZST_ALLOC_ID, - offset: i, - } + Pointer::new(ZST_ALLOC_ID, i) } pub fn zst_ptr() -> Self { - Pointer { - alloc_id: ZST_ALLOC_ID, - offset: 0, - } + Pointer::new(ZST_ALLOC_ID, 0) } pub fn never_ptr() -> Self { - Pointer { - alloc_id: NEVER_ALLOC_ID, - offset: 0, - } + Pointer::new(NEVER_ALLOC_ID, 0) } } @@ -167,20 +165,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { - return Pointer { - alloc_id: alloc_id, - offset: 0, - }; + return Pointer::new(alloc_id, 0); } let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; self.functions.insert(id, def.clone()); self.function_alloc_cache.insert(def, id); - Pointer { - alloc_id: id, - offset: 0, - } + Pointer::new(id, 0) } pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { @@ -207,10 +199,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let id = self.next_id; self.next_id.0 += 1; self.alloc_map.insert(id, alloc); - Ok(Pointer { - alloc_id: id, - offset: 0, - }) + Ok(Pointer::new(id, 0)) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error @@ -241,10 +230,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.truncate(new_size); } - Ok(Pointer { - alloc_id: ptr.alloc_id, - offset: 0, - }) + Ok(Pointer::new(ptr.alloc_id, 0)) } // TODO(solson): See comment on `reallocate`. @@ -535,7 +521,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = read_target_uint(endianess, bytes).unwrap() as usize; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }), + Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)), None => Ok(Pointer::from_int(offset)), } } @@ -547,22 +533,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - match val { - PrimVal::Bool(b) => self.write_bool(ptr, b), - PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), - PrimVal::I16(n) => self.write_int(ptr, n as i64, 2), - PrimVal::I32(n) => self.write_int(ptr, n as i64, 4), - PrimVal::I64(n) => self.write_int(ptr, n as i64, 8), - PrimVal::U8(n) => self.write_uint(ptr, n as u64, 1), - PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2), - PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4), - PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8), - PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4), - PrimVal::F32(f) => self.write_f32(ptr, f), - PrimVal::F64(f) => self.write_f64(ptr, f), - PrimVal::FnPtr(p) | - PrimVal::Ptr(p) => self.write_ptr(ptr, p), + use primval::PrimValKind::*; + match val.kind { + FnPtr(alloc_id) | Ptr(alloc_id) => { + let p = Pointer::new(alloc_id, val.bits as usize); + return self.write_ptr(ptr, p); + } + _ => {} } + + let (size, bits) = match val.kind { + I8 | U8 | Bool => (1, val.bits as u8 as u64), + I16 | U16 => (2, val.bits as u16 as u64), + I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), + I64 | U64 | F64 => (8, val.bits), + FnPtr(_) | Ptr(_) => bug!("handled above"), + }; + + self.write_uint(ptr, bits, size) } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { diff --git a/src/primval.rs b/src/primval.rs index 26c1d715c4ce4..5008aa24a2a03 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,306 +1,402 @@ #![allow(unknown_lints)] #![allow(float_cmp)] +use std::mem::transmute; + use rustc::mir::repr as mir; use error::{EvalError, EvalResult}; -use memory::Pointer; +use memory::{AllocId, Pointer}; -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PrimVal { - Bool(bool), - I8(i8), I16(i16), I32(i32), I64(i64), - U8(u8), U16(u16), U32(u32), U64(u64), +fn bits_to_f32(bits: u64) -> f32 { + unsafe { transmute::(bits as u32) } +} - Ptr(Pointer), - FnPtr(Pointer), - Char(char), +fn bits_to_f64(bits: u64) -> f64 { + unsafe { transmute::(bits) } +} - F32(f32), F64(f64), +fn f32_to_bits(f: f32) -> u64 { + unsafe { transmute::(f) as u64 } } -macro_rules! declare_expect_fn { - ($name:ident, $variant:ident, $t:ty) => ( - pub fn $name(self, error_msg: &str) -> $t { - match self { - PrimVal::$variant(x) => x, - _ => bug!("{}", error_msg), - } +fn f64_to_bits(f: f64) -> u64 { + unsafe { transmute::(f) } +} + +fn bits_to_bool(n: u64) -> bool { + // FIXME(solson): Can we reach here due to user error? + debug_assert!(n == 0 || n == 1, "bits interpreted as bool were {}", n); + n & 1 == 1 +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct PrimVal { + pub bits: u64, + pub kind: PrimValKind, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PrimValKind { + I8, I16, I32, I64, + U8, U16, U32, U64, + F32, F64, + Bool, + Char, + Ptr(AllocId), + FnPtr(AllocId), +} + +impl PrimValKind { + pub fn is_int(self) -> bool { + use self::PrimValKind::*; + match self { + I8 | I16 | I32 | I64 | U8 | U16 | U32 | U64 => true, + _ => false, } - ); + } } impl PrimVal { - declare_expect_fn!(expect_bool, Bool, bool); - declare_expect_fn!(expect_f32, F32, f32); - declare_expect_fn!(expect_f64, F64, f64); - declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer); - declare_expect_fn!(expect_ptr, Ptr, Pointer); + pub fn new(bits: u64, kind: PrimValKind) -> Self { + PrimVal { bits: bits, kind: kind } + } + + pub fn from_ptr(ptr: Pointer) -> Self { + PrimVal::new(ptr.offset as u64, PrimValKind::Ptr(ptr.alloc_id)) + } + + pub fn from_fn_ptr(ptr: Pointer) -> Self { + PrimVal::new(ptr.offset as u64, PrimValKind::FnPtr(ptr.alloc_id)) + } + + pub fn from_bool(b: bool) -> Self { + PrimVal::new(b as u64, PrimValKind::Bool) + } + + pub fn from_char(c: char) -> Self { + PrimVal::new(c as u64, PrimValKind::Char) + } + + pub fn from_f32(f: f32) -> Self { + PrimVal::new(f32_to_bits(f), PrimValKind::F32) + } + + pub fn from_f64(f: f64) -> Self { + PrimVal::new(f64_to_bits(f), PrimValKind::F64) + } + + pub fn from_uint_with_size(n: u64, size: usize) -> Self { + let kind = match size { + 1 => PrimValKind::U8, + 2 => PrimValKind::U16, + 4 => PrimValKind::U32, + 8 => PrimValKind::U64, + _ => bug!("can't make uint ({}) with size {}", n, size), + }; + PrimVal::new(n, kind) + } + + pub fn from_int_with_size(n: i64, size: usize) -> Self { + let kind = match size { + 1 => PrimValKind::I8, + 2 => PrimValKind::I16, + 4 => PrimValKind::I32, + 8 => PrimValKind::I64, + _ => bug!("can't make int ({}) with size {}", n, size), + }; + PrimVal::new(n as u64, kind) + } + + pub fn to_f32(self) -> f32 { + bits_to_f32(self.bits) + } + + pub fn to_f64(self) -> f64 { + bits_to_f64(self.bits) + } pub fn expect_uint(self, error_msg: &str) -> u64 { - use self::PrimVal::*; - match self { - U8(u) => u as u64, - U16(u) => u as u64, - U32(u) => u as u64, - U64(u) => u, - Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as u64, + use self::PrimValKind::*; + match self.kind { + U8 | U16 | U32 | U64 => self.bits, + Ptr(alloc_id) => { + let ptr = Pointer::new(alloc_id, self.bits as usize); + ptr.to_int().expect("non abstract ptr") as u64 + } _ => bug!("{}", error_msg), } } pub fn expect_int(self, error_msg: &str) -> i64 { - use self::PrimVal::*; - match self { - I8(i) => i as i64, - I16(i) => i as i64, - I32(i) => i as i64, - I64(i) => i, - Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as i64, + use self::PrimValKind::*; + match self.kind { + I8 | I16 | I32 | I64 => self.bits as i64, + Ptr(alloc_id) => { + let ptr = Pointer::new(alloc_id, self.bits as usize); + ptr.to_int().expect("non abstract ptr") as i64 + } _ => bug!("{}", error_msg), } } - pub fn uint_with_size(n: u64, size: usize) -> Self { - use self::PrimVal::*; - match size { - 1 => U8(n as u8), - 2 => U16(n as u16), - 4 => U32(n as u32), - 8 => U64(n), - _ => bug!("can't make uint ({}) with size {}", n, size), + pub fn expect_bool(self, error_msg: &str) -> bool { + match (self.kind, self.bits) { + (PrimValKind::Bool, 0) => false, + (PrimValKind::Bool, 1) => true, + _ => bug!("{}", error_msg), } } - pub fn int_with_size(n: i64, size: usize) -> Self { - use self::PrimVal::*; - match size { - 1 => I8(n as i8), - 2 => I16(n as i16), - 4 => I32(n as i32), - 8 => I64(n), - _ => bug!("can't make int ({}) with size {}", n, size), + pub fn expect_f32(self, error_msg: &str) -> f32 { + match self.kind { + PrimValKind::F32 => bits_to_f32(self.bits), + _ => bug!("{}", error_msg), } } -} -/// returns the result of the operation and whether the operation overflowed -pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::repr::BinOp::*; - use self::PrimVal::*; - - macro_rules! overflow { - ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({ - let (val, of) = $l.$op($r); - if of { - return Ok(($v(val), true)); - } else { - $v(val) - } - }) - } - - macro_rules! int_binops { - ($v:ident, $l:ident, $r:ident) => ({ - match bin_op { - Add => overflow!($v, $v, $l, overflowing_add, $r), - Sub => overflow!($v, $v, $l, overflowing_sub, $r), - Mul => overflow!($v, $v, $l, overflowing_mul, $r), - Div => overflow!($v, $v, $l, overflowing_div, $r), - Rem => overflow!($v, $v, $l, overflowing_rem, $r), - BitXor => $v($l ^ $r), - BitAnd => $v($l & $r), - BitOr => $v($l | $r), - - // these have already been handled - Shl | Shr => bug!("`{}` operation should already have been handled", bin_op.to_hir_binop().as_str()), - - Eq => Bool($l == $r), - Ne => Bool($l != $r), - Lt => Bool($l < $r), - Le => Bool($l <= $r), - Gt => Bool($l > $r), - Ge => Bool($l >= $r), - } - }) + pub fn expect_f64(self, error_msg: &str) -> f64 { + match self.kind { + PrimValKind::F32 => bits_to_f64(self.bits), + _ => bug!("{}", error_msg), + } } - macro_rules! float_binops { - ($v:ident, $l:ident, $r:ident) => ({ - match bin_op { - Add => $v($l + $r), - Sub => $v($l - $r), - Mul => $v($l * $r), - Div => $v($l / $r), - Rem => $v($l % $r), - - // invalid float ops - BitXor | BitAnd | BitOr | - Shl | Shr => bug!("`{}` is not a valid operation on floats", bin_op.to_hir_binop().as_str()), - - Eq => Bool($l == $r), - Ne => Bool($l != $r), - Lt => Bool($l < $r), - Le => Bool($l <= $r), - Gt => Bool($l > $r), - Ge => Bool($l >= $r), - } - }) + pub fn expect_ptr(self, error_msg: &str) -> Pointer { + match self.kind { + PrimValKind::Ptr(alloc_id) => Pointer::new(alloc_id, self.bits as usize), + _ => bug!("{}", error_msg), + } } - fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::repr::BinOp::*; - match bin_op { - Eq => Ok(Bool(false)), - Ne => Ok(Bool(true)), - Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), - _ => unimplemented!(), + pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer { + match self.kind { + PrimValKind::FnPtr(alloc_id) => Pointer::new(alloc_id, self.bits as usize), + _ => bug!("{}", error_msg), } } +} - match bin_op { - // can have rhs with a different numeric type - Shl | Shr => { - // these numbers are the maximum number a bitshift rhs could possibly have - // e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range - let type_bits: u32 = match left { - I8(_) | U8(_) => 8, - I16(_) | U16(_) => 16, - I32(_) | U32(_) => 32, - I64(_) | U64(_) => 64, - _ => bug!("bad MIR: bitshift lhs is not integral"), - }; - assert!(type_bits.is_power_of_two()); - // turn into `u32` because `overflowing_sh{l,r}` only take `u32` - let r = match right { - I8(i) => i as u32, - I16(i) => i as u32, - I32(i) => i as u32, - I64(i) => i as u32, - U8(i) => i as u32, - U16(i) => i as u32, - U32(i) => i as u32, - U64(i) => i as u32, - _ => bug!("bad MIR: bitshift rhs is not integral"), - }; - // apply mask - let r = r & (type_bits - 1); - macro_rules! shift { - ($v:ident, $l:ident, $r:ident) => ({ - match bin_op { - Shl => overflow!($v, U32, $l, overflowing_shl, $r), - Shr => overflow!($v, U32, $l, overflowing_shr, $r), - _ => bug!("it has already been checked that this is a shift op"), - } - }) - } - let val = match left { - I8(l) => shift!(I8, l, r), - I16(l) => shift!(I16, l, r), - I32(l) => shift!(I32, l, r), - I64(l) => shift!(I64, l, r), - U8(l) => shift!(U8, l, r), - U16(l) => shift!(U16, l, r), - U32(l) => shift!(U32, l, r), - U64(l) => shift!(U64, l, r), - _ => bug!("bad MIR: bitshift lhs is not integral (should already have been checked)"), - }; - return Ok((val, false)); - }, - _ => {}, - } +//////////////////////////////////////////////////////////////////////////////// +// MIR operator evaluation +//////////////////////////////////////////////////////////////////////////////// + +macro_rules! overflow { + ($kind:expr, $op:ident, $l:expr, $r:expr) => ({ + let (val, overflowed) = $l.$op($r); + let primval = PrimVal::new(val as u64, $kind); + Ok((primval, overflowed)) + }) +} - let val = match (left, right) { - (I8(l), I8(r)) => int_binops!(I8, l, r), - (I16(l), I16(r)) => int_binops!(I16, l, r), - (I32(l), I32(r)) => int_binops!(I32, l, r), - (I64(l), I64(r)) => int_binops!(I64, l, r), - (U8(l), U8(r)) => int_binops!(U8, l, r), - (U16(l), U16(r)) => int_binops!(U16, l, r), - (U32(l), U32(r)) => int_binops!(U32, l, r), - (U64(l), U64(r)) => int_binops!(U64, l, r), - (F32(l), F32(r)) => float_binops!(F32, l, r), - (F64(l), F64(r)) => float_binops!(F64, l, r), - (Char(l), Char(r)) => match bin_op { - Eq => Bool(l == r), - Ne => Bool(l != r), - Lt => Bool(l < r), - Le => Bool(l <= r), - Gt => Bool(l > r), - Ge => Bool(l >= r), - _ => bug!("invalid char op: {:?}", bin_op), - }, - - (Bool(l), Bool(r)) => { - Bool(match bin_op { - Eq => l == r, - Ne => l != r, - Lt => l < r, - Le => l <= r, - Gt => l > r, - Ge => l >= r, - BitOr => l | r, - BitXor => l ^ r, - BitAnd => l & r, - Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)), - }) +macro_rules! int_arithmetic { + ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ + let l = $l; + let r = $r; + match $kind { + I8 => overflow!(I8, $int_op, l as i8, r as i8), + I16 => overflow!(I16, $int_op, l as i16, r as i16), + I32 => overflow!(I32, $int_op, l as i32, r as i32), + I64 => overflow!(I64, $int_op, l as i64, r as i64), + U8 => overflow!(U8, $int_op, l as u8, r as u8), + U16 => overflow!(U16, $int_op, l as u16, r as u16), + U32 => overflow!(U32, $int_op, l as u32, r as u32), + U64 => overflow!(U64, $int_op, l as u64, r as u64), + _ => bug!("int_arithmetic should only be called on int primvals"), } + }) +} + +macro_rules! int_shift { + ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ + let l = $l; + let r = $r; + match $kind { + I8 => overflow!(I8, $int_op, l as i8, r), + I16 => overflow!(I16, $int_op, l as i16, r), + I32 => overflow!(I32, $int_op, l as i32, r), + I64 => overflow!(I64, $int_op, l as i64, r), + U8 => overflow!(U8, $int_op, l as u8, r), + U16 => overflow!(U16, $int_op, l as u16, r), + U32 => overflow!(U32, $int_op, l as u32, r), + U64 => overflow!(U64, $int_op, l as u64, r), + _ => bug!("int_shift should only be called on int primvals"), + } + }) +} + +macro_rules! float_arithmetic { + ($kind:expr, $from_bits:ident, $to_bits:ident, $float_op:tt, $l:expr, $r:expr) => ({ + let l = $from_bits($l); + let r = $from_bits($r); + let bits = $to_bits(l $float_op r); + PrimVal::new(bits, $kind) + }) +} + +macro_rules! f32_arithmetic { + ($float_op:tt, $l:expr, $r:expr) => ( + float_arithmetic!(F32, bits_to_f32, f32_to_bits, $float_op, $l, $r) + ) +} + +macro_rules! f64_arithmetic { + ($float_op:tt, $l:expr, $r:expr) => ( + float_arithmetic!(F64, bits_to_f64, f64_to_bits, $float_op, $l, $r) + ) +} +/// Returns the result of the specified operation and whether it overflowed. +pub fn binary_op<'tcx>( + bin_op: mir::BinOp, + left: PrimVal, + right: PrimVal +) -> EvalResult<'tcx, (PrimVal, bool)> { + use rustc::mir::repr::BinOp::*; + use self::PrimValKind::*; + + match (left.kind, right.kind) { (FnPtr(_), Ptr(_)) | - (Ptr(_), FnPtr(_)) => - unrelated_ptr_ops(bin_op)?, - - (FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op { - Eq => Bool(l_ptr == r_ptr), - Ne => Bool(l_ptr != r_ptr), - _ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))), - }, - - (Ptr(l_ptr), Ptr(r_ptr)) => { - if l_ptr.alloc_id != r_ptr.alloc_id { - return Ok((unrelated_ptr_ops(bin_op)?, false)); - } + (Ptr(_), FnPtr(_)) => return Ok((unrelated_ptr_ops(bin_op)?, false)), - let l = l_ptr.offset; - let r = r_ptr.offset; + (Ptr(l_alloc), Ptr(r_alloc)) if l_alloc != r_alloc + => return Ok((unrelated_ptr_ops(bin_op)?, false)), + (FnPtr(l_alloc), FnPtr(r_alloc)) => { match bin_op { - Eq => Bool(l == r), - Ne => Bool(l != r), - Lt => Bool(l < r), - Le => Bool(l <= r), - Gt => Bool(l > r), - Ge => Bool(l >= r), - _ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))), + Eq => return Ok((PrimVal::from_bool(l_alloc == r_alloc), false)), + Ne => return Ok((PrimVal::from_bool(l_alloc != r_alloc), false)), + _ => {} } } - (l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))), + _ => {} + } + + let (l, r) = (left.bits, right.bits); + + // These ops can have an RHS with a different numeric type. + if bin_op == Shl || bin_op == Shr { + // These are the maximum values a bitshift RHS could possibly have. For example, u16 + // can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in + // that range. + let type_bits: u32 = match left.kind { + I8 | U8 => 8, + I16 | U16 => 16, + I32 | U32 => 32, + I64 | U64 => 64, + _ => bug!("bad MIR: bitshift lhs is not integral"), + }; + + // Cast to `u32` because `overflowing_sh{l,r}` only take `u32`, then apply the bitmask + // to ensure it's within the valid shift value range. + let r = (right.bits as u32) & (type_bits - 1); + + return match bin_op { + Shl => int_shift!(left.kind, overflowing_shl, l, r), + Shr => int_shift!(left.kind, overflowing_shr, l, r), + _ => bug!("it has already been checked that this is a shift op"), + }; + } + + if left.kind != right.kind { + let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); + return Err(EvalError::Unimplemented(msg)); + } + + let val = match (bin_op, left.kind) { + (Eq, F32) => PrimVal::from_bool(bits_to_f32(l) == bits_to_f32(r)), + (Ne, F32) => PrimVal::from_bool(bits_to_f32(l) != bits_to_f32(r)), + (Lt, F32) => PrimVal::from_bool(bits_to_f32(l) < bits_to_f32(r)), + (Le, F32) => PrimVal::from_bool(bits_to_f32(l) <= bits_to_f32(r)), + (Gt, F32) => PrimVal::from_bool(bits_to_f32(l) > bits_to_f32(r)), + (Ge, F32) => PrimVal::from_bool(bits_to_f32(l) >= bits_to_f32(r)), + + (Eq, F64) => PrimVal::from_bool(bits_to_f64(l) == bits_to_f64(r)), + (Ne, F64) => PrimVal::from_bool(bits_to_f64(l) != bits_to_f64(r)), + (Lt, F64) => PrimVal::from_bool(bits_to_f64(l) < bits_to_f64(r)), + (Le, F64) => PrimVal::from_bool(bits_to_f64(l) <= bits_to_f64(r)), + (Gt, F64) => PrimVal::from_bool(bits_to_f64(l) > bits_to_f64(r)), + (Ge, F64) => PrimVal::from_bool(bits_to_f64(l) >= bits_to_f64(r)), + + (Add, F32) => f32_arithmetic!(+, l, r), + (Sub, F32) => f32_arithmetic!(-, l, r), + (Mul, F32) => f32_arithmetic!(*, l, r), + (Div, F32) => f32_arithmetic!(/, l, r), + (Rem, F32) => f32_arithmetic!(%, l, r), + + (Add, F64) => f64_arithmetic!(+, l, r), + (Sub, F64) => f64_arithmetic!(-, l, r), + (Mul, F64) => f64_arithmetic!(*, l, r), + (Div, F64) => f64_arithmetic!(/, l, r), + (Rem, F64) => f64_arithmetic!(%, l, r), + + (Eq, _) => PrimVal::from_bool(l == r), + (Ne, _) => PrimVal::from_bool(l != r), + (Lt, _) => PrimVal::from_bool(l < r), + (Le, _) => PrimVal::from_bool(l <= r), + (Gt, _) => PrimVal::from_bool(l > r), + (Ge, _) => PrimVal::from_bool(l >= r), + + (BitOr, k) => PrimVal::new(l | r, k), + (BitAnd, k) => PrimVal::new(l & r, k), + (BitXor, k) => PrimVal::new(l ^ r, k), + + (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), + (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), + (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), + (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), + (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + + _ => { + let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); + return Err(EvalError::Unimplemented(msg)); + } }; Ok((val, false)) } +fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { + use rustc::mir::repr::BinOp::*; + match bin_op { + Eq => Ok(PrimVal::from_bool(false)), + Ne => Ok(PrimVal::from_bool(true)), + Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), + _ => bug!(), + } +} + pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { use rustc::mir::repr::UnOp::*; - use self::PrimVal::*; - match (un_op, val) { - (Not, Bool(b)) => Ok(Bool(!b)), - (Not, I8(n)) => Ok(I8(!n)), - (Neg, I8(n)) => Ok(I8(-n)), - (Not, I16(n)) => Ok(I16(!n)), - (Neg, I16(n)) => Ok(I16(-n)), - (Not, I32(n)) => Ok(I32(!n)), - (Neg, I32(n)) => Ok(I32(-n)), - (Not, I64(n)) => Ok(I64(!n)), - (Neg, I64(n)) => Ok(I64(-n)), - (Not, U8(n)) => Ok(U8(!n)), - (Not, U16(n)) => Ok(U16(!n)), - (Not, U32(n)) => Ok(U32(!n)), - (Not, U64(n)) => Ok(U64(!n)), - - (Neg, F64(n)) => Ok(F64(-n)), - (Neg, F32(n)) => Ok(F32(-n)), - _ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))), - } + use self::PrimValKind::*; + + let bits = match (un_op, val.kind) { + (Not, Bool) => !bits_to_bool(val.bits) as u64, + + (Not, U8) => !(val.bits as u8) as u64, + (Not, U16) => !(val.bits as u16) as u64, + (Not, U32) => !(val.bits as u32) as u64, + (Not, U64) => !val.bits, + + (Not, I8) => !(val.bits as i8) as u64, + (Not, I16) => !(val.bits as i16) as u64, + (Not, I32) => !(val.bits as i32) as u64, + (Not, I64) => !(val.bits as i64) as u64, + + (Neg, I8) => -(val.bits as i8) as u64, + (Neg, I16) => -(val.bits as i16) as u64, + (Neg, I32) => -(val.bits as i32) as u64, + (Neg, I64) => -(val.bits as i64) as u64, + + (Neg, F32) => f32_to_bits(-bits_to_f32(val.bits)), + (Neg, F64) => f64_to_bits(-bits_to_f64(val.bits)), + + _ => { + let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); + return Err(EvalError::Unimplemented(msg)); + } + }; + + Ok(PrimVal::new(bits, val.kind)) } From ed679c3d238b7ece32d5072067d1a192318c0945 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 20 Oct 2016 13:10:22 +0200 Subject: [PATCH 0582/1096] make some pieces public that are required by priroda --- src/interpreter/mod.rs | 6 +++--- src/lib.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 463b9bf006392..0b39da9f382ce 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -17,7 +17,7 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer, AllocId}; use primval::{self, PrimVal, PrimValKind}; -use self::value::Value; +pub use self::value::Value; use std::collections::HashMap; @@ -1462,7 +1462,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { - fn get_local(&self, local: mir::Local) -> Option { + pub fn get_local(&self, local: mir::Local) -> Option { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1] } @@ -1474,7 +1474,7 @@ impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { } impl Lvalue { - fn from_ptr(ptr: Pointer) -> Self { + pub fn from_ptr(ptr: Pointer) -> Self { Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } } diff --git a/src/lib.rs b/src/lib.rs index 2f2894a59ee20..3ed75a54c2a51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,9 @@ pub use interpreter::{ eval_main, run_mir_passes, StackPopCleanup, + Value, + Lvalue, + LvalueExtra, }; pub use memory::{ @@ -43,3 +46,8 @@ pub use memory::{ Pointer, AllocId, }; + +pub use primval::{ + PrimVal, + PrimValKind, +}; From 24be49f7dd927d74af8a5adae672fa35234740e0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 10:29:56 +0200 Subject: [PATCH 0583/1096] add a 'tcx lifetime to Lvalue in preparation for statics --- src/interpreter/mod.rs | 28 ++++++++++++------------ src/interpreter/terminator/intrinsics.rs | 2 +- src/interpreter/terminator/mod.rs | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0b39da9f382ce..2663ea159dcdd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -77,7 +77,7 @@ pub struct Frame<'a, 'tcx: 'a> { pub return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to. - pub return_lvalue: Lvalue, + pub return_lvalue: Lvalue<'tcx>, /// The list of locals for this stack frame, stored in order as /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which @@ -99,7 +99,7 @@ pub struct Frame<'a, 'tcx: 'a> { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Lvalue { +pub enum Lvalue<'tcx> { /// An lvalue referring to a value allocated in the `Memory` system. Ptr { ptr: Pointer, @@ -347,7 +347,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, - return_lvalue: Lvalue, + return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation += 1; @@ -406,7 +406,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - dest: Lvalue, + dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; @@ -421,7 +421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { op: mir::BinOp, left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, - dest: Lvalue, + dest: Lvalue<'tcx>, ) -> EvalResult<'tcx, bool> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; self.write_primval(dest, val)?; @@ -430,7 +430,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn assign_fields>( &mut self, - dest: Lvalue, + dest: Lvalue<'tcx>, offsets: I, operands: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, ()> { @@ -863,7 +863,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { + fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::repr::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, @@ -900,7 +900,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue_projection( &mut self, proj: &mir::LvalueProjection<'tcx>, - ) -> EvalResult<'tcx, Lvalue> { + ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -1071,7 +1071,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn force_allocation(&mut self, lvalue: Lvalue) -> EvalResult<'tcx, Lvalue> { + fn force_allocation(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { Lvalue::Local { frame, local } => { let ptr = match self.stack[frame].get_local(local) { @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn write_primval( &mut self, - dest: Lvalue, + dest: Lvalue<'tcx>, val: PrimVal, ) -> EvalResult<'tcx, ()> { match dest { @@ -1175,7 +1175,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn write_value( &mut self, src_val: Value, - dest: Lvalue, + dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match dest { @@ -1441,7 +1441,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn dump_local(&self, lvalue: Lvalue) { + fn dump_local(&self, lvalue: Lvalue<'tcx>) { if let Lvalue::Local { frame, local } = lvalue { if let Some(val) = self.stack[frame].get_local(local) { match val { @@ -1473,7 +1473,7 @@ impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { } } -impl Lvalue { +impl<'tcx> Lvalue<'tcx> { pub fn from_ptr(ptr: Pointer) -> Self { Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } } @@ -1492,7 +1492,7 @@ impl Lvalue { ptr } - fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { + fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { match ty.sty { ty::TyArray(elem, n) => (elem, n as u64), diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index c3327dd76f9d0..c4289375432f6 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -15,7 +15,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], - dest: Lvalue, + dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index be6147f8b742c..e8be90cf91aec 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -150,7 +150,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy, - destination: Option<(Lvalue, mir::BasicBlock)>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx, ()> { @@ -264,7 +264,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, def_id: DefId, args: &[mir::Operand<'tcx>], - dest: Lvalue, + dest: Lvalue<'tcx>, dest_size: usize, ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); From bef879083e10461b5a3897e85958a289299594c9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 10:31:13 +0200 Subject: [PATCH 0584/1096] split eval_and_read_lvalue into two functions --- src/interpreter/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2663ea159dcdd..a7cc27a8e5f93 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -851,8 +851,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } + let lvalue = self.eval_lvalue(lvalue)?; + self.read_lvalue(lvalue) + } - match self.eval_lvalue(lvalue)? { + fn read_lvalue(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); Ok(Value::ByRef(ptr)) From a75e7f76865b5359b72bd8400a30083364ef180d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 10:32:27 +0200 Subject: [PATCH 0585/1096] don't allocate statics unless a reference to them is created --- src/interpreter/mod.rs | 200 ++++++++++++++++++++++++++++------------ src/interpreter/step.rs | 21 ++--- 2 files changed, 149 insertions(+), 72 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7cc27a8e5f93..405a8b41c6cdc 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -15,7 +15,7 @@ use std::rc::Rc; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; -use memory::{Memory, Pointer, AllocId}; +use memory::{Memory, Pointer}; use primval::{self, PrimVal, PrimValKind}; pub use self::value::Value; @@ -41,8 +41,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. - // FIXME(solson): Change from Pointer to Value. - statics: HashMap, Pointer>, + statics: HashMap, Constant<'tcx>>, /// The virtual call stack. stack: Vec>, @@ -111,9 +110,11 @@ pub enum Lvalue<'tcx> { Local { frame: usize, local: mir::Local, - } + }, + + Static(ConstantId<'tcx>), - // TODO(solson): Static/Const? None/Never? + // TODO(solson): None/Never? } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -130,9 +131,9 @@ pub enum CachedMir<'mir, 'tcx: 'mir> { Owned(Rc>) } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] /// Uniquely identifies a specific constant or static -struct ConstantId<'tcx> { +pub struct ConstantId<'tcx> { /// the def id of the constant/static or in case of promoteds, the def id of the function they belong to def_id: DefId, /// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated @@ -143,18 +144,36 @@ struct ConstantId<'tcx> { kind: ConstantKind, } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] enum ConstantKind { Promoted(mir::Promoted), /// Statics, constants and associated constants Global, } +#[derive(Copy, Clone, Debug)] +pub struct Constant<'tcx> { + data: Option, + mutable: bool, + ty: Ty<'tcx>, +} + +impl<'tcx> Constant<'tcx> { + fn uninitialized(ty: Ty<'tcx>) -> Self { + Constant { + data: None, + mutable: true, + ty: ty, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum StackPopCleanup { /// The stackframe existed to compute the initial value of a static/constant, make sure the - /// static isn't modifyable afterwards - Freeze(AllocId), + /// static isn't modifyable afterwards. The allocation of the result is frozen iff it's an + /// actual allocation. `PrimVal`s are unmodifyable anyway. + Freeze, /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), @@ -380,7 +399,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { - StackPopCleanup::Freeze(alloc_id) => self.memory.freeze(alloc_id)?, + StackPopCleanup::Freeze => if let Lvalue::Static(id) = frame.return_lvalue { + let static_value = self.statics + .get_mut(&id) + .expect("static should have been cached (freeze)"); + if let Value::ByRef(ptr) = static_value.data.expect("static should have been initialized") { + self.memory.freeze(ptr.alloc_id)?; + } + assert!(static_value.mutable); + static_value.mutable = false; + } else { + bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue); + }, StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, } @@ -817,9 +847,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - let static_ptr = *self.statics.get(&cid) - .expect("static should have been cached (rvalue)"); - Value::ByRef(static_ptr) + self.read_lvalue(Lvalue::Static(cid))? } } @@ -829,9 +857,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), kind: ConstantKind::Promoted(index), }; - let static_ptr = *self.statics.get(&cid) - .expect("a promoted constant hasn't been precomputed"); - Value::ByRef(static_ptr) + self.read_lvalue(Lvalue::Static(cid))? } }; @@ -864,6 +890,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local } => { self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) } + Lvalue::Static(cid) => Ok(self.statics + .get(&cid) + .expect("static not cached") + .data + .expect("static not initialized")), } } @@ -886,9 +917,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - let ptr = *self.statics.get(&cid) - .expect("static should have been cached (lvalue)"); - Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + Lvalue::Static(cid) } Projection(ref proj) => return self.eval_lvalue_projection(proj), @@ -1078,8 +1107,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn force_allocation(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { Lvalue::Local { frame, local } => { - let ptr = match self.stack[frame].get_local(local) { - Some(Value::ByRef(ptr)) => ptr, + match self.stack[frame].get_local(local) { + Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), opt_val => { let ty = self.stack[frame].mir.local_decls[local].ty; let substs = self.stack[frame].substs; @@ -1088,12 +1117,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(val) = opt_val { self.write_value_to_ptr(val, ptr, ty)?; } - ptr + Lvalue::from_ptr(ptr) } - }; - Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + } } Lvalue::Ptr { .. } => lvalue, + Lvalue::Static(cid) => { + let static_val = *self.statics.get(&cid).expect("static not cached"); + match static_val.data { + Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), + _ => { + let ptr = self.alloc_ptr_with_substs(static_val.ty, cid.substs)?; + if let Some(val) = static_val.data { + self.write_value_to_ptr(val, ptr, static_val.ty)?; + } + if !static_val.mutable { + self.memory.freeze(ptr.alloc_id)?; + } + let lval = self.statics.get_mut(&cid).expect("already checked"); + *lval = Constant { + data: Some(Value::ByRef(ptr)), + .. static_val + }; + Lvalue::from_ptr(ptr) + }, + } + } }; Ok(new_lvalue) } @@ -1173,6 +1222,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack[frame].set_local(local, Value::ByVal(val)); Ok(()) } + Lvalue::Static(cid) => { + let static_val = self.statics.get_mut(&cid).expect("static not cached"); + assert!(static_val.mutable); + static_val.data = Some(Value::ByVal(val)); + Ok(()) + } } } @@ -1183,48 +1238,73 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match dest { + Lvalue::Static(cid) => { + let dest = *self.statics.get_mut(&cid).expect("static should be cached"); + assert!(dest.mutable); + self.write_value_to_dest( + src_val, + |this, val| *this.statics.get_mut(&cid).expect("already checked") = Constant { data: Some(val), ..dest }, + dest.data, + dest_ty, + ) + }, + Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(src_val, ptr, dest_ty)?; + self.write_value_to_ptr(src_val, ptr, dest_ty) } - // The cases here can be a bit subtle. Read carefully! Lvalue::Local { frame, local } => { - let dest_val = self.stack[frame].get_local(local); - - if let Some(Value::ByRef(dest_ptr)) = dest_val { - // If the local value is already `ByRef` (that is, backed by an `Allocation`), - // then we must write the new value into this allocation, because there may be - // other pointers into the allocation. These other pointers are logically - // pointers into the local variable, and must be able to observe the change. - // - // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we - // knew for certain that there were no outstanding pointers to this local. - self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; - - } else if let Value::ByRef(src_ptr) = src_val { - // If the local value is not `ByRef`, then we know there are no pointers to it - // and we can simply overwrite the `Value` in the locals array directly. - // - // In this specific case, where the source value is `ByRef`, we must duplicate - // the allocation, because this is a by-value operation. It would be incorrect - // if they referred to the same allocation, since then a change to one would - // implicitly change the other. - // - // TODO(solson): It would be valid to attempt reading a primitive value out of - // the source and writing that into the destination without making an - // allocation. This would be a pure optimization. - let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, dest_ptr, dest_ty)?; - self.stack[frame].set_local(local, Value::ByRef(dest_ptr)); - - } else { - // Finally, we have the simple case where neither source nor destination are - // `ByRef`. We may simply copy the source value over the the destintion local. - self.stack[frame].set_local(local, src_val); - } + let dest = self.stack[frame].get_local(local); + self.write_value_to_dest( + src_val, + |this, val| this.stack[frame].set_local(local, val), + dest, + dest_ty, + ) } } + } + + // The cases here can be a bit subtle. Read carefully! + fn write_value_to_dest( + &mut self, + src_val: Value, + write_dest: F, + dest_val: Option, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, ()> { + if let Some(Value::ByRef(dest_ptr)) = dest_val { + // If the local value is already `ByRef` (that is, backed by an `Allocation`), + // then we must write the new value into this allocation, because there may be + // other pointers into the allocation. These other pointers are logically + // pointers into the local variable, and must be able to observe the change. + // + // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we + // knew for certain that there were no outstanding pointers to this local. + self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; + + } else if let Value::ByRef(src_ptr) = src_val { + // If the local value is not `ByRef`, then we know there are no pointers to it + // and we can simply overwrite the `Value` in the locals array directly. + // + // In this specific case, where the source value is `ByRef`, we must duplicate + // the allocation, because this is a by-value operation. It would be incorrect + // if they referred to the same allocation, since then a change to one would + // implicitly change the other. + // + // TODO(solson): It would be valid to attempt reading a primitive value out of + // the source and writing that into the destination without making an + // allocation. This would be a pure optimization. + let dest_ptr = self.alloc_ptr(dest_ty)?; + self.copy(src_ptr, dest_ptr, dest_ty)?; + write_dest(self, Value::ByRef(dest_ptr)); + + } else { + // Finally, we have the simple case where neither source nor destination are + // `ByRef`. We may simply copy the source value over the the destintion local. + write_dest(self, src_val); + } Ok(()) } diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index c109b7cd4895d..6c7d61b54e405 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -9,6 +9,7 @@ use super::{ Lvalue, ConstantKind, StackPopCleanup, + Constant, }; use error::EvalResult; use rustc::mir::repr as mir; @@ -128,15 +129,13 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } self.try(|this| { let mir = this.ecx.load_mir(def_id)?; - // FIXME(solson): Don't allocate a pointer unconditionally. - let ptr = this.ecx.alloc_ptr_with_substs(mir.return_ty, substs)?; - this.ecx.statics.insert(cid.clone(), ptr); + this.ecx.statics.insert(cid.clone(), Constant::uninitialized(mir.return_ty)); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { - StackPopCleanup::Freeze(ptr.alloc_id) + StackPopCleanup::Freeze } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::from_ptr(ptr), cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Static(cid.clone()), cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -167,6 +166,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } }, mir::Literal::Promoted { index } => { + let mir = self.mir.promoted[index].clone(); let cid = ConstantId { def_id: self.def_id, substs: self.substs, @@ -175,19 +175,16 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if self.ecx.statics.contains_key(&cid) { return; } - let mir = self.mir.promoted[index].clone(); - let return_ty = mir.return_ty; self.try(|this| { - // FIXME(solson): Don't allocate a pointer unconditionally. - let return_ptr = this.ecx.alloc_ptr_with_substs(return_ty, cid.substs)?; let mir = CachedMir::Owned(Rc::new(mir)); - this.ecx.statics.insert(cid.clone(), return_ptr); + let ty = this.ecx.monomorphize(mir.return_ty, this.substs); + this.ecx.statics.insert(cid.clone(), Constant::uninitialized(ty)); this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, - Lvalue::from_ptr(return_ptr), - StackPopCleanup::Freeze(return_ptr.alloc_id)) + Lvalue::Static(cid.clone()), + StackPopCleanup::Freeze) }); } } From b8842b25e8d96d877bdef1eba6ef1116bef56077 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 10:44:48 +0200 Subject: [PATCH 0586/1096] yield a miri error instead of panicking on uninitialized statics --- src/interpreter/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 405a8b41c6cdc..46b4c1c6d6f21 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -890,11 +890,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local } => { self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) } - Lvalue::Static(cid) => Ok(self.statics - .get(&cid) - .expect("static not cached") - .data - .expect("static not initialized")), + Lvalue::Static(cid) => self.statics + .get(&cid) + .expect("static not cached") + .data + .ok_or(EvalError::ReadUndefBytes), } } From f81c4ac91b7296dd306f1625fc995d55060a86d7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 10:45:01 +0200 Subject: [PATCH 0587/1096] more priroda requirements --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 46b4c1c6d6f21..23b1b390c695a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -881,7 +881,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.read_lvalue(lvalue) } - fn read_lvalue(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); From e7bcf35f8ac9ac0797d0117166e0158d71acc03b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 21 Oct 2016 03:17:53 -0600 Subject: [PATCH 0588/1096] Simplify PrimValKind and remove a horrible hack. This takes the `AllocId` out of PrimValKind, replacing it with a `relocation` field on `PrimVal`, which is closer to an earlier design for `PrimVal` I discussed with @eddyb. This commit prepares the code for removing the `PrimValKind` from `PrimVal` and making them more pure bitbags. The only code dealing with `PrimValKind` will be code making decisions like "what kind of operation do I need to do on these bits", like operators and casting. Transmutes of `PrimVal`s will become true no-ops, not even adjusting a `kind` field. This commit also removes my horrible `value_to_primval` hack that made an allocation for every `ByVal` passed in, so it could use `read_value` to get a `PrimVal` with the right kind. Now I just compute the `PrimValKind` from the `Ty` and re-tag the `PrimVal`. The code got slightly messier in some areas here, but I think a _lot_ of code will simplify in obvious ways once I remove the `kind` field from `PrimVal`. Gosh, if my commit messages aren't turning into essays these days. --- src/interpreter/cast.rs | 4 +- src/interpreter/mod.rs | 124 +++++++++++++++------- src/interpreter/terminator/intrinsics.rs | 66 ++++++------ src/interpreter/terminator/mod.rs | 6 +- src/interpreter/value.rs | 19 ++-- src/memory.rs | 16 ++- src/primval.rs | 129 +++++++++++++---------- 7 files changed, 214 insertions(+), 150 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 427b8aed0f191..70b39fc882ec5 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -21,8 +21,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), - FnPtr(alloc) | Ptr(alloc) => { - let ptr = Pointer::new(alloc, val.bits as usize); + FnPtr | Ptr => { + let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation"); self.cast_ptr(ptr, ty) } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 463b9bf006392..1e37d3ec75376 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -972,7 +972,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { - use primval::PrimValKind::*; use interpreter::value::Value::*; let val = match self.eval_and_read_lvalue(&proj.base)? { @@ -981,22 +980,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match val { - ByValPair( - PrimVal { kind: Ptr(ptr_alloc), bits: ptr_offset }, - PrimVal { kind: Ptr(vtable_alloc), bits: vtable_offset }, - ) => { - let ptr = Pointer::new(ptr_alloc, ptr_offset as usize); - let vtable = Pointer::new(vtable_alloc, vtable_offset as usize); + ByValPair(ptr, vtable) + if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() + => { + let ptr = ptr.try_as_ptr().unwrap(); + let vtable = vtable.try_as_ptr().unwrap(); (ptr, LvalueExtra::Vtable(vtable)) } - ByValPair(PrimVal { kind: Ptr(alloc), bits: offset }, n) => { - let ptr = Pointer::new(alloc, offset as usize); + ByValPair(ptr, n) if ptr.try_as_ptr().is_some() => { + let ptr = ptr.try_as_ptr().unwrap(); (ptr, LvalueExtra::Length(n.expect_uint("slice length"))) } - ByVal(PrimVal { kind: Ptr(alloc), bits: offset }) => { - let ptr = Pointer::new(alloc, offset as usize); + ByVal(ptr) if ptr.try_as_ptr().is_some() => { + let ptr = ptr.try_as_ptr().unwrap(); (ptr, LvalueExtra::None) } @@ -1122,39 +1120,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), }, - // FIXME(solson): This unnecessarily allocates to work around a new issue my `Value` - // locals refactoring introduced. There is code that calls this function and expects to - // get a PrimVal reflecting the specific type that it asked for, e.g. `PrimVal::Bool` - // when it was asking for `TyBool`. This used to always work because it would go - // through `read_value` which does the right thing. - // - // This is the comment and implementation from before my refactor: - // - // TODO(solson): Sanity-check the primval type against the input type. - // Value::ByVal(primval) => Ok(primval), - // - // Turns out sanity-checking isn't enough now, and we need conversion. - // - // Now that we can possibly be reading a `ByVal` straight out of the locals vec, if the - // user did something tricky like transmuting a `u8` to a `bool`, then we'll have a - // `PrimVal::U8` and need to convert to `PrimVal::Bool`. - // - // I want to avoid handling the full set of conversions between `PrimVal`s, so for now - // I will use this hack. I have a plan to change the representation of `PrimVal` to be - // more like a small piece of memory tagged with a `PrimValKind`, which should make the - // conversion easy and make the problem solveable using code already in `Memory`. Value::ByVal(primval) => { - let ptr = self.alloc_ptr(ty)?; - self.memory.write_primval(ptr, primval)?; - let primval = self.value_to_primval(Value::ByRef(ptr), ty)?; - self.memory.deallocate(ptr)?; - Ok(primval) + let new_primval = self.transmute_primval(primval, ty)?; + self.ensure_valid_value(new_primval, ty)?; + Ok(new_primval) } Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), } } + fn transmute_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + Ok(PrimVal { kind: self.ty_to_primval_kind(ty)?, ..val }) + } + fn write_primval( &mut self, dest: Lvalue, @@ -1252,6 +1231,77 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { + use syntax::ast::FloatTy; + + let kind = match ty.sty { + ty::TyBool => PrimValKind::Bool, + ty::TyChar => PrimValKind::Char, + + ty::TyInt(int_ty) => { + use syntax::ast::IntTy::*; + let size = match int_ty { + I8 => 1, + I16 => 2, + I32 => 4, + I64 => 8, + Is => self.memory.pointer_size(), + }; + PrimValKind::from_int_size(size) + } + + ty::TyUint(uint_ty) => { + use syntax::ast::UintTy::*; + let size = match uint_ty { + U8 => 1, + U16 => 2, + U32 => 4, + U64 => 8, + Us => self.memory.pointer_size(), + }; + PrimValKind::from_uint_size(size) + } + + ty::TyFloat(FloatTy::F32) => PrimValKind::F32, + ty::TyFloat(FloatTy::F64) => PrimValKind::F64, + + ty::TyFnPtr(_) => PrimValKind::FnPtr, + + ty::TyBox(ty) | + ty::TyRef(_, ty::TypeAndMut { ty, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if self.type_is_sized(ty) => PrimValKind::Ptr, + + ty::TyAdt(..) => { + use rustc::ty::layout::Layout::*; + if let CEnum { discr, signed, .. } = *self.type_layout(ty) { + let size = discr.size().bytes() as usize; + if signed { + PrimValKind::from_int_size(size) + } else { + PrimValKind::from_uint_size(size) + } + } else { + bug!("primitive read of non-clike enum: {:?}", ty); + } + }, + + _ => bug!("primitive read of non-primitive type: {:?}", ty), + }; + + Ok(kind) + } + + fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + match ty.sty { + ty::TyBool if val.bits > 1 => Err(EvalError::InvalidBool), + + ty::TyChar if ::std::char::from_u32(val.bits as u32).is_none() + => Err(EvalError::InvalidChar(val.bits as u32 as u64)), + + _ => Ok(()), + } + } + fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::FloatTy; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index c3327dd76f9d0..bb88e3864ee6f 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -19,10 +19,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { - let args_ptrs: EvalResult> = args.iter() + let arg_vals: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); - let args_ptrs = args_ptrs?; + let arg_vals = arg_vals?; let i32 = self.tcx.types.i32; let isize = self.tcx.types.isize; let usize = self.tcx.types.usize; @@ -42,8 +42,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { - let ptr = args_ptrs[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(args_ptrs[1], isize)? + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; @@ -51,23 +51,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "assume" => { let bool = self.tcx.types.bool; - let cond = self.value_to_primval(args_ptrs[0], bool)? - .expect_bool("assume arg not bool"); + let cond = self.value_to_primval(arg_vals[0], bool)?.try_as_bool()?; if !cond { return Err(EvalError::AssumptionNotHeld); } } "atomic_load" | "volatile_load" => { let ty = substs.type_at(0); - let ptr = args_ptrs[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } "atomic_store" | "volatile_store" => { let ty = substs.type_at(0); - let dest = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value_to_ptr(args_ptrs[1], dest, ty)?; + let dest = arg_vals[0].read_ptr(&self.memory)?; + self.write_value_to_ptr(arg_vals[1], dest, ty)?; } "breakpoint" => unimplemented!(), // halt miri @@ -78,9 +77,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); - let src = args_ptrs[0].read_ptr(&self.memory)?; - let dest = args_ptrs[1].read_ptr(&self.memory)?; - let count = self.value_to_primval(args_ptrs[2], usize)? + let src = arg_vals[0].read_ptr(&self.memory)?; + let dest = arg_vals[1].read_ptr(&self.memory)?; + let count = self.value_to_primval(arg_vals[2], usize)? .expect_uint("arith_offset second arg not isize"); self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } @@ -90,34 +89,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "ctlz" | "bswap" => { let elem_ty = substs.type_at(0); - let num = self.value_to_primval(args_ptrs[0], elem_ty)?; + let num = self.value_to_primval(arg_vals[0], elem_ty)?; let num = numeric_intrinsic(intrinsic_name, num); self.write_primval(dest, num)?; } "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?; + let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?; } "fabsf32" => { - let f = self.value_to_primval(args_ptrs[2], f32)? + let f = self.value_to_primval(arg_vals[2], f32)? .expect_f32("fabsf32 read non f32"); self.write_primval(dest, PrimVal::from_f32(f.abs()))?; } "fabsf64" => { - let f = self.value_to_primval(args_ptrs[2], f64)? + let f = self.value_to_primval(arg_vals[2], f64)? .expect_f64("fabsf64 read non f64"); self.write_primval(dest, PrimVal::from_f64(f.abs()))?; } "fadd_fast" => { let ty = substs.type_at(0); - let a = self.value_to_primval(args_ptrs[0], ty)?; - let b = self.value_to_primval(args_ptrs[0], ty)?; + let a = self.value_to_primval(arg_vals[0], ty)?; + let b = self.value_to_primval(arg_vals[0], ty)?; let result = primval::binary_op(mir::BinOp::Add, a, b)?; self.write_primval(dest, result.0)?; } @@ -151,8 +150,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.write_value_to_ptr(args_ptrs[1], ptr, ty)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; + self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } "needs_drop" => { @@ -165,10 +164,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); let pointee_size = self.type_size(pointee_ty) as isize; - let offset = self.value_to_primval(args_ptrs[1], isize)? + let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("offset second arg not isize"); - let ptr = args_ptrs[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); self.write_primval(dest, PrimVal::from_ptr(result_ptr))?; } @@ -186,29 +185,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "powif32" => { - let f = self.value_to_primval(args_ptrs[0], f32)? + let f = self.value_to_primval(arg_vals[0], f32)? .expect_f32("powif32 first arg not f32"); - let i = self.value_to_primval(args_ptrs[1], i32)? + let i = self.value_to_primval(arg_vals[1], i32)? .expect_int("powif32 second arg not i32"); self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?; } "powif64" => { - let f = self.value_to_primval(args_ptrs[0], f64)? + let f = self.value_to_primval(arg_vals[0], f64)? .expect_f64("powif64 first arg not f64"); - let i = self.value_to_primval(args_ptrs[1], i32)? + let i = self.value_to_primval(arg_vals[1], i32)? .expect_int("powif64 second arg not i32"); self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?; } "sqrtf32" => { - let f = self.value_to_primval(args_ptrs[0], f32)? + let f = self.value_to_primval(arg_vals[0], f32)? .expect_f32("sqrtf32 first arg not f32"); self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?; } "sqrtf64" => { - let f = self.value_to_primval(args_ptrs[0], f64)? + let f = self.value_to_primval(arg_vals[0], f64)? .expect_f64("sqrtf64 first arg not f64"); self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?; } @@ -222,7 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of_val" => { let ty = substs.type_at(0); - let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?; + let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; let size_val = self.usize_primval(size); self.write_primval(dest, size_val)?; } @@ -239,8 +238,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "transmute" => { - let ty = substs.type_at(0); - self.write_value(args_ptrs[0], dest, ty)?; + let dest_ty = substs.type_at(1); + let val = match arg_vals[0] { + Value::ByVal(primval) => + Value::ByVal(self.transmute_primval(primval, dest_ty)?), + v => v, + }; + self.write_value(val, dest, dest_ty)?; } "uninit" => { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index be6147f8b742c..1a7f985d20a78 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -35,8 +35,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { - let cond_val = self.eval_operand_to_primval(cond)? - .expect_bool("TerminatorKind::If condition constant was not a bool"); + let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?; self.goto_block(if cond_val { then_target } else { else_target }); } @@ -116,8 +115,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Assert { ref cond, expected, ref msg, target, .. } => { - let cond_val = self.eval_operand_to_primval(cond)? - .expect_bool("TerminatorKind::Assert condition constant was not a bool"); + let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?; if expected == cond_val { self.goto_block(target); } else { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 09b0f8345d878..22698f978184b 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,6 +1,6 @@ use error::EvalResult; use memory::{Memory, Pointer}; -use primval::{PrimVal, PrimValKind}; +use primval::PrimVal; /// A `Value` represents a single self-contained Rust value. /// @@ -23,10 +23,8 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ptr) => mem.read_ptr(ptr), - ByVal(PrimVal { kind: PrimValKind::Ptr(alloc), bits: offset }) | - ByVal(PrimVal { kind: PrimValKind::FnPtr(alloc), bits: offset }) => { - let ptr = Pointer::new(alloc, offset as usize); - Ok(ptr) + ByVal(ptr) if ptr.try_as_ptr().is_some() => { + Ok(ptr.try_as_ptr().unwrap()) } ByValPair(..) => unimplemented!(), @@ -46,12 +44,11 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, vtable)) } - ByValPair( - PrimVal { kind: PrimValKind::Ptr(ptr_alloc), bits: ptr_offset }, - PrimVal { kind: PrimValKind::Ptr(vtable_alloc), bits: vtable_offset }, - ) => { - let ptr = Pointer::new(ptr_alloc, ptr_offset as usize); - let vtable = Pointer::new(vtable_alloc, vtable_offset as usize); + ByValPair(ptr, vtable) + if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() + => { + let ptr = ptr.try_as_ptr().unwrap(); + let vtable = vtable.try_as_ptr().unwrap(); Ok((ptr, vtable)) } diff --git a/src/memory.rs b/src/memory.rs index 6979d7ef3397e..73770ee403f44 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -532,25 +532,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - use primval::PrimValKind::*; - match val.kind { - FnPtr(alloc_id) | Ptr(alloc_id) => { - let p = Pointer::new(alloc_id, val.bits as usize); - return self.write_ptr(ptr, p); - } - _ => {} + pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { + if let Some(ptr) = val.try_as_ptr() { + return self.write_ptr(dest, ptr); } + use primval::PrimValKind::*; let (size, bits) = match val.kind { I8 | U8 | Bool => (1, val.bits as u8 as u64), I16 | U16 => (2, val.bits as u16 as u64), I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), I64 | U64 | F64 => (8, val.bits), - FnPtr(_) | Ptr(_) => bug!("handled above"), + FnPtr | Ptr => bug!("handled above"), }; - self.write_uint(ptr, bits, size) + self.write_uint(dest, bits, size) } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { diff --git a/src/primval.rs b/src/primval.rs index 5008aa24a2a03..597bee6b402de 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -33,6 +33,15 @@ fn bits_to_bool(n: u64) -> bool { #[derive(Clone, Copy, Debug, PartialEq)] pub struct PrimVal { pub bits: u64, + + /// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An + /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only + /// large enough to contain one, hence the `Option`. + pub relocation: Option, + + // FIXME(solson): I think we can make this field unnecessary, or at least move it outside of + // this struct. We can either match over `Ty`s or generate simple `PrimVal`s from `Ty`s and + // match over those to decide which operations to perform on `PrimVal`s. pub kind: PrimValKind, } @@ -43,8 +52,8 @@ pub enum PrimValKind { F32, F64, Bool, Char, - Ptr(AllocId), - FnPtr(AllocId), + Ptr, + FnPtr, } impl PrimValKind { @@ -55,19 +64,43 @@ impl PrimValKind { _ => false, } } + + pub fn from_uint_size(size: usize) -> Self { + match size { + 1 => PrimValKind::U8, + 2 => PrimValKind::U16, + 4 => PrimValKind::U32, + 8 => PrimValKind::U64, + _ => bug!("can't make uint with size {}", size), + } + } + + pub fn from_int_size(size: usize) -> Self { + match size { + 1 => PrimValKind::I8, + 2 => PrimValKind::I16, + 4 => PrimValKind::I32, + 8 => PrimValKind::I64, + _ => bug!("can't make int with size {}", size), + } + } } impl PrimVal { pub fn new(bits: u64, kind: PrimValKind) -> Self { - PrimVal { bits: bits, kind: kind } + PrimVal { bits: bits, relocation: None, kind: kind } + } + + pub fn new_with_relocation(bits: u64, kind: PrimValKind, alloc_id: AllocId) -> Self { + PrimVal { bits: bits, relocation: Some(alloc_id), kind: kind } } pub fn from_ptr(ptr: Pointer) -> Self { - PrimVal::new(ptr.offset as u64, PrimValKind::Ptr(ptr.alloc_id)) + PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::Ptr, ptr.alloc_id) } pub fn from_fn_ptr(ptr: Pointer) -> Self { - PrimVal::new(ptr.offset as u64, PrimValKind::FnPtr(ptr.alloc_id)) + PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::FnPtr, ptr.alloc_id) } pub fn from_bool(b: bool) -> Self { @@ -87,64 +120,58 @@ impl PrimVal { } pub fn from_uint_with_size(n: u64, size: usize) -> Self { - let kind = match size { - 1 => PrimValKind::U8, - 2 => PrimValKind::U16, - 4 => PrimValKind::U32, - 8 => PrimValKind::U64, - _ => bug!("can't make uint ({}) with size {}", n, size), - }; - PrimVal::new(n, kind) + PrimVal::new(n, PrimValKind::from_uint_size(size)) } pub fn from_int_with_size(n: i64, size: usize) -> Self { - let kind = match size { - 1 => PrimValKind::I8, - 2 => PrimValKind::I16, - 4 => PrimValKind::I32, - 8 => PrimValKind::I64, - _ => bug!("can't make int ({}) with size {}", n, size), - }; - PrimVal::new(n as u64, kind) + PrimVal::new(n as u64, PrimValKind::from_int_size(size)) } pub fn to_f32(self) -> f32 { + assert!(self.relocation.is_none()); bits_to_f32(self.bits) } pub fn to_f64(self) -> f64 { + assert!(self.relocation.is_none()); bits_to_f64(self.bits) } + pub fn try_as_ptr(self) -> Option { + self.relocation.map(|alloc_id| { + Pointer::new(alloc_id, self.bits as usize) + }) + } + pub fn expect_uint(self, error_msg: &str) -> u64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as u64 + } + use self::PrimValKind::*; match self.kind { U8 | U16 | U32 | U64 => self.bits, - Ptr(alloc_id) => { - let ptr = Pointer::new(alloc_id, self.bits as usize); - ptr.to_int().expect("non abstract ptr") as u64 - } _ => bug!("{}", error_msg), } } pub fn expect_int(self, error_msg: &str) -> i64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as i64 + } + use self::PrimValKind::*; match self.kind { I8 | I16 | I32 | I64 => self.bits as i64, - Ptr(alloc_id) => { - let ptr = Pointer::new(alloc_id, self.bits as usize); - ptr.to_int().expect("non abstract ptr") as i64 - } _ => bug!("{}", error_msg), } } - pub fn expect_bool(self, error_msg: &str) -> bool { - match (self.kind, self.bits) { - (PrimValKind::Bool, 0) => false, - (PrimValKind::Bool, 1) => true, - _ => bug!("{}", error_msg), + pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { + match self.bits { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(EvalError::InvalidBool), } } @@ -163,17 +190,12 @@ impl PrimVal { } pub fn expect_ptr(self, error_msg: &str) -> Pointer { - match self.kind { - PrimValKind::Ptr(alloc_id) => Pointer::new(alloc_id, self.bits as usize), - _ => bug!("{}", error_msg), - } + self.try_as_ptr().expect(error_msg) } + /// FIXME(solson): Refactored into a duplicate of `expect_ptr`. Investigate removal. pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer { - match self.kind { - PrimValKind::FnPtr(alloc_id) => Pointer::new(alloc_id, self.bits as usize), - _ => bug!("{}", error_msg), - } + self.try_as_ptr().expect(error_msg) } } @@ -255,22 +277,19 @@ pub fn binary_op<'tcx>( use rustc::mir::repr::BinOp::*; use self::PrimValKind::*; - match (left.kind, right.kind) { - (FnPtr(_), Ptr(_)) | - (Ptr(_), FnPtr(_)) => return Ok((unrelated_ptr_ops(bin_op)?, false)), - - (Ptr(l_alloc), Ptr(r_alloc)) if l_alloc != r_alloc - => return Ok((unrelated_ptr_ops(bin_op)?, false)), - - (FnPtr(l_alloc), FnPtr(r_alloc)) => { - match bin_op { - Eq => return Ok((PrimVal::from_bool(l_alloc == r_alloc), false)), - Ne => return Ok((PrimVal::from_bool(l_alloc != r_alloc), false)), - _ => {} + match (left.try_as_ptr(), right.try_as_ptr()) { + (Some(left_ptr), Some(right_ptr)) => { + if left_ptr.alloc_id != right_ptr.alloc_id { + return Ok((unrelated_ptr_ops(bin_op)?, false)); } + + // If the pointers are into the same allocation, fall through to the more general match + // later, which will do comparisons on the `bits` fields, which are the pointer offsets + // in this case. } - _ => {} + (None, None) => {} + _ => unimplemented!(), } let (l, r) = (left.bits, right.bits); From 9e9586da9555d779be81bd27f2390dced5ff78f4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:21:52 +0200 Subject: [PATCH 0589/1096] constant ids are `Copy` now --- src/interpreter/step.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 6c7d61b54e405..1207bed97a3aa 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -129,13 +129,13 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } self.try(|this| { let mir = this.ecx.load_mir(def_id)?; - this.ecx.statics.insert(cid.clone(), Constant::uninitialized(mir.return_ty)); + this.ecx.statics.insert(cid, Constant::uninitialized(mir.return_ty)); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { StackPopCleanup::Freeze } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Static(cid.clone()), cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Static(cid), cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -178,12 +178,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let mir = CachedMir::Owned(Rc::new(mir)); let ty = this.ecx.monomorphize(mir.return_ty, this.substs); - this.ecx.statics.insert(cid.clone(), Constant::uninitialized(ty)); + this.ecx.statics.insert(cid, Constant::uninitialized(ty)); this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, - Lvalue::Static(cid.clone()), + Lvalue::Static(cid), StackPopCleanup::Freeze) }); } From f6bbea0f08469fde3e7b9f1b7b4e81690a9482a8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:24:10 +0200 Subject: [PATCH 0590/1096] choose better function and argument names --- src/interpreter/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 23b1b390c695a..7069a72389ca4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1241,7 +1241,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Static(cid) => { let dest = *self.statics.get_mut(&cid).expect("static should be cached"); assert!(dest.mutable); - self.write_value_to_dest( + self.write_value_possibly_by_val( src_val, |this, val| *this.statics.get_mut(&cid).expect("already checked") = Constant { data: Some(val), ..dest }, dest.data, @@ -1256,7 +1256,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local } => { let dest = self.stack[frame].get_local(local); - self.write_value_to_dest( + self.write_value_possibly_by_val( src_val, |this, val| this.stack[frame].set_local(local, val), dest, @@ -1267,14 +1267,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // The cases here can be a bit subtle. Read carefully! - fn write_value_to_dest( + fn write_value_possibly_by_val( &mut self, src_val: Value, write_dest: F, - dest_val: Option, + old_dest_val: Option, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { - if let Some(Value::ByRef(dest_ptr)) = dest_val { + if let Some(Value::ByRef(dest_ptr)) = old_dest_val { // If the local value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be // other pointers into the allocation. These other pointers are logically From d3b3c56b07ee480ea4633e21e33207b4e239950d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:39:39 +0200 Subject: [PATCH 0591/1096] rename statics/Constant/ConstantId/ConstantKind to [gG]lobal* --- src/interpreter/mod.rs | 46 ++++++++++++++++++++--------------------- src/interpreter/step.rs | 22 ++++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7069a72389ca4..4e6c80f9aab67 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -41,7 +41,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. - statics: HashMap, Constant<'tcx>>, + globals: HashMap, Global<'tcx>>, /// The virtual call stack. stack: Vec>, @@ -112,7 +112,7 @@ pub enum Lvalue<'tcx> { local: mir::Local, }, - Static(ConstantId<'tcx>), + Static(GlobalId<'tcx>), // TODO(solson): None/Never? } @@ -133,7 +133,7 @@ pub enum CachedMir<'mir, 'tcx: 'mir> { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] /// Uniquely identifies a specific constant or static -pub struct ConstantId<'tcx> { +pub struct GlobalId<'tcx> { /// the def id of the constant/static or in case of promoteds, the def id of the function they belong to def_id: DefId, /// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated @@ -141,26 +141,26 @@ pub struct ConstantId<'tcx> { /// but that would only require more branching when working with constants, and not bring any /// real benefits. substs: &'tcx Substs<'tcx>, - kind: ConstantKind, + kind: GlobalKind, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -enum ConstantKind { +enum GlobalKind { Promoted(mir::Promoted), /// Statics, constants and associated constants Global, } #[derive(Copy, Clone, Debug)] -pub struct Constant<'tcx> { +pub struct Global<'tcx> { data: Option, mutable: bool, ty: Ty<'tcx>, } -impl<'tcx> Constant<'tcx> { +impl<'tcx> Global<'tcx> { fn uninitialized(ty: Ty<'tcx>) -> Self { - Constant { + Global { data: None, mutable: true, ty: ty, @@ -188,7 +188,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(&tcx.data_layout, memory_size), - statics: HashMap::new(), + globals: HashMap::new(), stack: Vec::new(), stack_limit: stack_limit, } @@ -400,7 +400,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { StackPopCleanup::Freeze => if let Lvalue::Static(id) = frame.return_lvalue { - let static_value = self.statics + let static_value = self.globals .get_mut(&id) .expect("static should have been cached (freeze)"); if let Value::ByRef(ptr) = static_value.data.expect("static should have been initialized") { @@ -842,20 +842,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // function items are zero sized Value::ByRef(self.memory.allocate(0, 0)?) } else { - let cid = ConstantId { + let cid = GlobalId { def_id: def_id, substs: substs, - kind: ConstantKind::Global, + kind: GlobalKind::Global, }; self.read_lvalue(Lvalue::Static(cid))? } } Literal::Promoted { index } => { - let cid = ConstantId { + let cid = GlobalId { def_id: self.frame().def_id, substs: self.substs(), - kind: ConstantKind::Promoted(index), + kind: GlobalKind::Promoted(index), }; self.read_lvalue(Lvalue::Static(cid))? } @@ -890,7 +890,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local } => { self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) } - Lvalue::Static(cid) => self.statics + Lvalue::Static(cid) => self.globals .get(&cid) .expect("static not cached") .data @@ -912,10 +912,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Static(def_id) => { let substs = subst::Substs::empty(self.tcx); - let cid = ConstantId { + let cid = GlobalId { def_id: def_id, substs: substs, - kind: ConstantKind::Global, + kind: GlobalKind::Global, }; Lvalue::Static(cid) } @@ -1123,7 +1123,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Lvalue::Ptr { .. } => lvalue, Lvalue::Static(cid) => { - let static_val = *self.statics.get(&cid).expect("static not cached"); + let static_val = *self.globals.get(&cid).expect("static not cached"); match static_val.data { Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), _ => { @@ -1134,8 +1134,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !static_val.mutable { self.memory.freeze(ptr.alloc_id)?; } - let lval = self.statics.get_mut(&cid).expect("already checked"); - *lval = Constant { + let lval = self.globals.get_mut(&cid).expect("already checked"); + *lval = Global { data: Some(Value::ByRef(ptr)), .. static_val }; @@ -1223,7 +1223,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } Lvalue::Static(cid) => { - let static_val = self.statics.get_mut(&cid).expect("static not cached"); + let static_val = self.globals.get_mut(&cid).expect("static not cached"); assert!(static_val.mutable); static_val.data = Some(Value::ByVal(val)); Ok(()) @@ -1239,11 +1239,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { match dest { Lvalue::Static(cid) => { - let dest = *self.statics.get_mut(&cid).expect("static should be cached"); + let dest = *self.globals.get_mut(&cid).expect("static should be cached"); assert!(dest.mutable); self.write_value_possibly_by_val( src_val, - |this, val| *this.statics.get_mut(&cid).expect("already checked") = Constant { data: Some(val), ..dest }, + |this, val| *this.globals.get_mut(&cid).expect("already checked") = Global { data: Some(val), ..dest }, dest.data, dest_ty, ) diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 1207bed97a3aa..f25eea875fc32 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -4,12 +4,12 @@ use super::{ CachedMir, - ConstantId, + GlobalId, EvalContext, Lvalue, - ConstantKind, + GlobalKind, StackPopCleanup, - Constant, + Global, }; use error::EvalResult; use rustc::mir::repr as mir; @@ -119,17 +119,17 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) { - let cid = ConstantId { + let cid = GlobalId { def_id: def_id, substs: substs, - kind: ConstantKind::Global, + kind: GlobalKind::Global, }; - if self.ecx.statics.contains_key(&cid) { + if self.ecx.globals.contains_key(&cid) { return; } self.try(|this| { let mir = this.ecx.load_mir(def_id)?; - this.ecx.statics.insert(cid, Constant::uninitialized(mir.return_ty)); + this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { StackPopCleanup::Freeze } else { @@ -167,18 +167,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { }, mir::Literal::Promoted { index } => { let mir = self.mir.promoted[index].clone(); - let cid = ConstantId { + let cid = GlobalId { def_id: self.def_id, substs: self.substs, - kind: ConstantKind::Promoted(index), + kind: GlobalKind::Promoted(index), }; - if self.ecx.statics.contains_key(&cid) { + if self.ecx.globals.contains_key(&cid) { return; } self.try(|this| { let mir = CachedMir::Owned(Rc::new(mir)); let ty = this.ecx.monomorphize(mir.return_ty, this.substs); - this.ecx.statics.insert(cid, Constant::uninitialized(ty)); + this.ecx.globals.insert(cid, Global::uninitialized(ty)); this.ecx.push_stack_frame(this.def_id, constant.span, mir, From 2f81729e76e4bbd9acd2b1e5fb9bbc21eed3ba0f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:48:56 +0200 Subject: [PATCH 0592/1096] rename more [Ss]tatic* to [Gg]lobal* --- src/interpreter/mod.rs | 56 ++++++++++++++++++++--------------------- src/interpreter/step.rs | 4 +-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4e6c80f9aab67..bcab8be53e9d0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -112,7 +112,7 @@ pub enum Lvalue<'tcx> { local: mir::Local, }, - Static(GlobalId<'tcx>), + Global(GlobalId<'tcx>), // TODO(solson): None/Never? } @@ -170,8 +170,8 @@ impl<'tcx> Global<'tcx> { #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum StackPopCleanup { - /// The stackframe existed to compute the initial value of a static/constant, make sure the - /// static isn't modifyable afterwards. The allocation of the result is frozen iff it's an + /// The stackframe existed to compute the initial value of a static/constant, make sure it + /// isn't modifyable afterwards. The allocation of the result is frozen iff it's an /// actual allocation. `PrimVal`s are unmodifyable anyway. Freeze, /// A regular stackframe added due to a function call will need to get forwarded to the next @@ -399,15 +399,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { - StackPopCleanup::Freeze => if let Lvalue::Static(id) = frame.return_lvalue { - let static_value = self.globals + StackPopCleanup::Freeze => if let Lvalue::Global(id) = frame.return_lvalue { + let global_value = self.globals .get_mut(&id) - .expect("static should have been cached (freeze)"); - if let Value::ByRef(ptr) = static_value.data.expect("static should have been initialized") { + .expect("global should have been cached (freeze)"); + if let Value::ByRef(ptr) = global_value.data.expect("global should have been initialized") { self.memory.freeze(ptr.alloc_id)?; } - assert!(static_value.mutable); - static_value.mutable = false; + assert!(global_value.mutable); + global_value.mutable = false; } else { bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue); }, @@ -847,7 +847,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: GlobalKind::Global, }; - self.read_lvalue(Lvalue::Static(cid))? + self.read_lvalue(Lvalue::Global(cid))? } } @@ -857,7 +857,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), kind: GlobalKind::Promoted(index), }; - self.read_lvalue(Lvalue::Static(cid))? + self.read_lvalue(Lvalue::Global(cid))? } }; @@ -890,9 +890,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local } => { self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) } - Lvalue::Static(cid) => self.globals + Lvalue::Global(cid) => self.globals .get(&cid) - .expect("static not cached") + .expect("global not cached") .data .ok_or(EvalError::ReadUndefBytes), } @@ -917,7 +917,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: GlobalKind::Global, }; - Lvalue::Static(cid) + Lvalue::Global(cid) } Projection(ref proj) => return self.eval_lvalue_projection(proj), @@ -1122,22 +1122,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } Lvalue::Ptr { .. } => lvalue, - Lvalue::Static(cid) => { - let static_val = *self.globals.get(&cid).expect("static not cached"); - match static_val.data { + Lvalue::Global(cid) => { + let global_val = *self.globals.get(&cid).expect("global not cached"); + match global_val.data { Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), _ => { - let ptr = self.alloc_ptr_with_substs(static_val.ty, cid.substs)?; - if let Some(val) = static_val.data { - self.write_value_to_ptr(val, ptr, static_val.ty)?; + let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; + if let Some(val) = global_val.data { + self.write_value_to_ptr(val, ptr, global_val.ty)?; } - if !static_val.mutable { + if !global_val.mutable { self.memory.freeze(ptr.alloc_id)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { data: Some(Value::ByRef(ptr)), - .. static_val + .. global_val }; Lvalue::from_ptr(ptr) }, @@ -1222,10 +1222,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack[frame].set_local(local, Value::ByVal(val)); Ok(()) } - Lvalue::Static(cid) => { - let static_val = self.globals.get_mut(&cid).expect("static not cached"); - assert!(static_val.mutable); - static_val.data = Some(Value::ByVal(val)); + Lvalue::Global(cid) => { + let global_val = self.globals.get_mut(&cid).expect("global not cached"); + assert!(global_val.mutable); + global_val.data = Some(Value::ByVal(val)); Ok(()) } } @@ -1238,8 +1238,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match dest { - Lvalue::Static(cid) => { - let dest = *self.globals.get_mut(&cid).expect("static should be cached"); + Lvalue::Global(cid) => { + let dest = *self.globals.get_mut(&cid).expect("global should be cached"); assert!(dest.mutable); self.write_value_possibly_by_val( src_val, diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index f25eea875fc32..e745fe64d714a 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -135,7 +135,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Static(cid), cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -183,7 +183,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { constant.span, mir, this.substs, - Lvalue::Static(cid), + Lvalue::Global(cid), StackPopCleanup::Freeze) }); } From e82e6132ecb4c1c789a6e41e61aba551bb6003df Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:51:24 +0200 Subject: [PATCH 0593/1096] preemptively change some assertions into errors --- src/interpreter/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bcab8be53e9d0..66053e9dd59f7 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1224,9 +1224,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Lvalue::Global(cid) => { let global_val = self.globals.get_mut(&cid).expect("global not cached"); - assert!(global_val.mutable); - global_val.data = Some(Value::ByVal(val)); - Ok(()) + if global_val.mutable { + global_val.data = Some(Value::ByVal(val)); + Ok(()) + } else { + Err(EvalError::ModifiedConstantMemory) + } } } } @@ -1240,7 +1243,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Global(cid) => { let dest = *self.globals.get_mut(&cid).expect("global should be cached"); - assert!(dest.mutable); + if !dest.mutable { + return Err(EvalError::ModifiedConstantMemory); + } self.write_value_possibly_by_val( src_val, |this, val| *this.globals.get_mut(&cid).expect("already checked") = Global { data: Some(val), ..dest }, From 9d0b903d9db7baa07c3b1bac274e1b071005f44d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 11:54:38 +0200 Subject: [PATCH 0594/1096] remove GlobalKind --- src/interpreter/mod.rs | 16 +++++----------- src/interpreter/step.rs | 5 ++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 66053e9dd59f7..1cbfafd10b202 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -141,14 +141,8 @@ pub struct GlobalId<'tcx> { /// but that would only require more branching when working with constants, and not bring any /// real benefits. substs: &'tcx Substs<'tcx>, - kind: GlobalKind, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -enum GlobalKind { - Promoted(mir::Promoted), - /// Statics, constants and associated constants - Global, + /// this `Option` is `Some` for promoted constants + promoted: Option, } #[derive(Copy, Clone, Debug)] @@ -845,7 +839,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cid = GlobalId { def_id: def_id, substs: substs, - kind: GlobalKind::Global, + promoted: None, }; self.read_lvalue(Lvalue::Global(cid))? } @@ -855,7 +849,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cid = GlobalId { def_id: self.frame().def_id, substs: self.substs(), - kind: GlobalKind::Promoted(index), + promoted: Some(index), }; self.read_lvalue(Lvalue::Global(cid))? } @@ -915,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let cid = GlobalId { def_id: def_id, substs: substs, - kind: GlobalKind::Global, + promoted: None, }; Lvalue::Global(cid) } diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index e745fe64d714a..eac5494f43103 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -7,7 +7,6 @@ use super::{ GlobalId, EvalContext, Lvalue, - GlobalKind, StackPopCleanup, Global, }; @@ -122,7 +121,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { let cid = GlobalId { def_id: def_id, substs: substs, - kind: GlobalKind::Global, + promoted: None, }; if self.ecx.globals.contains_key(&cid) { return; @@ -170,7 +169,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let cid = GlobalId { def_id: self.def_id, substs: self.substs, - kind: GlobalKind::Promoted(index), + promoted: Some(index), }; if self.ecx.globals.contains_key(&cid) { return; From edc6b93b855ef342e0f3f5935eef60b3218a9b97 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 12:03:34 +0200 Subject: [PATCH 0595/1096] adjust some comments referencing locals --- src/interpreter/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1cbfafd10b202..ed5dc96458c1b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1274,17 +1274,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { if let Some(Value::ByRef(dest_ptr)) = old_dest_val { - // If the local value is already `ByRef` (that is, backed by an `Allocation`), + // If the value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be // other pointers into the allocation. These other pointers are logically // pointers into the local variable, and must be able to observe the change. // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we - // knew for certain that there were no outstanding pointers to this local. + // knew for certain that there were no outstanding pointers to this allocation. self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; } else if let Value::ByRef(src_ptr) = src_val { - // If the local value is not `ByRef`, then we know there are no pointers to it + // If the value is not `ByRef`, then we know there are no pointers to it // and we can simply overwrite the `Value` in the locals array directly. // // In this specific case, where the source value is `ByRef`, we must duplicate @@ -1301,7 +1301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { // Finally, we have the simple case where neither source nor destination are - // `ByRef`. We may simply copy the source value over the the destintion local. + // `ByRef`. We may simply copy the source value over the the destintion. write_dest(self, src_val); } Ok(()) From 512f344a3b924688903add51ddcc28ea29482a76 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 13:56:38 +0200 Subject: [PATCH 0596/1096] don't force allocate for Misc casts --- src/interpreter/mod.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5459ad2f03d71..790c73385b9e5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -652,13 +652,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Cast(kind, ref operand, cast_ty) => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); use rustc::mir::repr::CastKind::*; match kind { Unsize => { + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); self.unsize_into(src, src_ty, dest, dest_ty)?; @@ -669,32 +668,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_ty = self.operand_ty(operand); if self.type_is_fat_ptr(src_ty) { trace!("misc cast: {:?}", src); - let ptr_size = self.memory.pointer_size(); match (src, self.type_is_fat_ptr(dest_ty)) { - (Value::ByValPair(data, meta), true) => { - self.memory.write_primval(dest, data)?; - self.memory.write_primval(dest.offset(ptr_size as isize), meta)?; + (Value::ByRef(_), _) | + (Value::ByValPair(..), true) => { + self.write_value(src, dest, dest_ty)?; }, (Value::ByValPair(data, _), false) => { - self.memory.write_primval(dest, data)?; - }, - (Value::ByRef(ptr), true) => { - self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?; - }, - (Value::ByRef(ptr), false) => { - self.memory.copy(ptr, dest, ptr_size, ptr_size)?; + self.write_value(Value::ByVal(data), dest, dest_ty)?; }, (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { let src_val = self.value_to_primval(src, src_ty)?; let dest_val = self.cast_primval(src_val, dest_ty)?; - self.memory.write_primval(dest, dest_val)?; + self.write_value(Value::ByVal(dest_val), dest, dest_ty)?; } } ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); self.memory.write_ptr(dest, fn_ptr)?; }, @@ -703,6 +697,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; From eb08a2e64618faa3d4af5d1a687de1c3c13c2c6b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 14:54:37 +0200 Subject: [PATCH 0597/1096] don't force allocate for ReifyFnPointer casts --- src/interpreter/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 790c73385b9e5..96e0c00737d04 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -687,10 +687,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); - self.memory.write_ptr(dest, fn_ptr)?; + self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), }, From 9af5a0a420009d9b79709b6fbccdf2288a5517fb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 14:55:49 +0200 Subject: [PATCH 0598/1096] don't force allocate for UnsafeFnPointer casts --- src/interpreter/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 96e0c00737d04..902da52868ce1 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -695,13 +695,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); - self.memory.write_ptr(dest, fn_ptr)?; + self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), }, From 073f91654c5ac9d9a18e872ca340e9fd13d61d39 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 21 Oct 2016 15:18:12 +0200 Subject: [PATCH 0599/1096] don't force allocate for most Unsize casts only Arc -> Arc unsize casts are left --- src/interpreter/mod.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 902da52868ce1..c7e5e12794faa 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -656,8 +656,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::CastKind::*; match kind { Unsize => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); self.unsize_into(src, src_ty, dest, dest_ty)?; @@ -1484,7 +1482,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, src: Value, src_ty: Ty<'tcx>, - dest: Pointer, + dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match (&src_ty.sty, &dest_ty.sty) { @@ -1498,33 +1496,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - self.memory.write_ptr(dest, ptr)?; - let ptr_size = self.memory.pointer_size() as isize; - let dest_extra = dest.offset(ptr_size); - self.memory.write_usize(dest_extra, length as u64)?; + let len = self.usize_primval(length as u64); + let ptr = PrimVal::from_ptr(ptr); + self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } (&ty::TyTrait(_), &ty::TyTrait(_)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - self.write_value_to_ptr(src, dest, dest_ty)?; + self.write_value(src, dest, dest_ty)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - - self.memory.write_ptr(dest, ptr)?; - let ptr_size = self.memory.pointer_size() as isize; - let dest_extra = dest.offset(ptr_size); - self.memory.write_ptr(dest_extra, vtable)?; + let ptr = PrimVal::from_ptr(ptr); + let extra = PrimVal::from_ptr(vtable); + self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty)?; }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), } } (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => { + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); // unsizing of generic struct with pointer fields // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr @@ -1555,7 +1552,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr, src_fty)?; } else { - self.unsize_into(Value::ByRef(src_f_ptr), src_fty, dst_f_ptr, dst_fty)?; + self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } } } From 9ffc43e63926e38517a3dafe2b6c313ff8afbf78 Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Mon, 31 Oct 2016 16:37:54 +0000 Subject: [PATCH 0600/1096] README.md: Fix logging environment variable name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ef63b01bdfda..03949135b4b2b 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ cargo run tests/run-pass/vecs.rs # Or whatever test you like. ## Debugging -You can get detailed, statement-by-statement traces by setting the `MIRI_RUN` +You can get detailed, statement-by-statement traces by setting the `MIRI_LOG` environment variable to `trace`. These traces are indented based on call stack depth. You can get a much less verbose set of information with other logging levels such as `warn`. From 277a1ee86954481b711d991719c5a33d527ed83b Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 1 Nov 2016 23:26:04 +0100 Subject: [PATCH 0601/1096] rustup to rustc 1.14.0-nightly (3f4408347 2016-10-27) --- src/interpreter/mod.rs | 4 ++-- src/interpreter/step.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c7e5e12794faa..02f93900d483a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -895,7 +895,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Static(def_id) => { - let substs = subst::Substs::empty(self.tcx); + let substs = self.tcx.intern_substs(&[]); let cid = GlobalId { def_id: def_id, substs: substs, @@ -1655,7 +1655,7 @@ pub fn eval_main<'a, 'tcx: 'a>( def_id, mir.span, CachedMir::Ref(mir), - subst::Substs::empty(tcx), + tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None ).expect("could not allocate first stack frame"); diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index eac5494f43103..7a14a5d8da4ae 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -197,7 +197,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { ) { self.super_lvalue(lvalue, context, location); if let mir::Lvalue::Static(def_id) = *lvalue { - let substs = subst::Substs::empty(self.ecx.tcx); + let substs = self.ecx.tcx.intern_substs(&[]); let span = self.span; if let Some(node_item) = self.ecx.tcx.map.get_if_local(def_id) { if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { From 19c44dab056ededb388d8336e85341c00f2a2570 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 10:38:08 +0100 Subject: [PATCH 0602/1096] rustup to rustc 1.14.0-nightly (7c69b0d5a 2016-11-01) --- benches/helpers/miri_helper.rs | 1 - src/bin/miri.rs | 10 +-- src/error.rs | 2 +- src/interpreter/mod.rs | 106 +++++++---------------- src/interpreter/step.rs | 18 ++-- src/interpreter/terminator/intrinsics.rs | 2 +- src/interpreter/terminator/mod.rs | 6 +- src/lib.rs | 2 +- src/primval.rs | 8 +- 9 files changed, 54 insertions(+), 101 deletions(-) diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index cdd1412204f99..18c18eee6daf8 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -6,7 +6,6 @@ extern crate test; use self::miri::{eval_main, run_mir_passes}; use self::rustc::session::Session; -use self::rustc::mir::mir_map::MirMap; use self::rustc_driver::{driver, CompilerCalls, Compilation}; use std::cell::RefCell; use std::rc::Rc; diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 515b54e4bfd5f..d2a9bc6087ecc 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -11,7 +11,6 @@ extern crate syntax; use miri::{eval_main, run_mir_passes}; use rustc::session::Session; -use rustc::mir::mir_map::MirMap; use rustc_driver::{driver, CompilerCalls, Compilation}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; @@ -32,7 +31,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - let mir_map = state.mir_map.unwrap(); let (entry_node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); let entry_def_id = tcx.map.local_def_id(entry_node_id); @@ -70,12 +68,8 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { } } - let mut mir_map_copy = MirMap::new(tcx.dep_graph.clone()); - for def_id in mir_map.map.keys() { - mir_map_copy.map.insert(def_id, mir_map.map.get(&def_id).unwrap().clone()); - } - run_mir_passes(tcx, &mut mir_map_copy); - eval_main(tcx, &mir_map_copy, entry_def_id, memory_size, step_limit, stack_limit); + run_mir_passes(tcx); + eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); state.session.abort_if_errors(); }); diff --git a/src/error.rs b/src/error.rs index 44179eafabbab..a44b8cc76ebc4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ use std::error::Error; use std::fmt; -use rustc::mir::repr as mir; +use rustc::mir; use rustc::ty::BareFnTy; use memory::Pointer; use rustc_const_math::ConstMathErr; diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 02f93900d483a..b72feba8bc059 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,17 +1,12 @@ use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; -use rustc::mir::mir_map::MirMap; -use rustc::mir::repr as mir; +use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; -use std::cell::RefCell; -use std::ops::Deref; -use std::rc::Rc; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -20,6 +15,7 @@ use primval::{self, PrimVal, PrimValKind}; pub use self::value::Value; use std::collections::HashMap; +use std::cell::Ref; mod step; mod terminator; @@ -27,16 +23,12 @@ mod cast; mod vtable; mod value; +pub type MirRef<'tcx> = ::std::cell::Ref<'tcx, mir::Mir<'tcx>>; + pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, - /// A mapping from NodeIds to Mir, from rustc. Only contains MIR for crate-local items. - mir_map: &'a MirMap<'tcx>, - - /// A local cache from DefIds to Mir for non-crate-local items. - mir_cache: RefCell>>>, - /// The virtual memory system. memory: Memory<'a, 'tcx>, @@ -44,20 +36,20 @@ pub struct EvalContext<'a, 'tcx: 'a> { globals: HashMap, Global<'tcx>>, /// The virtual call stack. - stack: Vec>, + stack: Vec>, /// The maximum number of stack frames allowed stack_limit: usize, } /// A stack frame. -pub struct Frame<'a, 'tcx: 'a> { +pub struct Frame<'tcx> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. - pub mir: CachedMir<'a, 'tcx>, + pub mir: MirRef<'tcx>, /// The def_id of the current function. pub def_id: DefId, @@ -125,12 +117,6 @@ pub enum LvalueExtra { DowncastVariant(usize), } -#[derive(Clone)] -pub enum CachedMir<'mir, 'tcx: 'mir> { - Ref(&'mir mir::Mir<'tcx>), - Owned(Rc>) -} - #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] /// Uniquely identifies a specific constant or static pub struct GlobalId<'tcx> { @@ -176,11 +162,9 @@ pub enum StackPopCleanup { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: usize, stack_limit: usize) -> Self { EvalContext { tcx: tcx, - mir_map: mir_map, - mir_cache: RefCell::new(DefIdMap()), memory: Memory::new(&tcx.data_layout, memory_size), globals: HashMap::new(), stack: Vec::new(), @@ -211,7 +195,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self.memory } - pub fn stack(&self) -> &[Frame<'a, 'tcx>] { + pub fn stack(&self) -> &[Frame<'tcx>] { &self.stack } @@ -292,25 +276,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, CachedMir<'a, 'tcx>> { + pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, MirRef<'tcx>> { trace!("load mir {:?}", def_id); - if def_id.is_local() { - Ok(CachedMir::Ref(self.mir_map.map.get(&def_id).unwrap())) + if def_id.is_local() || self.tcx.sess.cstore.is_item_mir_available(def_id) { + Ok(self.tcx.item_mir(def_id)) } else { - let mut mir_cache = self.mir_cache.borrow_mut(); - if let Some(mir) = mir_cache.get(&def_id) { - return Ok(CachedMir::Owned(mir.clone())); - } - - let cs = &self.tcx.sess.cstore; - match cs.maybe_get_item_mir(self.tcx, def_id) { - Some(mir) => { - let cached = Rc::new(mir); - mir_cache.insert(def_id, cached.clone()); - Ok(CachedMir::Owned(cached)) - }, - None => Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id))), - } + Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id))) } } @@ -358,7 +329,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, def_id: DefId, span: codemap::Span, - mir: CachedMir<'a, 'tcx>, + mir: MirRef<'tcx>, substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, @@ -371,7 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let locals = vec![None; num_locals]; self.stack.push(Frame { - mir: mir.clone(), + mir: mir, block: mir::START_BLOCK, return_to_block: return_to_block, return_lvalue: return_lvalue, @@ -483,7 +454,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty); - use rustc::mir::repr::Rvalue::*; + use rustc::mir::Rvalue::*; match *rvalue { Use(ref operand) => { let value = self.eval_operand(operand)?; @@ -653,7 +624,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Cast(kind, ref operand, cast_ty) => { debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); - use rustc::mir::repr::CastKind::*; + use rustc::mir::CastKind::*; match kind { Unsize => { let src = self.eval_operand(operand)?; @@ -812,12 +783,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { - use rustc::mir::repr::Operand::*; + use rustc::mir::Operand::*; match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), Constant(mir::Constant { ref literal, ty, .. }) => { - use rustc::mir::repr::Literal; + use rustc::mir::Literal; let value = match *literal { Literal::Value { ref value } => self.const_to_value(value)?, @@ -883,7 +854,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { - use rustc::mir::repr::Lvalue::*; + use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, @@ -922,7 +893,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); - use rustc::mir::repr::ProjectionElem::*; + use rustc::mir::ProjectionElem::*; let (ptr, extra) = match proj.elem { Field(field, field_ty) => { // FIXME(solson) @@ -1462,16 +1433,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(val)) } - fn frame(&self) -> &Frame<'a, 'tcx> { + fn frame(&self) -> &Frame<'tcx> { self.stack.last().expect("no call frames exist") } - pub fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { + pub fn frame_mut(&mut self) -> &mut Frame<'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn mir(&self) -> CachedMir<'a, 'tcx> { - self.frame().mir.clone() + fn mir(&self) -> MirRef<'tcx> { + Ref::clone(&self.frame().mir) } fn substs(&self) -> &'tcx Substs<'tcx> { @@ -1583,7 +1554,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } -impl<'a, 'tcx: 'a> Frame<'a, 'tcx> { +impl<'tcx> Frame<'tcx> { pub fn get_local(&self, local: mir::Local) -> Option { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1] @@ -1630,34 +1601,23 @@ impl<'tcx> Lvalue<'tcx> { } } -impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { - type Target = mir::Mir<'tcx>; - fn deref(&self) -> &mir::Mir<'tcx> { - match *self { - CachedMir::Ref(r) => r, - CachedMir::Owned(ref rc) => rc, - } - } -} - pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir_map: &'a MirMap<'tcx>, def_id: DefId, memory_size: usize, step_limit: u64, stack_limit: usize, ) { - let mir = mir_map.map.get(&def_id).expect("no mir for main function"); - let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit); + let mut ecx = EvalContext::new(tcx, memory_size, stack_limit); + let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); ecx.push_stack_frame( def_id, mir.span, - CachedMir::Ref(mir), + mir, tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None + StackPopCleanup::None, ).expect("could not allocate first stack frame"); for _ in 0..step_limit { @@ -1695,7 +1655,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { impl<'tcx> ::std::panic::RefUnwindSafe for Instance<'tcx> {} impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[]) + ppaux::parameterized(f, self.1, self.0, &[]) } } err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); @@ -1703,7 +1663,7 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { err.emit(); } -pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &mut MirMap<'tcx>) { +pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut passes = ::rustc::mir::transform::Passes::new(); passes.push_hook(Box::new(::rustc_mir::transform::dump_mir::DumpMir)); passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); @@ -1716,7 +1676,7 @@ pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &mut MirMa passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"))); passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); - passes.run_passes(tcx, mir_map); + passes.run_passes(tcx); } // TODO(solson): Upstream these methods into rustc::ty::layout. diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 7a14a5d8da4ae..4c03b3c42aa1d 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -3,21 +3,21 @@ //! The main entry point is the `step` method. use super::{ - CachedMir, GlobalId, EvalContext, Lvalue, StackPopCleanup, Global, + MirRef, }; use error::EvalResult; -use rustc::mir::repr as mir; +use rustc::mir; use rustc::ty::{subst, self}; use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; +use std::cell::Ref; use syntax::codemap::Span; -use std::rc::Rc; impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. @@ -38,7 +38,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), def_id: self.frame().def_id, ecx: self, - mir: &mir, + mir: Ref::clone(&mir), new_constants: &mut new, }.visit_statement(block, stmt, mir::Location { block: block, @@ -59,7 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), def_id: self.frame().def_id, ecx: self, - mir: &mir, + mir: Ref::clone(&mir), new_constants: &mut new, }.visit_terminator(block, terminator, mir::Location { block: block, @@ -76,7 +76,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { trace!("{:?}", stmt); - use rustc::mir::repr::StatementKind::*; + use rustc::mir::StatementKind::*; match stmt.kind { Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, SetDiscriminant { .. } => unimplemented!(), @@ -110,7 +110,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, ecx: &'a mut EvalContext<'b, 'tcx>, - mir: &'a mir::Mir<'tcx>, + mir: MirRef<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, new_constants: &'a mut EvalResult<'tcx, u64>, @@ -165,7 +165,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } }, mir::Literal::Promoted { index } => { - let mir = self.mir.promoted[index].clone(); let cid = GlobalId { def_id: self.def_id, substs: self.substs, @@ -174,8 +173,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if self.ecx.globals.contains_key(&cid) { return; } + let mir = Ref::clone(&self.mir); + let mir = Ref::map(mir, |mir| &mir.promoted[index]); self.try(|this| { - let mir = CachedMir::Owned(Rc::new(mir)); let ty = this.ecx.monomorphize(mir.return_ty, this.substs); this.ecx.globals.insert(cid, Global::uninitialized(ty)); this.ecx.push_stack_frame(this.def_id, diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 5f8886c5c1f03..48098e4f497d0 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -1,5 +1,5 @@ use rustc::hir::def_id::DefId; -use rustc::mir::repr as mir; +use rustc::mir; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index f3edc5182500c..823caae623e0b 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -1,5 +1,5 @@ use rustc::hir::def_id::DefId; -use rustc::mir::repr as mir; +use rustc::mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; @@ -28,7 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, terminator: &mir::Terminator<'tcx>, ) -> EvalResult<'tcx, ()> { - use rustc::mir::repr::TerminatorKind::*; + use rustc::mir::TerminatorKind::*; match terminator.kind { Return => self.pop_stack_frame()?, @@ -204,7 +204,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir, resolved_substs, return_lvalue, - return_to_block + return_to_block, )?; let arg_locals = self.frame().mir.args_iter(); diff --git a/src/lib.rs b/src/lib.rs index 3ed75a54c2a51..d957e947e386e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ collections_bound, rustc_private, pub_restricted, + cell_extras, )] // From rustc. @@ -30,7 +31,6 @@ pub use error::{ }; pub use interpreter::{ - CachedMir, EvalContext, Frame, eval_main, diff --git a/src/primval.rs b/src/primval.rs index 597bee6b402de..74546626d6ec1 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -3,7 +3,7 @@ use std::mem::transmute; -use rustc::mir::repr as mir; +use rustc::mir; use error::{EvalError, EvalResult}; use memory::{AllocId, Pointer}; @@ -274,7 +274,7 @@ pub fn binary_op<'tcx>( left: PrimVal, right: PrimVal ) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::repr::BinOp::*; + use rustc::mir::BinOp::*; use self::PrimValKind::*; match (left.try_as_ptr(), right.try_as_ptr()) { @@ -377,7 +377,7 @@ pub fn binary_op<'tcx>( } fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::repr::BinOp::*; + use rustc::mir::BinOp::*; match bin_op { Eq => Ok(PrimVal::from_bool(false)), Ne => Ok(PrimVal::from_bool(true)), @@ -387,7 +387,7 @@ fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { } pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::repr::UnOp::*; + use rustc::mir::UnOp::*; use self::PrimValKind::*; let bits = match (un_op, val.kind) { From b3bf730513aaab6882d4d223f971ec0304c9e18a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 12:30:41 +0100 Subject: [PATCH 0603/1096] don't panic on invalid primval types, report an error instead --- src/error.rs | 7 ++++++- src/interpreter/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index a44b8cc76ebc4..86f22d33a257e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::BareFnTy; +use rustc::ty::{BareFnTy, Ty}; use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -46,6 +46,7 @@ pub enum EvalError<'tcx> { ModifiedConstantMemory, AssumptionNotHeld, InlineAsm, + TypeNotPrimitive(Ty<'tcx>), } pub type EvalResult<'tcx, T> = Result>; @@ -106,6 +107,8 @@ impl<'tcx> Error for EvalError<'tcx> { "`assume` argument was false", EvalError::InlineAsm => "cannot evaluate inline assembly", + EvalError::TypeNotPrimitive(_) => + "expected primitive type, got nonprimitive", } } @@ -134,6 +137,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", has, required), + EvalError::TypeNotPrimitive(ref ty) => + write!(f, "expected primitive type, got {}", ty), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b72feba8bc059..6454c7641274e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1325,11 +1325,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimValKind::from_uint_size(size) } } else { - bug!("primitive read of non-clike enum: {:?}", ty); + return Err(EvalError::TypeNotPrimitive(ty)); } }, - _ => bug!("primitive read of non-primitive type: {:?}", ty), + _ => return Err(EvalError::TypeNotPrimitive(ty)), }; Ok(kind) From bf73e7581e96b144ade8bccc16c370c392a4fa5c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 12:31:04 +0100 Subject: [PATCH 0604/1096] don't always allocate during `init` intrinsic processing --- src/interpreter/terminator/intrinsics.rs | 53 +++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 48098e4f497d0..2b10f82503852 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -6,7 +6,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use interpreter::value::Value; -use interpreter::{EvalContext, Lvalue}; +use interpreter::{EvalContext, Lvalue, LvalueExtra}; use primval::{self, PrimVal, PrimValKind}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -126,11 +126,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => {} "init" => { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - self.memory.write_repeat(dest, 0, size)?; + match dest { + Lvalue::Local { frame, local } => { + match self.stack[frame].get_local(local) { + Some(Value::ByRef(ptr)) => self.memory.write_repeat(ptr, 0, size)?, + None => match self.ty_to_primval_kind(dest_ty) { + Ok(kind) => self.stack[frame].set_local(local, Value::ByVal(PrimVal::new(0, kind))), + Err(_) => { + let ptr = self.alloc_ptr_with_substs(dest_ty, substs)?; + self.memory.write_repeat(ptr, 0, size)?; + self.stack[frame].set_local(local, Value::ByRef(ptr)); + } + }, + Some(Value::ByVal(val)) => self.stack[frame].set_local(local, Value::ByVal(PrimVal::new(0, val.kind))), + Some(Value::ByValPair(a, b)) => self.stack[frame].set_local(local, Value::ByValPair( + PrimVal::new(0, a.kind), + PrimVal::new(0, b.kind), + )), + } + } + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, + Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), + Lvalue::Global(cid) => { + let global_val = *self.globals.get(&cid).expect("global not cached"); + if !global_val.mutable { + return Err(EvalError::ModifiedConstantMemory); + } + match global_val.data { + Some(Value::ByRef(ptr)) => self.memory.write_repeat(ptr, 0, size)?, + None => match self.ty_to_primval_kind(dest_ty) { + Ok(kind) => self.globals + .get_mut(&cid) + .expect("already checked") + .data = Some(Value::ByVal(PrimVal::new(0, kind))), + Err(_) => { + let ptr = self.alloc_ptr_with_substs(dest_ty, substs)?; + self.memory.write_repeat(ptr, 0, size)?; + self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByRef(ptr)); + }, + }, + Some(Value::ByVal(val)) => self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByVal(PrimVal::new(0, val.kind))), + Some(Value::ByValPair(a, b)) => self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByValPair( + PrimVal::new(0, a.kind), + PrimVal::new(0, b.kind), + )), + } + } + } } "min_align_of" => { From 92f6874ead00ac6ea12c46ca555796c67db56c70 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 12:52:13 +0100 Subject: [PATCH 0605/1096] enable code sharing between global and local access --- src/interpreter/mod.rs | 32 ++++++++++++ src/interpreter/terminator/intrinsics.rs | 66 +++++++++--------------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6454c7641274e..bb5f944fa36cf 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1552,6 +1552,38 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } + + /// convenience function to ensure correct usage of globals and code-sharing with locals + pub fn modify_global< + F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Value>, + >( + &mut self, + cid: GlobalId<'tcx>, + f: F, + ) -> EvalResult<'tcx, ()> { + let mut val = *self.globals.get(&cid).expect("global not cached"); + if !val.mutable { + return Err(EvalError::ModifiedConstantMemory); + } + val.data = Some(f(self, val.data)?); + *self.globals.get_mut(&cid).expect("already checked") = val; + Ok(()) + } + + /// convenience function to ensure correct usage of locals and code-sharing with globals + pub fn modify_local< + F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Value>, + >( + &mut self, + frame: usize, + local: mir::Local, + f: F, + ) -> EvalResult<'tcx, ()> { + let val = self.stack[frame].get_local(local); + let val = f(self, val)?; + self.stack[frame].set_local(local, val); + Ok(()) + } } impl<'tcx> Frame<'tcx> { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 2b10f82503852..dfa4a427e9054 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -127,52 +127,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => { let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - match dest { - Lvalue::Local { frame, local } => { - match self.stack[frame].get_local(local) { - Some(Value::ByRef(ptr)) => self.memory.write_repeat(ptr, 0, size)?, - None => match self.ty_to_primval_kind(dest_ty) { - Ok(kind) => self.stack[frame].set_local(local, Value::ByVal(PrimVal::new(0, kind))), - Err(_) => { - let ptr = self.alloc_ptr_with_substs(dest_ty, substs)?; - self.memory.write_repeat(ptr, 0, size)?; - self.stack[frame].set_local(local, Value::ByRef(ptr)); - } - }, - Some(Value::ByVal(val)) => self.stack[frame].set_local(local, Value::ByVal(PrimVal::new(0, val.kind))), - Some(Value::ByValPair(a, b)) => self.stack[frame].set_local(local, Value::ByValPair( - PrimVal::new(0, a.kind), - PrimVal::new(0, b.kind), - )), - } + let init = |this: &mut Self, val: Option| { + match val { + Some(Value::ByRef(ptr)) => { + this.memory.write_repeat(ptr, 0, size)?; + Ok(Value::ByRef(ptr)) + }, + None => match this.ty_to_primval_kind(dest_ty) { + Ok(kind) => Ok(Value::ByVal(PrimVal::new(0, kind))), + Err(_) => { + let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; + this.memory.write_repeat(ptr, 0, size)?; + Ok(Value::ByRef(ptr)) + } + }, + Some(Value::ByVal(value)) => Ok(Value::ByVal(PrimVal::new(0, value.kind))), + Some(Value::ByValPair(a, b)) => Ok(Value::ByValPair( + PrimVal::new(0, a.kind), + PrimVal::new(0, b.kind), + )), } + }; + match dest { + Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), - Lvalue::Global(cid) => { - let global_val = *self.globals.get(&cid).expect("global not cached"); - if !global_val.mutable { - return Err(EvalError::ModifiedConstantMemory); - } - match global_val.data { - Some(Value::ByRef(ptr)) => self.memory.write_repeat(ptr, 0, size)?, - None => match self.ty_to_primval_kind(dest_ty) { - Ok(kind) => self.globals - .get_mut(&cid) - .expect("already checked") - .data = Some(Value::ByVal(PrimVal::new(0, kind))), - Err(_) => { - let ptr = self.alloc_ptr_with_substs(dest_ty, substs)?; - self.memory.write_repeat(ptr, 0, size)?; - self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByRef(ptr)); - }, - }, - Some(Value::ByVal(val)) => self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByVal(PrimVal::new(0, val.kind))), - Some(Value::ByValPair(a, b)) => self.globals.get_mut(&cid).expect("already checked").data = Some(Value::ByValPair( - PrimVal::new(0, a.kind), - PrimVal::new(0, b.kind), - )), - } - } + Lvalue::Global(cid) => self.modify_global(cid, init)?, } } From a9b984d21eec97c95d94c72bfd3f386e55f37b95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 13:13:47 +0100 Subject: [PATCH 0606/1096] don't always allocate for the `uninit` intrinsic --- src/interpreter/mod.rs | 9 ++++--- src/interpreter/terminator/intrinsics.rs | 33 ++++++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bb5f944fa36cf..0cd7190db9919 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1555,7 +1555,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// convenience function to ensure correct usage of globals and code-sharing with locals pub fn modify_global< - F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Value>, + F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Option>, >( &mut self, cid: GlobalId<'tcx>, @@ -1565,14 +1565,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !val.mutable { return Err(EvalError::ModifiedConstantMemory); } - val.data = Some(f(self, val.data)?); + val.data = f(self, val.data)?; *self.globals.get_mut(&cid).expect("already checked") = val; Ok(()) } /// convenience function to ensure correct usage of locals and code-sharing with globals pub fn modify_local< - F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Value>, + F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Option>, >( &mut self, frame: usize, @@ -1581,7 +1581,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { let val = self.stack[frame].get_local(local); let val = f(self, val)?; - self.stack[frame].set_local(local, val); + // can't use `set_local` here, because that's only meant for going to an initialized value + self.stack[frame].locals[local.index() - 1] = val; Ok(()) } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index dfa4a427e9054..3e13d2a85dbd9 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -131,21 +131,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match val { Some(Value::ByRef(ptr)) => { this.memory.write_repeat(ptr, 0, size)?; - Ok(Value::ByRef(ptr)) + Ok(Some(Value::ByRef(ptr))) }, None => match this.ty_to_primval_kind(dest_ty) { - Ok(kind) => Ok(Value::ByVal(PrimVal::new(0, kind))), + Ok(kind) => Ok(Some(Value::ByVal(PrimVal::new(0, kind)))), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; this.memory.write_repeat(ptr, 0, size)?; - Ok(Value::ByRef(ptr)) + Ok(Some(Value::ByRef(ptr))) } }, - Some(Value::ByVal(value)) => Ok(Value::ByVal(PrimVal::new(0, value.kind))), - Some(Value::ByValPair(a, b)) => Ok(Value::ByValPair( + Some(Value::ByVal(value)) => Ok(Some(Value::ByVal(PrimVal::new(0, value.kind)))), + Some(Value::ByValPair(a, b)) => Ok(Some(Value::ByValPair( PrimVal::new(0, a.kind), PrimVal::new(0, b.kind), - )), + ))), } }; match dest { @@ -271,12 +271,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "uninit" => { - // FIXME(solson): Attempt writing a None over the destination when it's an - // Lvalue::Local (that is not ByRef). Otherwise do the mark_definedness as usual. - let dest = self.force_allocation(dest)?.to_ptr(); - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; - self.memory.mark_definedness(dest, size, false)?; + let uninit = |this: &mut Self, val: Option| { + match val { + Some(Value::ByRef(ptr)) => { + this.memory.mark_definedness(ptr, size, false)?; + Ok(Some(Value::ByRef(ptr))) + }, + None => Ok(None), + Some(_) => Ok(None), + } + }; + match dest { + Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, + Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), + Lvalue::Global(cid) => self.modify_global(cid, uninit)?, + } } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), From a1acd9405dbe2a3cd6a477a349c38513ddbdde4e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 13:27:35 +0100 Subject: [PATCH 0607/1096] don't allocate on `drop` calls --- src/interpreter/terminator/mod.rs | 11 ++++------- src/interpreter/value.rs | 7 ++----- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 823caae623e0b..38768483d1d87 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -104,13 +104,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Drop { ref location, target, .. } => { - // FIXME(solson) - let lvalue = self.eval_lvalue(location)?; - let lvalue = self.force_allocation(lvalue)?; + let val = self.eval_and_read_lvalue(location)?; - let ptr = lvalue.to_ptr(); let ty = self.lvalue_ty(location); - self.drop(ptr, ty)?; + self.drop(val, ty)?; self.goto_block(target); } @@ -471,7 +468,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) } - fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn drop(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); return Ok(()); @@ -482,7 +479,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { ty::TyBox(_contents_ty) => { - let contents_ptr = self.memory.read_ptr(ptr)?; + let contents_ptr = val.read_ptr(&self.memory)?; // self.drop(contents_ptr, contents_ty)?; trace!("-deallocating box"); self.memory.deallocate(contents_ptr)?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 22698f978184b..d9285a41e41ab 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -23,12 +23,9 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ptr) => mem.read_ptr(ptr), - ByVal(ptr) if ptr.try_as_ptr().is_some() => { - Ok(ptr.try_as_ptr().unwrap()) + ByVal(ptr) | ByValPair(ptr, _) => { + Ok(ptr.try_as_ptr().expect("unimplemented: `read_ptr` on non-ptr primval")) } - - ByValPair(..) => unimplemented!(), - ByVal(_other) => unimplemented!(), } } From feefb66ebd967e0c9e81e01a814351ab3bc07495 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 13:30:54 +0100 Subject: [PATCH 0608/1096] recursively drop Box...>> --- src/interpreter/terminator/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 38768483d1d87..aad6238fdf8ce 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -478,9 +478,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Call user-defined Drop::drop impls. match ty.sty { - ty::TyBox(_contents_ty) => { + ty::TyBox(contents_ty) => { let contents_ptr = val.read_ptr(&self.memory)?; - // self.drop(contents_ptr, contents_ty)?; + self.drop(Value::ByRef(contents_ptr), contents_ty)?; trace!("-deallocating box"); self.memory.deallocate(contents_ptr)?; } From b90cc77bac0bb2577568fb12d340c0ea7e787d1d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 15:22:39 +0100 Subject: [PATCH 0609/1096] basic struct and tuple drop "glue" --- src/interpreter/terminator/mod.rs | 50 +++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index aad6238fdf8ce..1b045417f4c2e 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -104,10 +104,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Drop { ref location, target, .. } => { - let val = self.eval_and_read_lvalue(location)?; + let lval = self.eval_lvalue(location)?; let ty = self.lvalue_ty(location); - self.drop(val, ty)?; + self.drop(lval, ty)?; self.goto_block(target); } @@ -468,25 +468,49 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) } - fn drop(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn drop(&mut self, lval: Lvalue<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); return Ok(()); } - trace!("-need to drop {:?}", ty); + trace!("-need to drop {:?} at {:?}", ty, lval); // TODO(solson): Call user-defined Drop::drop impls. + // special case `Box` to deallocate the inner allocation + // FIXME: if user defined Drop impls work, then this can go away, since the stdlib calls + // heap::deallocate + if let ty::TyBox(contents_ty) = ty.sty { + let val = self.read_lvalue(lval)?; + let contents_ptr = val.read_ptr(&self.memory)?; + self.drop(Lvalue::from_ptr(contents_ptr), contents_ty)?; + trace!("-deallocating box"); + return self.memory.deallocate(contents_ptr); + } match ty.sty { - ty::TyBox(contents_ty) => { - let contents_ptr = val.read_ptr(&self.memory)?; - self.drop(Value::ByRef(contents_ptr), contents_ty)?; - trace!("-deallocating box"); - self.memory.deallocate(contents_ptr)?; - } - - // TODO(solson): Implement drop for other relevant types (e.g. aggregates). - _ => {} + ty::TyAdt(adt_def, substs) => { + // FIXME: some structs are represented as ByValPair + let ptr = self.force_allocation(lval)?.to_ptr(); + if adt_def.is_univariant() { + for (i, field_ty) in adt_def.struct_variant().fields.iter().enumerate() { + let field_ty = self.monomorphize_field_ty(field_ty, substs); + let offset = self.get_field_offset(ty, i)?.bytes() as isize; + self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty)?; + } + } else { + unimplemented!() + } + }, + ty::TyTuple(fields) => { + // FIXME: some tuples are represented as ByValPair + let ptr = self.force_allocation(lval)?.to_ptr(); + for (i, field_ty) in fields.iter().enumerate() { + let offset = self.get_field_offset(ty, i)?.bytes() as isize; + self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty)?; + } + }, + // other types do not need to process drop + _ => {}, } Ok(()) From 50fd0765b5e6150b026ed590a3b4771421751d2b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 15:55:09 +0100 Subject: [PATCH 0610/1096] call drop "glue" for enums --- src/interpreter/terminator/mod.rs | 37 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 1b045417f4c2e..4e6be7fdec92b 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -490,15 +490,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair - let ptr = self.force_allocation(lval)?.to_ptr(); - if adt_def.is_univariant() { - for (i, field_ty) in adt_def.struct_variant().fields.iter().enumerate() { - let field_ty = self.monomorphize_field_ty(field_ty, substs); - let offset = self.get_field_offset(ty, i)?.bytes() as isize; - self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty)?; - } - } else { - unimplemented!() + let adt_ptr = self.force_allocation(lval)?.to_ptr(); + let layout = self.type_layout(ty); + let fields = match *layout { + Layout::Univariant { ref variant, .. } => { + adt_def.struct_variant().fields.iter().zip(&variant.offsets) + }, + Layout::General { ref variants, .. } => { + let discr_val = self.read_discriminant_value(adt_ptr, ty)?; + match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u64_unchecked()) { + // start at offset 1, to skip over the discriminant + Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]), + None => return Err(EvalError::InvalidDiscriminant), + } + }, + Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { + let discr = self.read_discriminant_value(adt_ptr, ty)?; + if discr == nndiscr { + adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) + } else { + // FIXME: the zst variant might contain zst types that impl Drop + return Ok(()); // nothing to do, this is zero sized (e.g. `None`) + } + }, + _ => bug!("{:?} is not an adt layout", layout), + }; + for (field_ty, offset) in fields { + let field_ty = self.monomorphize_field_ty(field_ty, substs); + self.drop(Lvalue::from_ptr(adt_ptr.offset(offset.bytes() as isize)), field_ty)?; } }, ty::TyTuple(fields) => { From e4060993483899c2ac75c186589d950d402c3ab8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 16:01:14 +0100 Subject: [PATCH 0611/1096] sanity check that boxes of zsts don't deallocate the zst allocation --- tests/run-pass/zst_box.rs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/run-pass/zst_box.rs diff --git a/tests/run-pass/zst_box.rs b/tests/run-pass/zst_box.rs new file mode 100644 index 0000000000000..12138be5af976 --- /dev/null +++ b/tests/run-pass/zst_box.rs @@ -0,0 +1,8 @@ +fn main() { + let x = Box::new(()); + let y = Box::new(()); + drop(y); + let z = Box::new(()); + drop(x); + drop(z); +} From 1e0d5b817dc21242178c10793b6d4803cb153411 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 17:32:06 +0100 Subject: [PATCH 0612/1096] implement a bunch of intrinsics --- src/interpreter/terminator/intrinsics.rs | 37 ++++++++++++++++++++++++ src/interpreter/terminator/mod.rs | 10 +++++++ 2 files changed, 47 insertions(+) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 3e13d2a85dbd9..71fb83d580e48 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -69,6 +69,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(arg_vals[1], dest, ty)?; } + "atomic_fence_acq" => { + // we are inherently singlethreaded and singlecored, this is a nop + } + + "atomic_xsub_rel" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let change = self.value_to_primval(arg_vals[1], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_xsub_rel doesn't work with nonprimitives"), + }; + self.write_primval(dest, old)?; + // FIXME: what do atomics do on overflow? + let (val, _) = primval::binary_op(mir::BinOp::Sub, old, change)?; + self.write_primval(Lvalue::from_ptr(ptr), val)?; + } + "breakpoint" => unimplemented!(), // halt miri "copy" | @@ -101,6 +121,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?; } + "drop_in_place" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let mut drops = Vec::new(); + self.drop(Lvalue::from_ptr(ptr), ty, &mut drops)?; + self.eval_drop_impls(drops)?; + } + "fabsf32" => { let f = self.value_to_primval(arg_vals[2], f32)? .expect_f32("fabsf32 read non f32"); @@ -248,6 +276,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size_val = self.usize_primval(size); self.write_primval(dest, size_val)?; } + + "min_align_of_val" | + "align_of_val" => { + let ty = substs.type_at(0); + let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; + let align_val = self.usize_primval(align); + self.write_primval(dest, align_val)?; + } + "type_name" => { let ty = substs.type_at(0); let ty_name = ty.to_string(); diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 4e6be7fdec92b..9078554f5d44a 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -291,6 +291,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::from_ptr(ptr))?; } + "__rust_deallocate" => { + let ptr = args[0].read_ptr(&self.memory)?; + // FIXME: insert sanity check for size and align? + let _old_size = self.value_to_primval(args[1], usize)? + .expect_uint("__rust_deallocate second arg not usize"); + let _align = self.value_to_primval(args[2], usize)? + .expect_uint("__rust_deallocate third arg not usize"); + self.memory.deallocate(ptr)?; + }, + "__rust_reallocate" => { let ptr = args[0].read_ptr(&self.memory)?; let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); From 2a35b3e3224a28d0c4e95214676e625e15869f59 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Nov 2016 17:32:37 +0100 Subject: [PATCH 0613/1096] call user defined drop impls --- src/interpreter/terminator/mod.rs | 55 +++++++++++++++++++++----- tests/{compile-fail => run-pass}/rc.rs | 2 - 2 files changed, 46 insertions(+), 11 deletions(-) rename tests/{compile-fail => run-pass}/rc.rs (72%) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 9078554f5d44a..c94ea97b056b0 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -107,8 +107,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let lval = self.eval_lvalue(location)?; let ty = self.lvalue_ty(location); - self.drop(lval, ty)?; + + // we can't generate the drop stack frames on the fly, + // because that would change our call stack + // and very much confuse the further processing of the drop glue + let mut drops = Vec::new(); + self.drop(lval, ty, &mut drops)?; self.goto_block(target); + self.eval_drop_impls(drops)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -140,6 +146,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + fn eval_drop_impls(&mut self, drops: Vec<(DefId, Pointer, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { + let span = self.frame().span; + for (drop_def_id, adt_ptr, substs) in drops { + // FIXME: supply a real span + let mir = self.load_mir(drop_def_id)?; + trace!("substs for drop glue: {:?}", substs); + self.push_stack_frame( + drop_def_id, + span, + mir, + substs, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + let mut arg_locals = self.frame().mir.args_iter(); + let first = arg_locals.next().expect("drop impl has self arg"); + assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; + let ty = self.frame().mir.local_decls[first].ty; + self.write_value(Value::ByVal(PrimVal::from_ptr(adt_ptr)), dest, ty)?; + } + Ok(()) + } + fn eval_fn_call( &mut self, def_id: DefId, @@ -478,29 +508,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) } - fn drop(&mut self, lval: Lvalue<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + /// push DefIds of drop impls and their argument on the given vector + fn drop( + &mut self, + lval: Lvalue<'tcx>, + ty: Ty<'tcx>, + drop: &mut Vec<(DefId, Pointer, &'tcx Substs<'tcx>)>, + ) -> EvalResult<'tcx, ()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); return Ok(()); } trace!("-need to drop {:?} at {:?}", ty, lval); - // TODO(solson): Call user-defined Drop::drop impls. - // special case `Box` to deallocate the inner allocation - // FIXME: if user defined Drop impls work, then this can go away, since the stdlib calls - // heap::deallocate if let ty::TyBox(contents_ty) = ty.sty { let val = self.read_lvalue(lval)?; let contents_ptr = val.read_ptr(&self.memory)?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty)?; + self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; trace!("-deallocating box"); return self.memory.deallocate(contents_ptr); } + match ty.sty { ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair let adt_ptr = self.force_allocation(lval)?.to_ptr(); + // run drop impl before the fields' drop impls + if let Some(drop_def_id) = adt_def.destructor() { + drop.push((drop_def_id, adt_ptr, substs)); + } let layout = self.type_layout(ty); let fields = match *layout { Layout::Univariant { ref variant, .. } => { @@ -527,7 +564,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; for (field_ty, offset) in fields { let field_ty = self.monomorphize_field_ty(field_ty, substs); - self.drop(Lvalue::from_ptr(adt_ptr.offset(offset.bytes() as isize)), field_ty)?; + self.drop(Lvalue::from_ptr(adt_ptr.offset(offset.bytes() as isize)), field_ty, drop)?; } }, ty::TyTuple(fields) => { @@ -535,7 +572,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.force_allocation(lval)?.to_ptr(); for (i, field_ty) in fields.iter().enumerate() { let offset = self.get_field_offset(ty, i)?.bytes() as isize; - self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty)?; + self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty, drop)?; } }, // other types do not need to process drop diff --git a/tests/compile-fail/rc.rs b/tests/run-pass/rc.rs similarity index 72% rename from tests/compile-fail/rc.rs rename to tests/run-pass/rc.rs index 001dec5b42747..c96818932d777 100644 --- a/tests/compile-fail/rc.rs +++ b/tests/run-pass/rc.rs @@ -1,5 +1,3 @@ -//error-pattern: no mir for `std::result::unwrap_failed::__STATIC_FMTSTR` - use std::cell::RefCell; use std::rc::Rc; From bd25230882c31e34a233431034b0ad7e5b3f81c2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 09:15:31 +0100 Subject: [PATCH 0614/1096] nit: move if let into match --- src/interpreter/terminator/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index c94ea97b056b0..fdd703e940bdf 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -521,16 +521,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } trace!("-need to drop {:?} at {:?}", ty, lval); - // special case `Box` to deallocate the inner allocation - if let ty::TyBox(contents_ty) = ty.sty { - let val = self.read_lvalue(lval)?; - let contents_ptr = val.read_ptr(&self.memory)?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; - trace!("-deallocating box"); - return self.memory.deallocate(contents_ptr); - } - match ty.sty { + // special case `Box` to deallocate the inner allocation + ty::TyBox(contents_ty) => { + let val = self.read_lvalue(lval)?; + let contents_ptr = val.read_ptr(&self.memory)?; + self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; + trace!("-deallocating box"); + self.memory.deallocate(contents_ptr)?; + }, + ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair let adt_ptr = self.force_allocation(lval)?.to_ptr(); From ff95efc52575c7ef8deb3c6628328d7a07442ec9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 09:15:59 +0100 Subject: [PATCH 0615/1096] Revert "Fix tests broken by std::vec::SetLenOnDrop." This reverts commit 366c793306609f4a80e7977be766cbc7e9c2b3be. --- tests/run-pass/heap.rs | 27 +++++++++++---------------- tests/run-pass/vecs.rs | 18 ++++-------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/tests/run-pass/heap.rs b/tests/run-pass/heap.rs index 4bf6a085e35fc..b533f91646988 100644 --- a/tests/run-pass/heap.rs +++ b/tests/run-pass/heap.rs @@ -11,25 +11,20 @@ fn make_box_syntax() -> Box<(i16, i16)> { fn allocate_reallocate() { let mut s = String::new(); - // 4 byte heap alloc (__rust_allocate) - s.push('f'); - assert_eq!(s.len(), 1); - assert_eq!(s.capacity(), 4); + // 6 byte heap alloc (__rust_allocate) + s.push_str("foobar"); + assert_eq!(s.len(), 6); + assert_eq!(s.capacity(), 6); - // heap size doubled to 8 (__rust_reallocate) - // FIXME: String::push_str is broken because it hits the std::vec::SetLenOnDrop code and we - // don't call destructors in miri yet. - s.push('o'); - s.push('o'); - s.push('o'); - s.push('o'); - assert_eq!(s.len(), 5); - assert_eq!(s.capacity(), 8); + // heap size doubled to 12 (__rust_reallocate) + s.push_str("baz"); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 12); - // heap size reduced to 5 (__rust_reallocate) + // heap size reduced to 9 (__rust_reallocate) s.shrink_to_fit(); - assert_eq!(s.len(), 5); - assert_eq!(s.capacity(), 5); + assert_eq!(s.len(), 9); + assert_eq!(s.capacity(), 9); } fn main() { diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index 0ad7a371762bc..b3a88014e6f9a 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -1,13 +1,3 @@ -// FIXME: The normal `vec!` macro is currently broken in Miri because it hits the -// std::vec::SetLenOnDrop code and Miri doesn't call destructors yet. -macro_rules! miri_vec { - ($($e:expr),*) => ({ - let mut v = Vec::new(); - $(v.push($e);)* - v - }); -} - fn make_vec() -> Vec { let mut v = Vec::with_capacity(4); v.push(1); @@ -16,22 +6,22 @@ fn make_vec() -> Vec { } fn make_vec_macro() -> Vec { - miri_vec![1, 2] + vec![1, 2] } fn make_vec_macro_repeat() -> Vec { - miri_vec![42, 42, 42, 42, 42] + vec![42; 5] } fn vec_into_iter() -> u8 { - miri_vec![1, 2, 3, 4] + vec![1, 2, 3, 4] .into_iter() .map(|x| x * x) .fold(0, |x, y| x + y) } fn vec_reallocate() -> Vec { - let mut v = miri_vec![1, 2]; + let mut v = vec![1, 2]; v.push(3); v.push(4); v.push(5); From 73d7f1d41b0521da8385f9910a20c1c116246dd4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 09:34:54 +0100 Subject: [PATCH 0616/1096] implement drop for NonZero optimized enums --- src/interpreter/terminator/mod.rs | 15 +++++++ .../regions-lifetime-nonfree-late-bound.rs | 45 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/run-pass/regions-lifetime-nonfree-late-bound.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index fdd703e940bdf..6c400a040fb28 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -560,6 +560,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); // nothing to do, this is zero sized (e.g. `None`) } }, + Layout::RawNullablePointer { nndiscr, .. } => { + let discr = self.read_discriminant_value(adt_ptr, ty)?; + if discr == nndiscr { + assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); + let field_ty = &adt_def.variants[discr as usize].fields[0]; + let field_ty = self.monomorphize_field_ty(field_ty, substs); + // FIXME: once read_discriminant_value works with lvalue, don't force + // alloc in the RawNullablePointer case + self.drop(Lvalue::from_ptr(adt_ptr), field_ty, drop)?; + return Ok(()); + } else { + // FIXME: the zst variant might contain zst types that impl Drop + return Ok(()); // nothing to do, this is zero sized (e.g. `None`) + } + }, _ => bug!("{:?} is not an adt layout", layout), }; for (field_ty, offset) in fields { diff --git a/tests/run-pass/regions-lifetime-nonfree-late-bound.rs b/tests/run-pass/regions-lifetime-nonfree-late-bound.rs new file mode 100644 index 0000000000000..1aef95d8a3f30 --- /dev/null +++ b/tests/run-pass/regions-lifetime-nonfree-late-bound.rs @@ -0,0 +1,45 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is a regression test for the ICE from issue #10846. +// +// The original issue causing the ICE: the LUB-computations during +// type inference were encountering late-bound lifetimes, and +// asserting that such lifetimes should have already been substituted +// with a concrete lifetime. +// +// However, those encounters were occurring within the lexical scope +// of the binding for the late-bound lifetime; that is, the late-bound +// lifetimes were perfectly valid. The core problem was that the type +// folding code was over-zealously passing back all lifetimes when +// doing region-folding, when really all clients of the region-folding +// case only want to see FREE lifetime variables, not bound ones. + +// pretty-expanded FIXME #23616 + +#![allow(unused_features)] +#![feature(box_syntax)] + +pub fn main() { + fn explicit() { + fn test(_x: Option>) where F: FnMut(Box FnMut(&'a isize)>) {} + test(Some(box |_f: Box FnMut(&'a isize)>| {})); + } + + // The code below is shorthand for the code above (and more likely + // to represent what one encounters in practice). + fn implicit() { + fn test(_x: Option>) where F: FnMut(Box< FnMut(& isize)>) {} + test(Some(box |_f: Box< FnMut(& isize)>| {})); + } + + explicit(); + implicit(); +} From b12e7224afaee84cb0f01ff82dfa367644826a56 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 15:48:45 +0100 Subject: [PATCH 0617/1096] move method to function so it can be used in map iterators without borrowing self --- src/interpreter/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0cd7190db9919..6ce987d314d32 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -285,11 +285,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn monomorphize_field_ty(&self, f: ty::FieldDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { - let substituted = &f.ty(self.tcx, substs); - self.tcx.normalize_associated_type(&substituted) - } - pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, substs); self.tcx.normalize_associated_type(&substituted) @@ -1511,8 +1506,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { - let src_fty = self.monomorphize_field_ty(src_f, substs_a); - let dst_fty = self.monomorphize_field_ty(dst_f, substs_b); + let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); + let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); if self.type_size(dst_fty) == 0 { continue; } @@ -1729,3 +1724,9 @@ impl IntegerExt for layout::Integer { } } } + + +pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: ty::FieldDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { + let substituted = &f.ty(tcx, substs); + tcx.normalize_associated_type(&substituted) +} From 8003c570d8fe50063587e05dc09dc2be837c0a03 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 15:49:14 +0100 Subject: [PATCH 0618/1096] don't panic on pointer to value comparison in primvals --- src/primval.rs | 2 +- tests/compile-fail/tag-align-dyn-u64.rs | 37 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/tag-align-dyn-u64.rs diff --git a/src/primval.rs b/src/primval.rs index 74546626d6ec1..2d8b50076234f 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -289,7 +289,7 @@ pub fn binary_op<'tcx>( } (None, None) => {} - _ => unimplemented!(), + _ => return Err(EvalError::ReadPointerAsBytes), } let (l, r) = (left.bits, right.bits); diff --git a/tests/compile-fail/tag-align-dyn-u64.rs b/tests/compile-fail/tag-align-dyn-u64.rs new file mode 100644 index 0000000000000..dc93965b7e552 --- /dev/null +++ b/tests/compile-fail/tag-align-dyn-u64.rs @@ -0,0 +1,37 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use std::mem; + +enum Tag { + Tag2(A) +} + +struct Rec { + c8: u8, + t: Tag +} + +fn mk_rec() -> Rec { + return Rec { c8:0, t:Tag::Tag2(0) }; +} + +fn is_u64_aligned(u: &Tag) -> bool { + let p: usize = unsafe { mem::transmute(u) }; + let u64_align = std::mem::align_of::(); + return (p & (u64_align - 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes +} + +pub fn main() { + let x = mk_rec(); + assert!(is_u64_aligned(&x.t)); +} From 859b7049c89244c73c0ebf1520f73f1bdf98f359 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 15:49:51 +0100 Subject: [PATCH 0619/1096] add method to ensure that a Value::ByRef is now a Value::ByVal* --- src/interpreter/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6ce987d314d32..689ef9245363e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1115,13 +1115,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + /// ensures this Value is not a ByRef + fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match value { - Value::ByRef(ptr) => match self.read_value(ptr, ty)? { - Value::ByRef(_) => bug!("read_value can't result in `ByRef`"), - Value::ByVal(primval) => Ok(primval), - Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), - }, + Value::ByRef(ptr) => self.read_value(ptr, ty), + other => Ok(other), + } + } + + fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + match self.follow_by_ref_value(value, ty)? { + Value::ByRef(_) => bug!("follow_by_ref_value can't result in `ByRef`"), Value::ByVal(primval) => { let new_primval = self.transmute_primval(primval, ty)?; From 0420c27c8e662990e884fc93f58eff5ea12dd995 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 15:55:05 +0100 Subject: [PATCH 0620/1096] fix drop ordering and forward fat pointer extras to fields this doesn't yet use the fat pointer extras --- src/interpreter/terminator/mod.rs | 117 ++++++++++++++++++++++------ tests/run-pass/move-arg-2-unique.rs | 20 +++++ 2 files changed, 113 insertions(+), 24 deletions(-) create mode 100644 tests/run-pass/move-arg-2-unique.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 6c400a040fb28..1a0edb0b91a7a 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -3,7 +3,7 @@ use rustc::mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; -use rustc::ty::subst::Substs; +use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use std::rc::Rc; use syntax::codemap::{DUMMY_SP, Span}; @@ -12,7 +12,7 @@ use syntax::{ast, attr}; use error::{EvalError, EvalResult}; use memory::Pointer; use primval::PrimVal; -use super::{EvalContext, Lvalue, IntegerExt, StackPopCleanup}; +use super::{EvalContext, Lvalue, IntegerExt, StackPopCleanup, LvalueExtra, monomorphize_field_ty}; use super::value::Value; mod intrinsics; @@ -146,9 +146,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn eval_drop_impls(&mut self, drops: Vec<(DefId, Pointer, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { + fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { let span = self.frame().span; - for (drop_def_id, adt_ptr, substs) in drops { + // add them to the stack in reverse order, because the impl that needs to run the last + // is the one that needs to be at the bottom of the stack + for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { // FIXME: supply a real span let mir = self.load_mir(drop_def_id)?; trace!("substs for drop glue: {:?}", substs); @@ -165,7 +167,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; let ty = self.frame().mir.local_decls[first].ty; - self.write_value(Value::ByVal(PrimVal::from_ptr(adt_ptr)), dest, ty)?; + self.write_value(self_arg, dest, ty)?; } Ok(()) } @@ -513,7 +515,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, lval: Lvalue<'tcx>, ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Pointer, &'tcx Substs<'tcx>)>, + drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, ) -> EvalResult<'tcx, ()> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); @@ -525,18 +527,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // special case `Box` to deallocate the inner allocation ty::TyBox(contents_ty) => { let val = self.read_lvalue(lval)?; - let contents_ptr = val.read_ptr(&self.memory)?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; - trace!("-deallocating box"); - self.memory.deallocate(contents_ptr)?; + // we are going through the read_value path, because that already does all the + // checks for the trait object types. We'd only be repeating ourselves here. + let val = self.follow_by_ref_value(val, ty)?; + trace!("box dealloc on {:?}", val); + match val { + Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), + Value::ByVal(ptr) => { + assert!(self.type_is_sized(contents_ty)); + let contents_ptr = ptr.expect_ptr("value of Box type must be a pointer"); + self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; + }, + Value::ByValPair(prim_ptr, extra) => { + let ptr = prim_ptr.expect_ptr("value of Box type must be a pointer"); + let extra = match extra.try_as_ptr() { + Some(vtable) => LvalueExtra::Vtable(vtable), + None => LvalueExtra::Length(extra.expect_uint("slice length")), + }; + self.drop( + Lvalue::Ptr { + ptr: ptr, + extra: extra, + }, + contents_ty, + drop, + )?; + }, + } + let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); + let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); + // this is somewhat hacky, but hey, there's no representation difference between + // pointers and references, so + // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) + // is the same as + // fn drop(&mut self) if Self is Box + drop.push((box_free_fn, val, substs)); }, ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair - let adt_ptr = self.force_allocation(lval)?.to_ptr(); + let lval = self.force_allocation(lval)?; + let adt_ptr = match lval { + Lvalue::Ptr { ptr, .. } => ptr, + _ => bug!("force allocation can only yield Lvalue::Ptr"), + }; // run drop impl before the fields' drop impls if let Some(drop_def_id) = adt_def.destructor() { - drop.push((drop_def_id, adt_ptr, substs)); + drop.push((drop_def_id, Value::ByVal(PrimVal::from_ptr(adt_ptr)), substs)); } let layout = self.type_layout(ty); let fields = match *layout { @@ -565,10 +602,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if discr == nndiscr { assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); let field_ty = &adt_def.variants[discr as usize].fields[0]; - let field_ty = self.monomorphize_field_ty(field_ty, substs); + let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); // FIXME: once read_discriminant_value works with lvalue, don't force // alloc in the RawNullablePointer case - self.drop(Lvalue::from_ptr(adt_ptr), field_ty, drop)?; + self.drop(lval, field_ty, drop)?; return Ok(()); } else { // FIXME: the zst variant might contain zst types that impl Drop @@ -577,18 +614,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, _ => bug!("{:?} is not an adt layout", layout), }; - for (field_ty, offset) in fields { - let field_ty = self.monomorphize_field_ty(field_ty, substs); - self.drop(Lvalue::from_ptr(adt_ptr.offset(offset.bytes() as isize)), field_ty, drop)?; - } + let tcx = self.tcx; + self.drop_fields( + fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)), + lval, + drop, + )?; }, ty::TyTuple(fields) => { - // FIXME: some tuples are represented as ByValPair - let ptr = self.force_allocation(lval)?.to_ptr(); - for (i, field_ty) in fields.iter().enumerate() { - let offset = self.get_field_offset(ty, i)?.bytes() as isize; - self.drop(Lvalue::from_ptr(ptr.offset(offset)), field_ty, drop)?; - } + let offsets = match *self.type_layout(ty) { + Layout::Univariant { ref variant, .. } => &variant.offsets, + _ => bug!("tuples must be univariant"), + }; + self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; }, // other types do not need to process drop _ => {}, @@ -596,6 +634,37 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + + fn drop_fields< + I: Iterator, ty::layout::Size)>, + >( + &mut self, + mut fields: I, + lval: Lvalue<'tcx>, + drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, + ) -> EvalResult<'tcx, ()> { + // FIXME: some aggregates may be represented by PrimVal::Pair + let (adt_ptr, extra) = match self.force_allocation(lval)? { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("force allocation must yield Lvalue::Ptr"), + }; + // manual iteration, because we need to be careful about the last field if it is unsized + while let Some((field_ty, offset)) = fields.next() { + let ptr = adt_ptr.offset(offset.bytes() as isize); + if self.type_is_sized(field_ty) { + self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; + } else { + let lvalue = Lvalue::Ptr { + ptr: ptr, + extra: extra, + }; + self.drop(lvalue, field_ty, drop)?; + break; // if it is not sized, then this is the last field anyway + } + } + assert!(fields.next().is_none()); + Ok(()) + } } #[derive(Debug)] diff --git a/tests/run-pass/move-arg-2-unique.rs b/tests/run-pass/move-arg-2-unique.rs new file mode 100644 index 0000000000000..d44c83763b7c4 --- /dev/null +++ b/tests/run-pass/move-arg-2-unique.rs @@ -0,0 +1,20 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_features, unused_variables)] +#![feature(box_syntax)] + +fn test(foo: Box> ) { assert_eq!((*foo)[0], 10); } + +pub fn main() { + let x = box vec![10]; + // Test forgetting a local by move-in + test(x); +} From bd6e52d831769ccf86d1bbd66444f13c800cac7e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 16:37:12 +0100 Subject: [PATCH 0621/1096] fix the `drop_in_place` intrinsic for fat pointers --- src/interpreter/terminator/intrinsics.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 71fb83d580e48..f6dc8778a5f3e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -123,9 +123,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "drop_in_place" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr_ty = self.tcx.mk_mut_ptr(ty); + let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { + Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), + Value::ByVal(ptr) => Lvalue::from_ptr(ptr.expect_ptr("drop_in_place first arg not a pointer")), + Value::ByValPair(ptr, extra) => Lvalue::Ptr { + ptr: ptr.expect_ptr("drop_in_place first arg not a pointer"), + extra: match extra.try_as_ptr() { + Some(vtable) => LvalueExtra::Vtable(vtable), + None => LvalueExtra::Length(extra.expect_uint("either pointer or not, but not neither")), + }, + }, + }; let mut drops = Vec::new(); - self.drop(Lvalue::from_ptr(ptr), ty, &mut drops)?; + self.drop(lvalue, ty, &mut drops)?; self.eval_drop_impls(drops)?; } From c4c8764f53779d192401a571626115e3c7b26b61 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 16:38:04 +0100 Subject: [PATCH 0622/1096] generate a drop method pointer in the vtable and process it --- src/interpreter/terminator/mod.rs | 19 ++++++++++++++++++ src/interpreter/vtable.rs | 15 ++++++++++++-- .../call_drop_through_trait_object.rs | 20 +++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass/call_drop_through_trait_object.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 1a0edb0b91a7a..bbf3fab93aec4 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -628,6 +628,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; }, + ty::TyTrait(_) => { + let lval = self.force_allocation(lval)?; + let (ptr, vtable) = match lval { + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), + _ => bug!("expected an lvalue with a vtable"), + }; + let drop_fn = self.memory.read_ptr(vtable)?; + // some values don't need to call a drop impl, so the value is null + if !drop_fn.points_to_zst() { + let (def_id, substs, ty) = self.memory.get_fn(drop_fn.alloc_id)?; + let fn_sig = self.tcx.erase_late_bound_regions_and_normalize(&ty.sig); + let real_ty = fn_sig.inputs[0]; + self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; + drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); + } else { + // just a sanity check + assert_eq!(drop_fn.offset, 0); + } + } // other types do not need to process drop _ => {}, } diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 54001bbc71226..17f2c6b7020ce 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -84,8 +84,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_size = self.memory.pointer_size(); let vtable = self.memory.allocate(ptr_size * (3 + methods.len()), ptr_size)?; - // FIXME: generate a destructor for the vtable. - // trans does this with glue::get_drop_glue(ccx, trait_ref.self_ty()) + // in case there is no drop function to be called, this still needs to be initialized + self.memory.write_usize(vtable, 0)?; + if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { + if let Some(drop_def_id) = adt_def.destructor() { + let ty_scheme = self.tcx.lookup_item_type(drop_def_id); + let fn_ty = match ty_scheme.ty.sty { + ty::TyFnDef(_, _, fn_ty) => fn_ty, + _ => bug!("drop method is not a TyFnDef"), + }; + let fn_ptr = self.memory.create_fn_ptr(drop_def_id, substs, fn_ty); + self.memory.write_ptr(vtable, fn_ptr)?; + } + } self.memory.write_usize(vtable.offset(ptr_size as isize), size as u64)?; self.memory.write_usize(vtable.offset((ptr_size * 2) as isize), align as u64)?; diff --git a/tests/run-pass/call_drop_through_trait_object.rs b/tests/run-pass/call_drop_through_trait_object.rs new file mode 100644 index 0000000000000..9b6acf0b14746 --- /dev/null +++ b/tests/run-pass/call_drop_through_trait_object.rs @@ -0,0 +1,20 @@ +trait Foo {} + +struct Bar; + +static mut DROP_CALLED: bool = false; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_CALLED = true; } + } +} + +impl Foo for Bar {} + +fn main() { + let b: Box = Box::new(Bar); + assert!(unsafe { !DROP_CALLED }); + drop(b); + assert!(unsafe { DROP_CALLED }); +} From 5ef7924aa96fe5aef3662b68e7b100847d6f3f5a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 16:51:43 +0100 Subject: [PATCH 0623/1096] call drop for all elements of an owned slice --- src/interpreter/terminator/mod.rs | 13 +++++++++++++ tests/run-pass/call_drop_through_owned_slice.rs | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/run-pass/call_drop_through_owned_slice.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index bbf3fab93aec4..f7ca25c13c728 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -646,6 +646,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // just a sanity check assert_eq!(drop_fn.offset, 0); } + }, + ty::TySlice(elem_ty) => { + let lval = self.force_allocation(lval)?; + let (ptr, len) = match lval { + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), + _ => bug!("expected an lvalue with a length"), + }; + let size = self.type_size(elem_ty) as isize; + // FIXME: this creates a lot of stack frames if the element type has + // a drop impl + for i in 0..len { + self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; + } } // other types do not need to process drop _ => {}, diff --git a/tests/run-pass/call_drop_through_owned_slice.rs b/tests/run-pass/call_drop_through_owned_slice.rs new file mode 100644 index 0000000000000..3ec6be65ed8b6 --- /dev/null +++ b/tests/run-pass/call_drop_through_owned_slice.rs @@ -0,0 +1,16 @@ +struct Bar; + +static mut DROP_COUNT: usize = 0; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1; } + } +} + +fn main() { + let b: Box<[Bar]> = vec![Bar, Bar, Bar, Bar].into_boxed_slice(); + assert_eq!(unsafe { DROP_COUNT }, 0); + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); +} From 893f16389ec6e5bf117bed796738799a2c636519 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 17:34:33 +0100 Subject: [PATCH 0624/1096] run drop on array elements --- src/interpreter/terminator/mod.rs | 16 ++++++++++++++- tests/run-pass/call_drop_on_array_elements.rs | 16 +++++++++++++++ .../call_drop_on_fat_ptr_array_elements.rs | 20 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/call_drop_on_array_elements.rs create mode 100644 tests/run-pass/call_drop_on_fat_ptr_array_elements.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index f7ca25c13c728..dab0aed4acbe3 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -659,7 +659,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for i in 0..len { self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; } - } + }, + ty::TyArray(elem_ty, len) => { + let lval = self.force_allocation(lval)?; + let (ptr, extra) = match lval { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("expected an lvalue with a length"), + }; + let size = self.type_size(elem_ty) as isize; + // FIXME: this creates a lot of stack frames if the element type has + // a drop impl + for i in 0..len { + self.drop(Lvalue::Ptr { ptr: ptr.offset(i as isize * size), extra: extra }, elem_ty, drop)?; + } + }, + // FIXME: what about TyClosure and TyAnon? // other types do not need to process drop _ => {}, } diff --git a/tests/run-pass/call_drop_on_array_elements.rs b/tests/run-pass/call_drop_on_array_elements.rs new file mode 100644 index 0000000000000..80dd63de5e9a5 --- /dev/null +++ b/tests/run-pass/call_drop_on_array_elements.rs @@ -0,0 +1,16 @@ +struct Bar; + +static mut DROP_COUNT: usize = 0; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1; } + } +} + +fn main() { + let b = [Bar, Bar, Bar, Bar]; + assert_eq!(unsafe { DROP_COUNT }, 0); + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); +} diff --git a/tests/run-pass/call_drop_on_fat_ptr_array_elements.rs b/tests/run-pass/call_drop_on_fat_ptr_array_elements.rs new file mode 100644 index 0000000000000..a1ab5c45e358c --- /dev/null +++ b/tests/run-pass/call_drop_on_fat_ptr_array_elements.rs @@ -0,0 +1,20 @@ +trait Foo {} + +struct Bar; + +impl Foo for Bar {} + +static mut DROP_COUNT: usize = 0; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1; } + } +} + +fn main() { + let b: [Box; 4] = [Box::new(Bar), Box::new(Bar), Box::new(Bar), Box::new(Bar)]; + assert_eq!(unsafe { DROP_COUNT }, 0); + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); +} From 07c752cc82d2318028e90edee6939b9a32be0310 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 17:51:13 +0100 Subject: [PATCH 0625/1096] fix the block processing for the drop_in_place intrinsic --- src/interpreter/terminator/intrinsics.rs | 9 +++++++- src/interpreter/terminator/mod.rs | 3 +-- .../call_drop_through_trait_object_rc.rs | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/call_drop_through_trait_object_rc.rs diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index f6dc8778a5f3e..9161f6e35b4e1 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -18,6 +18,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, + target: mir::BasicBlock, ) -> EvalResult<'tcx, ()> { let arg_vals: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) @@ -137,7 +138,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let mut drops = Vec::new(); self.drop(lvalue, ty, &mut drops)?; - self.eval_drop_impls(drops)?; + // need to change the block before pushing the drop impl stack frames + // we could do this for all intrinsics before evaluating the intrinsics, but if + // the evaluation fails, we should not have moved forward + self.goto_block(target); + return self.eval_drop_impls(drops); } "fabsf32" => { @@ -341,6 +346,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), } + self.goto_block(target); + // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index dab0aed4acbe3..dcca791f66653 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -187,8 +187,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); let (ret, target) = destination.unwrap(); - self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout)?; - self.goto_block(target); + self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) } diff --git a/tests/run-pass/call_drop_through_trait_object_rc.rs b/tests/run-pass/call_drop_through_trait_object_rc.rs new file mode 100644 index 0000000000000..ce56ca6a1cafd --- /dev/null +++ b/tests/run-pass/call_drop_through_trait_object_rc.rs @@ -0,0 +1,22 @@ +trait Foo {} + +struct Bar; + +static mut DROP_CALLED: bool = false; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_CALLED = true; } + } +} + +impl Foo for Bar {} + +use std::rc::Rc; + +fn main() { + let b: Rc = Rc::new(Bar); + assert!(unsafe { !DROP_CALLED }); + drop(b); + assert!(unsafe { DROP_CALLED }); +} From 53f11185179739f62edda527717f22ccdf91258f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 17:51:28 +0100 Subject: [PATCH 0626/1096] remove needless allocations --- src/interpreter/terminator/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index dcca791f66653..90f58ddb61d3d 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -628,7 +628,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; }, ty::TyTrait(_) => { - let lval = self.force_allocation(lval)?; let (ptr, vtable) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), _ => bug!("expected an lvalue with a vtable"), @@ -647,7 +646,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, ty::TySlice(elem_ty) => { - let lval = self.force_allocation(lval)?; let (ptr, len) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), _ => bug!("expected an lvalue with a length"), From 392123552a028bd7e0592c7703f92a8de4872e5f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Nov 2016 17:51:53 +0100 Subject: [PATCH 0627/1096] fix a bug error message --- src/interpreter/terminator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 90f58ddb61d3d..76206aef94943 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -661,7 +661,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let lval = self.force_allocation(lval)?; let (ptr, extra) = match lval { Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("expected an lvalue with a length"), + _ => bug!("expected an lvalue with optional extra data"), }; let size = self.type_size(elem_ty) as isize; // FIXME: this creates a lot of stack frames if the element type has From b6c7d76562dc778d9fdad0d67f9c0e3f60e15baa Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 5 Nov 2016 13:02:29 +0100 Subject: [PATCH 0628/1096] address nits --- src/interpreter/terminator/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 76206aef94943..4fe4ac72cbc06 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -686,11 +686,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lval: Lvalue<'tcx>, drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, ) -> EvalResult<'tcx, ()> { - // FIXME: some aggregates may be represented by PrimVal::Pair - let (adt_ptr, extra) = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("force allocation must yield Lvalue::Ptr"), - }; + // FIXME: some aggregates may be represented by Value::ByValPair + let (adt_ptr, extra) = match self.force_allocation(lval)?.to_ptr_and_extra(); // manual iteration, because we need to be careful about the last field if it is unsized while let Some((field_ty, offset)) = fields.next() { let ptr = adt_ptr.offset(offset.bytes() as isize); From 86062ef930a756cb993832fbfce2467500050268 Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Sat, 5 Nov 2016 02:53:02 +0000 Subject: [PATCH 0629/1096] Read discriminant as a signed integer if specified by layout. This ensures it gets sign extended correctly. Fixes #78 --- src/interpreter/terminator/mod.rs | 7 ++++++- tests/run-pass/negative_discriminant.rs | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/negative_discriminant.rs diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index fdd703e940bdf..414fdeb936dd8 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -252,11 +252,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let adt_layout = self.type_layout(adt_ty); let discr_val = match *adt_layout { - General { discr, .. } | CEnum { discr, .. } => { + General { discr, .. } | CEnum { discr, signed: false, .. } => { let discr_size = discr.size().bytes(); self.memory.read_uint(adt_ptr, discr_size as usize)? } + CEnum { discr, signed: true, .. } => { + let discr_size = discr.size().bytes(); + self.memory.read_int(adt_ptr, discr_size as usize)? as u64 + } + RawNullablePointer { nndiscr, .. } => { self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? } diff --git a/tests/run-pass/negative_discriminant.rs b/tests/run-pass/negative_discriminant.rs new file mode 100644 index 0000000000000..16f175e7dfc80 --- /dev/null +++ b/tests/run-pass/negative_discriminant.rs @@ -0,0 +1,13 @@ +enum AB { A = -1, B = 1 } + +fn main() { + match AB::A { + AB::A => (), + AB::B => panic!(), + } + + match AB::B { + AB::A => panic!(), + AB::B => (), + } +} From 9c85e203c56949a887035ab66e6da5d34dea1c82 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 5 Nov 2016 17:09:37 +0100 Subject: [PATCH 0630/1096] remove leftover match --- src/interpreter/terminator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 4fe4ac72cbc06..8ea049aa4f8d8 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -687,7 +687,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, ) -> EvalResult<'tcx, ()> { // FIXME: some aggregates may be represented by Value::ByValPair - let (adt_ptr, extra) = match self.force_allocation(lval)?.to_ptr_and_extra(); + let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); // manual iteration, because we need to be careful about the last field if it is unsized while let Some((field_ty, offset)) = fields.next() { let ptr = adt_ptr.offset(offset.bytes() as isize); From a8d90ff12e33b89b6c2ed204a24d5697e6cc642c Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 6 Nov 2016 22:25:54 -0800 Subject: [PATCH 0631/1096] Update for changes in rustc. --- src/interpreter/cast.rs | 23 +++++++++++++---------- src/interpreter/mod.rs | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 70b39fc882ec5..f14baa2446c0b 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -92,21 +92,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - use primval::PrimValKind::*; use rustc::ty::TypeVariants::*; match ty.sty { TyRef(..) | TyRawPtr(_) => Ok(PrimVal::from_ptr(ptr)), TyFnPtr(_) => Ok(PrimVal::from_fn_ptr(ptr)), - TyInt(IntTy::I8) => Ok(PrimVal::new(ptr.to_int()? as u64, I8)), - TyInt(IntTy::I16) => Ok(PrimVal::new(ptr.to_int()? as u64, I16)), - TyInt(IntTy::I32) => Ok(PrimVal::new(ptr.to_int()? as u64, I32)), - TyInt(IntTy::I64) => Ok(PrimVal::new(ptr.to_int()? as u64, I64)), - - TyUint(UintTy::U8) => Ok(PrimVal::new(ptr.to_int()? as u64, U8)), - TyUint(UintTy::U16) => Ok(PrimVal::new(ptr.to_int()? as u64, U16)), - TyUint(UintTy::U32) => Ok(PrimVal::new(ptr.to_int()? as u64, U32)), - TyUint(UintTy::U64) => Ok(PrimVal::new(ptr.to_int()? as u64, U64)), + TyInt(IntTy::I8) | + TyInt(IntTy::I16) | + TyInt(IntTy::I32) | + TyInt(IntTy::I64) | + TyInt(IntTy::Is) | + TyUint(UintTy::U8) | + TyUint(UintTy::U16) | + TyUint(UintTy::U32) | + TyUint(UintTy::U64) | + TyUint(UintTy::Us) => { + let val = PrimVal::from_ptr(ptr); + self.transmute_primval(val, ty) + } _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 689ef9245363e..b15f8b2d2d8a4 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1699,13 +1699,13 @@ pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut passes = ::rustc::mir::transform::Passes::new(); passes.push_hook(Box::new(::rustc_mir::transform::dump_mir::DumpMir)); passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); - passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads"))); + passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("no-landing-pads"))); passes.push_pass(Box::new(::rustc_mir::transform::erase_regions::EraseRegions)); passes.push_pass(Box::new(::rustc_borrowck::ElaborateDrops)); passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); - passes.push_pass(Box::new(::rustc_mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"))); + passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("elaborate-drops"))); passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); passes.run_passes(tcx); From 2d4301ea7eb7569881e2250a00098d119d053c14 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 6 Nov 2016 22:30:56 -0800 Subject: [PATCH 0632/1096] Simplify cast_ptr. --- src/interpreter/cast.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index f14baa2446c0b..e59376c321357 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -96,21 +96,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { TyRef(..) | TyRawPtr(_) => Ok(PrimVal::from_ptr(ptr)), TyFnPtr(_) => Ok(PrimVal::from_fn_ptr(ptr)), - - TyInt(IntTy::I8) | - TyInt(IntTy::I16) | - TyInt(IntTy::I32) | - TyInt(IntTy::I64) | - TyInt(IntTy::Is) | - TyUint(UintTy::U8) | - TyUint(UintTy::U16) | - TyUint(UintTy::U32) | - TyUint(UintTy::U64) | - TyUint(UintTy::Us) => { - let val = PrimVal::from_ptr(ptr); - self.transmute_primval(val, ty) - } - + TyInt(_) | TyUint(_) => self.transmute_primval(PrimVal::from_ptr(ptr), ty), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } From 921f5af1fe14dad904df3b5f6da47860b234a813 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Thu, 10 Nov 2016 19:20:11 +0100 Subject: [PATCH 0633/1096] ensure that integers cast to pointers will never point at a valid alloc, not even the zst alloc --- src/error.rs | 2 +- src/interpreter/terminator/mod.rs | 2 +- src/memory.rs | 6 +++--- tests/run-pass/assume_bug.rs | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass/assume_bug.rs diff --git a/src/error.rs b/src/error.rs index 86f22d33a257e..52662218ffa5b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,7 +61,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => - "tried to use a pointer as a function pointer", + "tried to use an integer pointer as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index f99a23a22ae6a..1ddcee34722a1 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -639,7 +639,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null - if !drop_fn.points_to_zst() { + if drop_fn != Pointer::from_int(0) { let (def_id, substs, ty) = self.memory.get_fn(drop_fn.alloc_id)?; let fn_sig = self.tcx.erase_late_bound_regions_and_normalize(&ty.sig); let real_ty = fn_sig.inputs[0]; diff --git a/src/memory.rs b/src/memory.rs index 73770ee403f44..a83ed98a37e32 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -73,7 +73,7 @@ impl Pointer { // FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger // than host usize. pub fn from_int(i: usize) -> Self { - Pointer::new(ZST_ALLOC_ID, i) + Pointer::new(NEVER_ALLOC_ID, i) } pub fn zst_ptr() -> Self { @@ -290,7 +290,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -302,7 +302,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } diff --git a/tests/run-pass/assume_bug.rs b/tests/run-pass/assume_bug.rs new file mode 100644 index 0000000000000..e14f875c022e3 --- /dev/null +++ b/tests/run-pass/assume_bug.rs @@ -0,0 +1,3 @@ +fn main() { + vec![()].into_iter(); +} From b2d476eb387b739e37f28435613596e027241fc5 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:07:41 +0100 Subject: [PATCH 0634/1096] `type_size` now returns `None` for unsized types --- src/interpreter/mod.rs | 29 ++++++++++++++---------- src/interpreter/terminator/intrinsics.rs | 16 ++++++++----- src/interpreter/terminator/mod.rs | 6 ++--- src/interpreter/vtable.rs | 2 +- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b15f8b2d2d8a4..9c5dd30e378a0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: Ty<'tcx>, substs: &'tcx Substs<'tcx> ) -> EvalResult<'tcx, Pointer> { - let size = self.type_size_with_substs(ty, substs); + let size = self.type_size_with_substs(ty, substs).expect("cannot alloc memory for unsized type"); let align = self.type_align_with_substs(ty, substs); self.memory.allocate(size, align) } @@ -290,7 +290,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>) -> usize { + fn type_size(&self, ty: Ty<'tcx>) -> Option { self.type_size_with_substs(ty, self.substs()) } @@ -298,8 +298,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.type_align_with_substs(ty, self.substs()) } - fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + let layout = self.type_layout_with_substs(ty, substs); + if layout.is_unsized() { + None + } else { + Some(layout.size(&self.tcx.data_layout).bytes() as usize) + } } fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { @@ -480,7 +485,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty).expect("array elements are sized") as u64, _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); @@ -534,7 +539,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { for operand in operands { let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty), 0); + assert_eq!(self.type_size(operand_ty), Some(0)); } let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; @@ -576,7 +581,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyArray(elem_ty, n) => (elem_ty, n), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("repeat element type must be sized"); let value = self.eval_operand(operand)?; // FIXME(solson) @@ -991,7 +996,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)? @@ -1007,7 +1012,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("sequence element must be sized"); assert!(n >= min_length as u64); let index = if from_end { @@ -1026,7 +1031,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); assert!((from as u64) <= n - (to as u64)); let ptr = base_ptr.offset(from as isize * elem_size as isize); let extra = LvalueExtra::Length(n - to as u64 - from as u64); @@ -1046,7 +1051,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { - let size = self.type_size(ty); + let size = self.type_size(ty).expect("cannot copy from an unsized type"); let align = self.type_align(ty); self.memory.copy(src, dest, size, align)?; Ok(()) @@ -1512,7 +1517,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); - if self.type_size(dst_fty) == 0 { + if self.type_size(dst_fty) == Some(0) { continue; } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 9161f6e35b4e1..280cabc078593 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -96,7 +96,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty).expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty); let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; @@ -230,7 +230,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty) as isize; + let pointee_size = self.type_size(pointee_ty).expect("cannot offset a pointer to an unsized type") as isize; let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("offset second arg not isize"); @@ -281,7 +281,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = substs.type_at(0); - let size = self.type_size(ty) as u64; + // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the + // `size_of_val` intrinsic, then change this back to + // .expect("size_of intrinsic called on unsized value") + // see https://github.com/rust-lang/rust/pull/37708 + let size = self.type_size(ty).unwrap_or(!0) as u64; let size_val = self.usize_primval(size); self.write_primval(dest, size_val)?; } @@ -360,8 +364,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { value: Value, ) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - if self.type_is_sized(ty) { - Ok((self.type_size(ty) as u64, self.type_align(ty) as u64)) + if let Some(size) = self.type_size(ty) { + Ok((size as u64, self.type_align(ty) as u64)) } else { match ty.sty { ty::TyAdt(def, substs) => { @@ -435,7 +439,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; + let elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; let len = value.expect_slice_len(&self.memory)?; let align = self.type_align(elem_ty); Ok((len * elem_size, align as u64)) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 1ddcee34722a1..0097e4d2c2358 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -193,7 +193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { let ty = fn_ty.sig.0.output; - let size = self.type_size(ty); + let size = self.type_size(ty).expect("function return type cannot be unsized"); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, size)?; self.goto_block(target); @@ -655,7 +655,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), _ => bug!("expected an lvalue with a length"), }; - let size = self.type_size(elem_ty) as isize; + let size = self.type_size(elem_ty).expect("slice element must be sized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { @@ -668,7 +668,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("expected an lvalue with optional extra data"), }; - let size = self.type_size(elem_ty) as isize; + let size = self.type_size(elem_ty).expect("array element cannot be unsized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 17f2c6b7020ce..7320872724015 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }).collect(); - let size = self.type_size(trait_ref.self_ty()); + let size = self.type_size(trait_ref.self_ty()).expect("can't create a vtable for an unsized type"); let align = self.type_align(trait_ref.self_ty()); let ptr_size = self.memory.pointer_size(); From 1c40fb0da1dcdb8fd7445589c66b5a755a36ab3b Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:08:14 +0100 Subject: [PATCH 0635/1096] report the bad integer size instead of just the fact that it is bad --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index a83ed98a37e32..412eb62e9aefe 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -570,7 +570,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 2 => Ok(self.layout.i16_align.abi() as usize), 4 => Ok(self.layout.i32_align.abi() as usize), 8 => Ok(self.layout.i64_align.abi() as usize), - _ => bug!("bad integer size"), + _ => bug!("bad integer size: {}", size), } } From d42a7d021d564edc950678c39da593266f6411ba Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Fri, 11 Nov 2016 13:10:47 +0100 Subject: [PATCH 0636/1096] fix null optimizations for smaller than pointer enums fixes #76 --- src/interpreter/mod.rs | 20 +++++++++----------- src/interpreter/terminator/mod.rs | 15 +++++++++------ tests/run-pass/small_enum_size_bug.rs | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 tests/run-pass/small_enum_size_bug.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9c5dd30e378a0..bd99d9f86f429 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -203,10 +203,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::from_uint_with_size(n, self.memory.pointer_size()) } - fn isize_primval(&self, n: i64) -> PrimVal { - PrimVal::from_int_with_size(n, self.memory.pointer_size()) - } - fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len(), 1)?; @@ -523,7 +519,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(value, dest, value_ty)?; } else { assert_eq!(operands.len(), 0); - let zero = self.isize_primval(0); + let value_size = self.type_size(dest_ty).expect("pointer types are sized"); + let zero = PrimVal::from_int_with_size(0, value_size); self.write_primval(dest, zero)?; } } else { @@ -541,13 +538,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty), Some(0)); } - let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; + let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); - try!(self.memory.write_isize(dest, 0)); + let dest_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + try!(self.memory.write_int(dest, 0, dest_size)); } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -694,7 +692,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { + fn nonnull_offset_and_ty(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -709,10 +707,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), }; - self.field_path_offset(inner_ty, path) + self.field_path_offset_and_ty(inner_ty, path) } - fn field_path_offset>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, Size> { + fn field_path_offset_and_ty>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { let mut offset = Size::from_bytes(0); // Skip the initial 0 intended for LLVM GEP. @@ -722,7 +720,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); } - Ok(offset) + Ok((offset, ty)) } fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 0097e4d2c2358..afbcf3d0e5a10 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -263,14 +263,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.read_int(adt_ptr, discr_size as usize)? as u64 } - RawNullablePointer { nndiscr, .. } => { - self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? + RawNullablePointer { nndiscr, value } => { + let discr_size = value.size(&self.tcx.data_layout).bytes() as usize; + self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; + let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); - self.read_nonnull_discriminant_value(nonnull, nndiscr)? + // only the pointer part of a fat pointer is used for this space optimization + let discr_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } // The discriminant_value intrinsic returns 0 for non-sum types. @@ -281,8 +284,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { - let not_null = match self.memory.read_usize(ptr) { + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: usize) -> EvalResult<'tcx, u64> { + let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, Err(e) => return Err(e), diff --git a/tests/run-pass/small_enum_size_bug.rs b/tests/run-pass/small_enum_size_bug.rs new file mode 100644 index 0000000000000..7576a97e36adf --- /dev/null +++ b/tests/run-pass/small_enum_size_bug.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +enum E { + A = 1, + B = 2, + C = 3, +} + +fn main() { + let enone = None::; + if let Some(..) = enone { + panic!(); + } +} From 75f56eb144be4c87893c83fa5ad3b0ce4b84e737 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 19:26:20 +0100 Subject: [PATCH 0637/1096] fix field indexing into fat pointers --- src/interpreter/mod.rs | 15 ++++++++++++--- src/interpreter/terminator/mod.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bd99d9f86f429..08713b721142e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -544,7 +544,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); - let dest_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + let dest_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); try!(self.memory.write_int(dest, 0, dest_size)); } } else { @@ -734,8 +734,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - assert_eq!(field_index, 0); - Ok(ty) + if self.type_is_sized(ty) { + assert_eq!(field_index, 0); + Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)) + } else { + match (field_index, &ty.sty) { + (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), + (1, &ty::TyTrait(_)) | + (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)), + _ => bug!("invalid fat pointee type"), + } + } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index afbcf3d0e5a10..655a59902e9ee 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -272,7 +272,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); + let discr_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } From f71c31c0e866f87e7aaafc315b042c077ff302b5 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 21:30:03 +0100 Subject: [PATCH 0638/1096] cannot index into non-fat-pointers --- src/interpreter/mod.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 08713b721142e..0e96656a21964 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -734,16 +734,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - if self.type_is_sized(ty) { - assert_eq!(field_index, 0); - Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)) - } else { - match (field_index, &ty.sty) { - (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyTrait(_)) | - (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.never)), - _ => bug!("invalid fat pointee type"), - } + assert!(!self.type_is_sized(ty)); + match (field_index, &self.tcx.struct_tail(ty).sty) { + (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), + (1, &ty::TyTrait(_)) | + (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), + _ => bug!("invalid fat pointee type"), } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), From 2c34d6558c4d5c8f0283566cba8110a50de427a1 Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 13 Nov 2016 21:56:57 +0100 Subject: [PATCH 0639/1096] also address TyStr in the null pointer optimization --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 0e96656a21964..3a4d875921a73 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -734,12 +734,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | ty::TyBox(ty) => { - assert!(!self.type_is_sized(ty)); match (field_index, &self.tcx.struct_tail(ty).sty) { + (1, &ty::TyStr) | (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), (1, &ty::TyTrait(_)) | (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), - _ => bug!("invalid fat pointee type"), + _ => bug!("invalid fat pointee type: {}", ty), } } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), From 511fa40d23fc77c7867c773135eb0a2fe15a607f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:11:00 +0100 Subject: [PATCH 0640/1096] add test for int -> fn ptr cast --- src/error.rs | 2 +- tests/compile-fail/cast_box_int_to_fn_ptr.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/cast_box_int_to_fn_ptr.rs diff --git a/src/error.rs b/src/error.rs index 52662218ffa5b..8001d95941e84 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,7 +61,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => - "tried to use an integer pointer as a function pointer", + "tried to use an integer pointer or a dangling pointer as a function pointer", EvalError::InvalidBool => "invalid boolean value read", EvalError::InvalidDiscriminant => diff --git a/tests/compile-fail/cast_box_int_to_fn_ptr.rs b/tests/compile-fail/cast_box_int_to_fn_ptr.rs new file mode 100644 index 0000000000000..030bed6a35298 --- /dev/null +++ b/tests/compile-fail/cast_box_int_to_fn_ptr.rs @@ -0,0 +1,8 @@ +fn main() { + let b = Box::new(42); + let g = unsafe { + std::mem::transmute::<&usize, &fn(i32)>(&b) + }; + + (*g)(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer +} From 4a39c228df089e32cebea1b1aec8cd9694089574 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:11:44 +0100 Subject: [PATCH 0641/1096] minor fixes the FIXME was wrong here, there's no need for any special offsetting --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3a4d875921a73..a67142e7335d2 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1406,7 +1406,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.type_is_sized(ty) { PrimVal::from_ptr(p) } else { - // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` + trace!("reading fat pointer extra of type {}", ty); let extra = ptr.offset(self.memory.pointer_size() as isize); let extra = match self.tcx.struct_tail(ty).sty { ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), @@ -1513,7 +1513,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { //let dst = adt::MaybeSizedValue::sized(dst); let src_ptr = match src { Value::ByRef(ptr) => ptr, - _ => panic!("expected pointer, got {:?}", src), + _ => bug!("expected pointer, got {:?}", src), }; let iter = src_fields.zip(dst_fields).enumerate(); From 14ff6411f0018c58de6a323dc8fb8bc46fc44356 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:12:49 +0100 Subject: [PATCH 0642/1096] make sure ByVal pointers act just like ByRef to a pointer --- src/interpreter/cast.rs | 5 +-- src/interpreter/mod.rs | 40 ++++++++----------- src/interpreter/terminator/intrinsics.rs | 14 ++++--- src/interpreter/terminator/mod.rs | 14 +++---- src/interpreter/value.rs | 31 +++++++-------- src/memory.rs | 4 +- src/primval.rs | 50 ++++++++++-------------- tests/compile-fail/cast_int_to_fn_ptr.rs | 7 ++++ 8 files changed, 77 insertions(+), 88 deletions(-) create mode 100644 tests/compile-fail/cast_int_to_fn_ptr.rs diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index e59376c321357..217a10a4c7bcc 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -21,10 +21,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), - FnPtr | Ptr => { - let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation"); - self.cast_ptr(ptr, ty) - } + FnPtr | Ptr => self.cast_ptr(val.to_ptr(), ty), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a67142e7335d2..791466f6908bf 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -963,33 +963,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { - use interpreter::value::Value::*; + let val = self.eval_and_read_lvalue(&proj.base)?; - let val = match self.eval_and_read_lvalue(&proj.base)? { - ByRef(ptr) => self.read_value(ptr, base_ty)?, - v => v, + let pointee_type = match base_ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => ty, + _ => bug!("can only deref pointer types"), }; - match val { - ByValPair(ptr, vtable) - if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() - => { - let ptr = ptr.try_as_ptr().unwrap(); - let vtable = vtable.try_as_ptr().unwrap(); - (ptr, LvalueExtra::Vtable(vtable)) - } - - ByValPair(ptr, n) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::Length(n.expect_uint("slice length"))) - } + trace!("deref to {} on {:?}", pointee_type, val); - ByVal(ptr) if ptr.try_as_ptr().is_some() => { - let ptr = ptr.try_as_ptr().unwrap(); - (ptr, LvalueExtra::None) - } - - _ => bug!("can't deref non pointer types"), + match self.tcx.struct_tail(pointee_type).sty { + ty::TyTrait(_) => { + let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; + (ptr, LvalueExtra::Vtable(vtable)) + }, + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.expect_slice(&self.memory)?; + (ptr, LvalueExtra::Length(len)) + }, + _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 280cabc078593..bc258574665d1 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -124,15 +124,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "drop_in_place" => { let ty = substs.type_at(0); + trace!("drop in place on {}", ty); let ptr_ty = self.tcx.mk_mut_ptr(ty); let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(ptr) => Lvalue::from_ptr(ptr.expect_ptr("drop_in_place first arg not a pointer")), + Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()), Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.expect_ptr("drop_in_place first arg not a pointer"), - extra: match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("either pointer or not, but not neither")), + ptr: ptr.to_ptr(), + extra: match self.tcx.struct_tail(ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ptr_ty), }, }, }; @@ -440,7 +442,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; - let len = value.expect_slice_len(&self.memory)?; + let (_, len) = value.expect_slice(&self.memory)?; let align = self.type_align(elem_ty); Ok((len * elem_size, align as u64)) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 655a59902e9ee..fbd9e76a07f24 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -85,8 +85,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let fn_ptr = self.eval_operand_to_primval(func)? - .expect_fn_ptr("TyFnPtr callee did not evaluate to FnPtr"); + let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); @@ -542,14 +541,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), Value::ByVal(ptr) => { assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.expect_ptr("value of Box type must be a pointer"); + let contents_ptr = ptr.to_ptr(); self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; }, Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.expect_ptr("value of Box type must be a pointer"); - let extra = match extra.try_as_ptr() { - Some(vtable) => LvalueExtra::Vtable(vtable), - None => LvalueExtra::Length(extra.expect_uint("slice length")), + let ptr = prim_ptr.to_ptr(); + let extra = match self.tcx.struct_tail(contents_ty).sty { + ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + _ => bug!("invalid fat pointer type: {}", ty), }; self.drop( Lvalue::Ptr { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index d9285a41e41ab..fa89d02ad77d1 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -22,10 +22,7 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - - ByVal(ptr) | ByValPair(ptr, _) => { - Ok(ptr.try_as_ptr().expect("unimplemented: `read_ptr` on non-ptr primval")) - } + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.to_ptr()), } } @@ -35,29 +32,29 @@ impl<'a, 'tcx: 'a> Value { ) -> EvalResult<'tcx, (Pointer, Pointer)> { use self::Value::*; match *self { - ByRef(ptr) => { - let ptr = mem.read_ptr(ptr)?; - let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?; + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size() as isize))?; Ok((ptr, vtable)) } - ByValPair(ptr, vtable) - if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some() - => { - let ptr = ptr.try_as_ptr().unwrap(); - let vtable = vtable.try_as_ptr().unwrap(); - Ok((ptr, vtable)) - } + ByValPair(ptr, vtable) => Ok((ptr.to_ptr(), vtable.to_ptr())), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice_len(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByValPair(_, val) if val.kind.is_int() => Ok(val.bits), + ByRef(ref_ptr) => { + let ptr = mem.read_ptr(ref_ptr)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size() as isize))?; + Ok((ptr, len)) + }, + ByValPair(ptr, val) => { + Ok((ptr.to_ptr(), val.try_as_uint()?)) + }, _ => unimplemented!(), } } diff --git a/src/memory.rs b/src/memory.rs index 412eb62e9aefe..348437f50b646 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -533,8 +533,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - if let Some(ptr) = val.try_as_ptr() { - return self.write_ptr(dest, ptr); + if let Some(alloc_id) = val.relocation { + return self.write_ptr(dest, Pointer::new(alloc_id, val.bits as usize)); } use primval::PrimValKind::*; diff --git a/src/primval.rs b/src/primval.rs index 2d8b50076234f..15cc88ca4cf9e 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -137,15 +137,19 @@ impl PrimVal { bits_to_f64(self.bits) } - pub fn try_as_ptr(self) -> Option { + pub fn to_ptr(self) -> Pointer { self.relocation.map(|alloc_id| { Pointer::new(alloc_id, self.bits as usize) - }) + }).unwrap_or_else(|| Pointer::from_int(self.bits as usize)) + } + + pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { + self.to_ptr().to_int().map(|val| val as u64) } pub fn expect_uint(self, error_msg: &str) -> u64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as u64 + if let Ok(int) = self.try_as_uint() { + return int; } use self::PrimValKind::*; @@ -156,8 +160,8 @@ impl PrimVal { } pub fn expect_int(self, error_msg: &str) -> i64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as i64 + if let Ok(int) = self.try_as_uint() { + return int as i64; } use self::PrimValKind::*; @@ -188,15 +192,6 @@ impl PrimVal { _ => bug!("{}", error_msg), } } - - pub fn expect_ptr(self, error_msg: &str) -> Pointer { - self.try_as_ptr().expect(error_msg) - } - - /// FIXME(solson): Refactored into a duplicate of `expect_ptr`. Investigate removal. - pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer { - self.try_as_ptr().expect(error_msg) - } } //////////////////////////////////////////////////////////////////////////////// @@ -277,19 +272,13 @@ pub fn binary_op<'tcx>( use rustc::mir::BinOp::*; use self::PrimValKind::*; - match (left.try_as_ptr(), right.try_as_ptr()) { - (Some(left_ptr), Some(right_ptr)) => { - if left_ptr.alloc_id != right_ptr.alloc_id { - return Ok((unrelated_ptr_ops(bin_op)?, false)); - } - - // If the pointers are into the same allocation, fall through to the more general match - // later, which will do comparisons on the `bits` fields, which are the pointer offsets - // in this case. - } - - (None, None) => {} - _ => return Err(EvalError::ReadPointerAsBytes), + // If the pointers are into the same allocation, fall through to the more general match + // later, which will do comparisons on the `bits` fields, which are the pointer offsets + // in this case. + let left_ptr = left.to_ptr(); + let right_ptr = right.to_ptr(); + if left_ptr.alloc_id != right_ptr.alloc_id { + return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } let (l, r) = (left.bits, right.bits); @@ -376,12 +365,15 @@ pub fn binary_op<'tcx>( Ok((val, false)) } -fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> { +fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { use rustc::mir::BinOp::*; match bin_op { Eq => Ok(PrimVal::from_bool(false)), Ne => Ok(PrimVal::from_bool(true)), Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), + _ if left.to_int().is_ok() ^ right.to_int().is_ok() => { + Err(EvalError::ReadPointerAsBytes) + }, _ => bug!(), } } diff --git a/tests/compile-fail/cast_int_to_fn_ptr.rs b/tests/compile-fail/cast_int_to_fn_ptr.rs new file mode 100644 index 0000000000000..dc39f7dda1b63 --- /dev/null +++ b/tests/compile-fail/cast_int_to_fn_ptr.rs @@ -0,0 +1,7 @@ +fn main() { + let g = unsafe { + std::mem::transmute::(42) + }; + + g(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer +} From 13f22f8344f2ac149ecb5d6d817f208e99926ba1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 14:33:07 +0100 Subject: [PATCH 0643/1096] print traces only when not running on the rust run-pass test suite (since tracing is slow) --- tests/compiletest.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index e8f43bbbe019a..025ec660d2e8f 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -60,6 +60,8 @@ fn compile_test() { let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { Box::new(files.chain(std::fs::read_dir(path).unwrap())) } else { + // print traces only when not running on the rust run-pass test suite (since tracing is slow) + std::env::set_var("MIRI_LOG", "trace"); Box::new(files) }; let mut mir_not_found = 0; From f77a0ab10bbe2dac1ad7733c0cd63729ceb0ed15 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:18:49 +0100 Subject: [PATCH 0644/1096] fix writing int->ptr transmuted primvals to memory --- src/memory.rs | 3 +- tests/run-pass/binops.rs | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/binops.rs diff --git a/src/memory.rs b/src/memory.rs index 348437f50b646..8bb39d5167f34 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -543,7 +543,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { I16 | U16 => (2, val.bits as u16 as u64), I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), I64 | U64 | F64 => (8, val.bits), - FnPtr | Ptr => bug!("handled above"), + // int -> ptr transmutes are handled here + FnPtr | Ptr => return self.write_usize(dest, val.bits), }; self.write_uint(dest, bits, size) diff --git a/tests/run-pass/binops.rs b/tests/run-pass/binops.rs new file mode 100644 index 0000000000000..9466f115b1a50 --- /dev/null +++ b/tests/run-pass/binops.rs @@ -0,0 +1,96 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Binop corner cases + +fn test_nil() { + assert_eq!((), ()); + assert!((!(() != ()))); + assert!((!(() < ()))); + assert!((() <= ())); + assert!((!(() > ()))); + assert!((() >= ())); +} + +fn test_bool() { + assert!((!(true < false))); + assert!((!(true <= false))); + assert!((true > false)); + assert!((true >= false)); + + assert!((false < true)); + assert!((false <= true)); + assert!((!(false > true))); + assert!((!(false >= true))); + + // Bools support bitwise binops + assert_eq!(false & false, false); + assert_eq!(true & false, false); + assert_eq!(true & true, true); + assert_eq!(false | false, false); + assert_eq!(true | false, true); + assert_eq!(true | true, true); + assert_eq!(false ^ false, false); + assert_eq!(true ^ false, true); + assert_eq!(true ^ true, false); +} + +fn test_ptr() { + unsafe { + let p1: *const u8 = ::std::mem::transmute(0_usize); + let p2: *const u8 = ::std::mem::transmute(0_usize); + let p3: *const u8 = ::std::mem::transmute(1_usize); + + assert_eq!(p1, p2); + assert!(p1 != p3); + assert!(p1 < p3); + assert!(p1 <= p3); + assert!(p3 > p1); + assert!(p3 >= p3); + assert!(p1 <= p2); + assert!(p1 >= p2); + } +} + +#[derive(PartialEq, Debug)] +struct P { + x: isize, + y: isize, +} + +fn p(x: isize, y: isize) -> P { + P { + x: x, + y: y + } +} + +fn test_class() { + let q = p(1, 2); + let mut r = p(1, 2); + + unsafe { + println!("q = {:x}, r = {:x}", + (::std::mem::transmute::<*const P, usize>(&q)), + (::std::mem::transmute::<*const P, usize>(&r))); + } + assert_eq!(q, r); + r.y = 17; + assert!((r.y != q.y)); + assert_eq!(r.y, 17); + assert!((q != r)); +} + +pub fn main() { + test_nil(); + test_bool(); + test_ptr(); + test_class(); +} From e2091ff93481c5e1c41da26ea202c911c1140a0c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:19:38 +0100 Subject: [PATCH 0645/1096] add more atomic intrinsics --- src/interpreter/terminator/intrinsics.rs | 48 ++++++++++++++++++++++++ tests/run-pass/sendable-class.rs | 34 +++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/run-pass/sendable-class.rs diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index bc258574665d1..5cea0bc2d0c70 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_load" | + "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; @@ -74,6 +75,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // we are inherently singlethreaded and singlecored, this is a nop } + "atomic_xchg" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let change = self.value_to_primval(arg_vals[1], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"), + }; + self.write_primval(dest, old)?; + self.write_primval(Lvalue::from_ptr(ptr), change)?; + } + + "atomic_cxchg" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let expect_old = self.value_to_primval(arg_vals[1], ty)?; + let change = self.value_to_primval(arg_vals[2], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), + }; + let (val, _) = primval::binary_op(mir::BinOp::Eq, old, expect_old)?; + let dest = self.force_allocation(dest)?.to_ptr(); + self.write_pair_to_ptr(old, val, dest, dest_ty)?; + self.write_primval(Lvalue::from_ptr(ptr), change)?; + } + + "atomic_xadd_relaxed" => { + let ty = substs.type_at(0); + let ptr = arg_vals[0].read_ptr(&self.memory)?; + let change = self.value_to_primval(arg_vals[1], ty)?; + let old = self.read_value(ptr, ty)?; + let old = match old { + Value::ByVal(val) => val, + Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"), + }; + self.write_primval(dest, old)?; + // FIXME: what do atomics do on overflow? + let (val, _) = primval::binary_op(mir::BinOp::Add, old, change)?; + self.write_primval(Lvalue::from_ptr(ptr), val)?; + }, + "atomic_xsub_rel" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; diff --git a/tests/run-pass/sendable-class.rs b/tests/run-pass/sendable-class.rs new file mode 100644 index 0000000000000..b3e07d00f010f --- /dev/null +++ b/tests/run-pass/sendable-class.rs @@ -0,0 +1,34 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that a class with only sendable fields can be sent + +// pretty-expanded FIXME #23616 + +use std::sync::mpsc::channel; + +#[allow(dead_code)] +struct Foo { + i: isize, + j: char, +} + +fn foo(i:isize, j: char) -> Foo { + Foo { + i: i, + j: j + } +} + +pub fn main() { + let (tx, rx) = channel(); + let _ = tx.send(foo(42, 'c')); + let _ = rx; +} From 1549c2d51e128825f6ecd92ae1cd88dd4a0ae02b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:20:10 +0100 Subject: [PATCH 0646/1096] erase all lifetimes from function types before creating pointers to them --- src/interpreter/mod.rs | 3 +++ src/interpreter/vtable.rs | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 791466f6908bf..a431a7111a1a5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -654,6 +654,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { + let fn_ty = self.tcx.erase_regions(&fn_ty); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, @@ -665,6 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; + let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, @@ -1390,6 +1392,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), ty::TyFnDef(def_id, substs, fn_ty) => { + let fn_ty = self.tcx.erase_regions(&fn_ty); PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 7320872724015..9892da0bdb5b5 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -35,7 +35,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, mth.method.fty) + let fn_ty = self.tcx.erase_regions(&mth.method.fty); + self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -90,7 +91,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(drop_def_id) = adt_def.destructor() { let ty_scheme = self.tcx.lookup_item_type(drop_def_id); let fn_ty = match ty_scheme.ty.sty { - ty::TyFnDef(_, _, fn_ty) => fn_ty, + ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; let fn_ptr = self.memory.create_fn_ptr(drop_def_id, substs, fn_ty); From 4748587a7743f297ff7550b51dc7dfe4ef7c67ee Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:23:19 +0100 Subject: [PATCH 0647/1096] fix creation of simd types --- src/interpreter/mod.rs | 7 ++++ .../simd-intrinsic-generic-elements.rs | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/run-pass/simd-intrinsic-generic-elements.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a431a7111a1a5..3c4900c054fbd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -570,6 +570,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + Vector { element, count } => { + let elem_size = element.size(&self.tcx.data_layout).bytes(); + debug_assert_eq!(count, operands.len() as u64); + let offsets = (0..).map(|i| i * elem_size); + self.assign_fields(dest, offsets, operands)?; + } + _ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))), } } diff --git a/tests/run-pass/simd-intrinsic-generic-elements.rs b/tests/run-pass/simd-intrinsic-generic-elements.rs new file mode 100644 index 0000000000000..36567f4c03310 --- /dev/null +++ b/tests/run-pass/simd-intrinsic-generic-elements.rs @@ -0,0 +1,42 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(repr_simd, platform_intrinsics)] + +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x2(i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x3(i32, i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x4(i32, i32, i32, i32); +#[repr(simd)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[allow(non_camel_case_types)] +struct i32x8(i32, i32, i32, i32, + i32, i32, i32, i32); + +fn main() { + let _x2 = i32x2(20, 21); + let _x3 = i32x3(30, 31, 32); + let _x4 = i32x4(40, 41, 42, 43); + let _x8 = i32x8(80, 81, 82, 83, 84, 85, 86, 87); + + let _y2 = i32x2(120, 121); + let _y3 = i32x3(130, 131, 132); + let _y4 = i32x4(140, 141, 142, 143); + let _y8 = i32x8(180, 181, 182, 183, 184, 185, 186, 187); + +} From 5ee75c0805be98f89ac4edd6101a0960add75108 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 15:24:22 +0100 Subject: [PATCH 0648/1096] don't print in the binop tests --- tests/run-pass/binops.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/run-pass/binops.rs b/tests/run-pass/binops.rs index 9466f115b1a50..a03b96fa499fd 100644 --- a/tests/run-pass/binops.rs +++ b/tests/run-pass/binops.rs @@ -76,11 +76,6 @@ fn test_class() { let q = p(1, 2); let mut r = p(1, 2); - unsafe { - println!("q = {:x}, r = {:x}", - (::std::mem::transmute::<*const P, usize>(&q)), - (::std::mem::transmute::<*const P, usize>(&r))); - } assert_eq!(q, r); r.y = 17; assert!((r.y != q.y)); From 1c5c6cd078a12beba06c5eb70bd9f05ca27836b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 16:15:17 +0100 Subject: [PATCH 0649/1096] allow zsts in the zero case of a nullable pointer optimized enum --- src/interpreter/mod.rs | 6 ++++- .../enum-nullable-const-null-with-fields.rs | 22 +++++++++++++++++++ tests/run-pass/unique-send.rs | 20 +++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/enum-nullable-const-null-with-fields.rs create mode 100644 tests/run-pass/unique-send.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3c4900c054fbd..6697dcd60e65b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -518,7 +518,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value_ty = self.operand_ty(operand); self.write_value(value, dest, value_ty)?; } else { - assert_eq!(operands.len(), 0); + if let Some(operand) = operands.get(0) { + assert_eq!(operands.len(), 1); + let operand_ty = self.operand_ty(operand); + assert_eq!(self.type_size(operand_ty), Some(0)); + } let value_size = self.type_size(dest_ty).expect("pointer types are sized"); let zero = PrimVal::from_int_with_size(0, value_size); self.write_primval(dest, zero)?; diff --git a/tests/run-pass/enum-nullable-const-null-with-fields.rs b/tests/run-pass/enum-nullable-const-null-with-fields.rs new file mode 100644 index 0000000000000..1342c4e104de5 --- /dev/null +++ b/tests/run-pass/enum-nullable-const-null-with-fields.rs @@ -0,0 +1,22 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use std::result::Result; +use std::result::Result::Ok; + +static C: Result<(), Box> = Ok(()); + +// This is because of yet another bad assertion (ICE) about the null side of a nullable enum. +// So we won't actually compile if the bug is present, but we check the value in main anyway. + +pub fn main() { + assert!(C.is_ok()); +} diff --git a/tests/run-pass/unique-send.rs b/tests/run-pass/unique-send.rs new file mode 100644 index 0000000000000..7644da08e4afa --- /dev/null +++ b/tests/run-pass/unique-send.rs @@ -0,0 +1,20 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +use std::sync::mpsc::channel; + +pub fn main() { + let (tx, rx) = channel::>(); + tx.send(box 100).unwrap(); + let v = rx.recv().unwrap(); + assert_eq!(v, box 100); +} From 64155ffd102ba739008a903afd82bb7aeb4c3607 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 16:32:21 +0100 Subject: [PATCH 0650/1096] implement fn item -> trait object conversion --- src/interpreter/vtable.rs | 11 +++++++---- tests/run-pass/issue-30530.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/run-pass/issue-30530.rs diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 9892da0bdb5b5..201bedc1a862e 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -58,11 +58,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } traits::VtableFnPointer( traits::VtableFnPointerData { - fn_ty: _bare_fn_ty, + fn_ty, nested: _ }) => { - let _trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_ref.def_id()).unwrap(); - //vec![trans_fn_pointer_shim(ccx, trait_closure_kind, bare_fn_ty)].into_iter() - unimplemented!() + match fn_ty.sty { + ty::TyFnDef(did, substs, bare_fn_ty) => { + vec![Some(self.memory.create_fn_ptr(did, substs, bare_fn_ty))].into_iter() + }, + _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), + } } traits::VtableObject(ref data) => { // this would imply that the Self type being erased is diff --git a/tests/run-pass/issue-30530.rs b/tests/run-pass/issue-30530.rs new file mode 100644 index 0000000000000..d5139c908bdac --- /dev/null +++ b/tests/run-pass/issue-30530.rs @@ -0,0 +1,35 @@ +// Copyright 2012-2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for Issue #30530: alloca's created for storing +// intermediate scratch values during brace-less match arms need to be +// initialized with their drop-flag set to "dropped" (or else we end +// up running the destructors on garbage data at the end of the +// function). + +pub enum Handler { + Default, + #[allow(dead_code)] + Custom(*mut Box), +} + +fn main() { + take(Handler::Default, Box::new(main)); +} + +#[inline(never)] +pub fn take(h: Handler, f: Box) -> Box { + unsafe { + match h { + Handler::Custom(ptr) => *Box::from_raw(ptr), + Handler::Default => f, + } + } +} From fd68670c0a65dc6b972b336d449ca60547a01355 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 15 Nov 2016 17:19:37 +0100 Subject: [PATCH 0651/1096] merge closures and function and implement some closure vtable cases --- src/error.rs | 9 +-- src/interpreter/mod.rs | 9 ++- src/interpreter/terminator/mod.rs | 16 +++-- src/interpreter/vtable.rs | 15 ++--- src/memory.rs | 76 +++++++++--------------- tests/compile-fail/cast_fn_ptr.rs | 2 +- tests/run-pass/last-use-in-cap-clause.rs | 25 ++++++++ 7 files changed, 75 insertions(+), 77 deletions(-) create mode 100644 tests/run-pass/last-use-in-cap-clause.rs diff --git a/src/error.rs b/src/error.rs index 8001d95941e84..481815996a0e1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,15 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::{BareFnTy, Ty}; +use rustc::ty::{BareFnTy, Ty, FnSig}; +use syntax::abi::Abi; use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { - FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), + FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), DanglingPointerDeref, InvalidMemoryAccess, @@ -123,8 +124,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) }, EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), - EvalError::FunctionPointerTyMismatch(expected, got) => - write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), + EvalError::FunctionPointerTyMismatch(abi, sig, got) => + write!(f, "tried to call a function with abi {:?} and sig {:?} through a function pointer of type {:?}", abi, sig, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6697dcd60e65b..ee503f97d78ea 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -666,7 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, fn_ty) => { let fn_ty = self.tcx.erase_regions(&fn_ty); - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -676,9 +676,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(unsafe_fn_ty) => { let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; - let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; + let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), @@ -1403,8 +1403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), ty::TyFnDef(def_id, substs, fn_ty) => { - let fn_ty = self.tcx.erase_regions(&fn_ty); - PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) + PrimVal::from_fn_ptr(self.memory.create_fn_ptr(self.tcx, def_id, substs, fn_ty)) }, ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, ty::TyBox(ty) | diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index fbd9e76a07f24..ccf7671eec23e 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -86,9 +86,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); - let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; - if fn_ty != bare_fn_ty { - return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty)); + let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + if abi != bare_fn_ty.abi || sig != bare_fn_ty.sig.skip_binder() { + return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); } self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, terminator.source_info.span)? @@ -500,9 +500,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; - let (def_id, substs, ty) = self.memory.get_fn(fn_ptr.alloc_id)?; - // FIXME: skip_binder is wrong for HKL - *first_ty = ty.sig.skip_binder().inputs[0]; + let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + *first_ty = sig.inputs[0]; Ok((def_id, substs)) } else { Err(EvalError::VtableForArgumentlessMethod) @@ -643,9 +642,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { - let (def_id, substs, ty) = self.memory.get_fn(drop_fn.alloc_id)?; - let fn_sig = self.tcx.erase_late_bound_regions_and_normalize(&ty.sig); - let real_ty = fn_sig.inputs[0]; + let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; + let real_ty = sig.inputs[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); } else { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 201bedc1a862e..6651194be7d1a 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -36,7 +36,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .into_iter() .map(|opt_mth| opt_mth.map(|mth| { let fn_ty = self.tcx.erase_regions(&mth.method.fty); - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) + self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -47,14 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs, nested: _ }) => { let closure_type = self.tcx.closure_type(closure_def_id, substs); - let fn_ty = ty::BareFnTy { - unsafety: closure_type.unsafety, - abi: closure_type.abi, - sig: closure_type.sig, - }; - let _fn_ty = self.tcx.mk_bare_fn(fn_ty); - unimplemented!() - //vec![Some(self.memory.create_fn_ptr(closure_def_id, substs.func_substs, fn_ty))].into_iter() + vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter() } traits::VtableFnPointer( traits::VtableFnPointerData { @@ -62,7 +55,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { nested: _ }) => { match fn_ty.sty { ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_ptr(did, substs, bare_fn_ty))].into_iter() + vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter() }, _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), } @@ -97,7 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; - let fn_ptr = self.memory.create_fn_ptr(drop_def_id, substs, fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx, drop_def_id, substs, fn_ty); self.memory.write_ptr(vtable, fn_ptr)?; } } diff --git a/src/memory.rs b/src/memory.rs index 8bb39d5167f34..fce0ff4c976a4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,10 +4,12 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr}; use rustc::hir::def_id::DefId; -use rustc::ty::{BareFnTy, ClosureTy, ClosureSubsts}; +use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt}; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; +use syntax::abi::Abi; + use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -88,19 +90,9 @@ impl Pointer { #[derive(Debug, Clone, Hash, Eq, PartialEq)] struct FunctionDefinition<'tcx> { pub def_id: DefId, - pub kind: FunctionKind<'tcx>, -} - -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -enum FunctionKind<'tcx> { - Closure { - substs: ClosureSubsts<'tcx>, - ty: ClosureTy<'tcx>, - }, - Function { - substs: &'tcx Substs<'tcx>, - ty: &'tcx BareFnTy<'tcx>, - } + pub substs: &'tcx Substs<'tcx>, + pub abi: Abi, + pub sig: &'tcx ty::FnSig<'tcx>, } //////////////////////////////////////////////////////////////////////////////// @@ -143,23 +135,31 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + pub fn create_closure_ptr(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + // FIXME: this is a hack + let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: fn_ty.unsafety, + abi: fn_ty.abi, + sig: fn_ty.sig, + }); self.create_fn_alloc(FunctionDefinition { def_id: def_id, - kind: FunctionKind::Closure { - substs: substs, - ty: fn_ty, - } + substs: substs.func_substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), }) } - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { self.create_fn_alloc(FunctionDefinition { def_id: def_id, - kind: FunctionKind::Function { - substs: substs, - ty: fn_ty, - } + substs: substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), }) } @@ -308,33 +308,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_closure(&self, id: AllocId) -> EvalResult<'tcx, (DefId, ClosureSubsts<'tcx>, ClosureTy<'tcx>)> { - debug!("reading closure fn ptr: {}", id); - match self.functions.get(&id) { - Some(&FunctionDefinition { - def_id, - kind: FunctionKind::Closure { ref substs, ref ty } - }) => Ok((def_id, *substs, ty.clone())), - Some(&FunctionDefinition { - kind: FunctionKind::Function { .. }, .. - }) => Err(EvalError::CalledClosureAsFunction), - None => match self.alloc_map.get(&id) { - Some(_) => Err(EvalError::ExecuteMemory), - None => Err(EvalError::InvalidFunctionPointer), - } - } - } - - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, &'tcx BareFnTy<'tcx>)> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Abi, &'tcx ty::FnSig<'tcx>)> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { Some(&FunctionDefinition { def_id, - kind: FunctionKind::Function { substs, ty } - }) => Ok((def_id, substs, ty)), - Some(&FunctionDefinition { - kind: FunctionKind::Closure { .. }, .. - }) => Err(EvalError::CalledClosureAsFunction), + substs, + abi, + sig, + }) => Ok((def_id, substs, abi, sig)), None => match self.alloc_map.get(&id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index e9b2536a7005a..c8070913f1cb5 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -5,5 +5,5 @@ fn main() { std::mem::transmute::(f) }; - g(42) //~ ERROR tried to call a function of type + g(42) //~ ERROR tried to call a function with abi Rust and sig } diff --git a/tests/run-pass/last-use-in-cap-clause.rs b/tests/run-pass/last-use-in-cap-clause.rs new file mode 100644 index 0000000000000..de2d815ca54eb --- /dev/null +++ b/tests/run-pass/last-use-in-cap-clause.rs @@ -0,0 +1,25 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure #1399 stays fixed + +#[allow(dead_code)] +struct A { a: Box } + +fn foo() -> Box isize + 'static> { + let k: Box<_> = Box::new(22); + let _u = A {a: k.clone()}; + let result = || 22; + Box::new(result) +} + +pub fn main() { + assert_eq!(foo()(), 22); +} From a5aafbdfbf096d230e4356f777c7ce75d29ce222 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 11:31:28 +0100 Subject: [PATCH 0652/1096] rustup --- src/interpreter/terminator/mod.rs | 9 ++++---- src/interpreter/vtable.rs | 37 ++++++++++++++++--------------- src/memory.rs | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index ccf7671eec23e..a80ac216aa23f 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -5,7 +5,6 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use std::rc::Rc; use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; @@ -479,7 +478,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), } - Ok((vtable_closure.closure_def_id, vtable_closure.substs.func_substs)) + Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs)) } traits::VtableFnPointer(vtable_fn_ptr) => { @@ -715,7 +714,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { #[derive(Debug)] pub(super) struct ImplMethod<'tcx> { - pub(super) method: Rc>, + pub(super) method: ty::AssociatedItem, pub(super) substs: &'tcx Substs<'tcx>, pub(super) is_provided: bool, } @@ -733,7 +732,7 @@ pub(super) fn get_impl_method<'a, 'tcx>( let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); - match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { Some(node_item) => { let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); @@ -770,7 +769,7 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let trait_def = tcx.lookup_trait_def(trait_def_id); - match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { Some(node_item) => { let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 6651194be7d1a..181dafa77d9c8 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::traits::{self, Reveal, SelectionContext}; -use rustc::ty::subst::{Substs, Subst}; +use rustc::ty::subst::Substs; use rustc::ty; use super::EvalContext; @@ -35,7 +35,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { - let fn_ty = self.tcx.erase_regions(&mth.method.fty); + let fn_ty = self.tcx.item_type(mth.method.def_id); + let fn_ty = match fn_ty.sty { + ty::TyFnDef(_, _, fn_ty) => fn_ty, + _ => bug!("bad function type: {}", fn_ty), + }; + let fn_ty = self.tcx.erase_regions(&fn_ty); self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty) })) .collect::>() @@ -85,8 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_usize(vtable, 0)?; if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { if let Some(drop_def_id) = adt_def.destructor() { - let ty_scheme = self.tcx.lookup_item_type(drop_def_id); - let fn_ty = match ty_scheme.ty.sty { + let fn_ty = match self.tcx.item_type(drop_def_id).sty { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; @@ -120,18 +124,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.populate_implementations_for_trait_if_necessary(trait_id); - let trait_item_def_ids = self.tcx.impl_or_trait_items(trait_id); - trait_item_def_ids - .iter() - + self.tcx + .associated_items(trait_id) // Filter out non-method items. - .filter_map(|&trait_method_def_id| { - let trait_method_type = match self.tcx.impl_or_trait_item(trait_method_def_id) { - ty::MethodTraitItem(trait_method_type) => trait_method_type, - _ => return None, - }; - debug!("get_vtable_methods: trait_method_def_id={:?}", - trait_method_def_id); + .filter_map(|trait_method_type| { + if trait_method_type.kind != ty::AssociatedKind::Method { + return None; + } + debug!("get_vtable_methods: trait_method_type={:?}", + trait_method_type); let name = trait_method_type.name; @@ -146,7 +147,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // the method may have some early-bound lifetimes, add // regions for those - let method_substs = Substs::for_item(self.tcx, trait_method_def_id, + let method_substs = Substs::for_item(self.tcx, trait_method_type.def_id, |_, _| self.tcx.mk_region(ty::ReErased), |_, _| self.tcx.types.err); @@ -162,8 +163,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // method could then never be called, so we do not want to // try and trans it, in that case. Issue #23435. if mth.is_provided { - let predicates = mth.method.predicates.predicates.subst(self.tcx, mth.substs); - if !self.normalize_and_test_predicates(predicates) { + let predicates = self.tcx.item_predicates(trait_method_type.def_id).instantiate_own(self.tcx, mth.substs); + if !self.normalize_and_test_predicates(predicates.predicates) { debug!("get_vtable_methods: predicates do not hold"); return Some(None); } diff --git a/src/memory.rs b/src/memory.rs index fce0ff4c976a4..6670ed66cf02e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }); self.create_fn_alloc(FunctionDefinition { def_id: def_id, - substs: substs.func_substs, + substs: substs.substs, abi: fn_ty.abi, // FIXME: why doesn't this compile? //sig: tcx.erase_late_bound_regions(&fn_ty.sig), From 4ebf7bfea6eeecab4ec605abfa01cc61af53f95e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 11:31:53 +0100 Subject: [PATCH 0653/1096] rustup allows one to install the rust source, that's obviously not a target --- tests/compiletest.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 025ec660d2e8f..181f06ba1ebd2 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -34,8 +34,9 @@ fn for_all_targets(sysroot: &str, mut f: F) { continue; } let target = target.file_name().into_string().unwrap(); - if target == "etc" { - continue; + match &*target { + "etc" | "src" => continue, + _ => {}, } let stderr = std::io::stderr(); writeln!(stderr.lock(), "running tests for target {}", target).unwrap(); From 51ff9fdaf684c89c12ac5bf41980a53eed44ee2d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 14:48:34 +0100 Subject: [PATCH 0654/1096] deallocate all locals on function exit and transitively freeze constants through pointers --- src/error.rs | 8 ++++- src/interpreter/mod.rs | 43 +++++++++++++++++++++-- src/interpreter/step.rs | 5 +-- src/interpreter/terminator/mod.rs | 7 ++-- src/memory.rs | 23 +++++++++++- tests/compile-fail/modifying_constants.rs | 6 ++++ tests/run-pass/move-arg-3-unique.rs | 18 ++++++++++ 7 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 tests/compile-fail/modifying_constants.rs create mode 100644 tests/run-pass/move-arg-3-unique.rs diff --git a/src/error.rs b/src/error.rs index 481815996a0e1..0fd35ff656526 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,7 @@ use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum EvalError<'tcx> { FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), @@ -48,6 +48,8 @@ pub enum EvalError<'tcx> { AssumptionNotHeld, InlineAsm, TypeNotPrimitive(Ty<'tcx>), + ReallocatedFrozenMemory, + DeallocatedFrozenMemory, } pub type EvalResult<'tcx, T> = Result>; @@ -110,6 +112,10 @@ impl<'tcx> Error for EvalError<'tcx> { "cannot evaluate inline assembly", EvalError::TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", + EvalError::ReallocatedFrozenMemory => + "tried to reallocate frozen memory", + EvalError::DeallocatedFrozenMemory => + "tried to deallocate frozen memory", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ee503f97d78ea..dbc6d7bfb8344 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -364,6 +364,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let global_value = self.globals .get_mut(&id) .expect("global should have been cached (freeze)"); + match global_value.data.expect("global should have been initialized") { + Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?, + Value::ByVal(val) => if let Some(alloc_id) = val.relocation { + self.memory.freeze(alloc_id)?; + }, + Value::ByValPair(a, b) => { + if let Some(alloc_id) = a.relocation { + self.memory.freeze(alloc_id)?; + } + if let Some(alloc_id) = b.relocation { + self.memory.freeze(alloc_id)?; + } + }, + } if let Value::ByRef(ptr) = global_value.data.expect("global should have been initialized") { self.memory.freeze(ptr.alloc_id)?; } @@ -375,7 +389,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, } - // TODO(solson): Deallocate local variables. + // check that all locals have been deallocated through StorageDead + for (i, local) in frame.locals.into_iter().enumerate() { + if let Some(Value::ByRef(ptr)) = local { + trace!("deallocating local {}: {:?}", i + 1, ptr); + self.memory.dump(ptr.alloc_id); + match self.memory.deallocate(ptr) { + Ok(()) | Err(EvalError::DeallocatedFrozenMemory) => {}, + other => return other, + } + } + } Ok(()) } @@ -729,6 +753,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Skip the initial 0 intended for LLVM GEP. for field_index in path { let field_offset = self.get_field_offset(ty, field_index)?; + trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset); ty = self.get_field_ty(ty, field_index)?; offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); } @@ -1595,8 +1620,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { let val = self.stack[frame].get_local(local); let val = f(self, val)?; - // can't use `set_local` here, because that's only meant for going to an initialized value - self.stack[frame].locals[local.index() - 1] = val; + if let Some(val) = val { + self.stack[frame].set_local(local, val); + } else { + self.deallocate_local(frame, local)?; + } + Ok(()) + } + + pub fn deallocate_local(&mut self, frame: usize, local: mir::Local) -> EvalResult<'tcx, ()> { + if let Some(Value::ByRef(ptr)) = self.stack[frame].get_local(local) { + self.memory.deallocate(ptr)?; + } + // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. + self.stack[frame].locals[local.index() - 1] = None; Ok(()) } } diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 4c03b3c42aa1d..90564cd2fcc91 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -81,8 +81,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, SetDiscriminant { .. } => unimplemented!(), - // Miri can safely ignore these. Only translation needs them. - StorageLive(_) | StorageDead(_) => {} + // Miri can safely ignore these. Only translation needs it. + StorageLive(_) | + StorageDead(_) => {} // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index a80ac216aa23f..b0c24fc344f2a 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { + pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { let span = self.frame().span; // add them to the stack in reverse order, because the impl that needs to run the last // is the one that needs to be at the bottom of the stack @@ -249,6 +249,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty); + trace!("read_discriminant_value {:?}", adt_layout); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, signed: false, .. } => { @@ -263,12 +264,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { RawNullablePointer { nndiscr, value } => { let discr_size = value.size(&self.tcx.data_layout).bytes() as usize; + trace!("rawnullablepointer with size {}", discr_size); self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; let nonnull = adt_ptr.offset(offset.bytes() as isize); + trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? @@ -515,7 +518,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } /// push DefIds of drop impls and their argument on the given vector - fn drop( + pub fn drop( &mut self, lval: Lvalue<'tcx>, ty: Ty<'tcx>, diff --git a/src/memory.rs b/src/memory.rs index 6670ed66cf02e..4421fe23ebab8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -212,6 +212,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if ptr.points_to_zst() { return self.allocate(new_size, align); } + if self.get(ptr.alloc_id).map(|alloc| alloc.immutable) == Ok(true) { + return Err(EvalError::ReallocatedFrozenMemory); + } let size = self.get(ptr.alloc_id)?.bytes.len(); @@ -242,6 +245,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } + if self.get(ptr.alloc_id).map(|alloc| alloc.immutable) == Ok(true) { + return Err(EvalError::DeallocatedFrozenMemory); + } if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { self.memory_usage -= alloc.bytes.len(); @@ -446,9 +452,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { + // FIXME: the comment is wrong and do we really still need this check? // It's not possible to freeze the zero-sized allocation, because it doesn't exist. if alloc_id != ZST_ALLOC_ID { - self.get_mut(alloc_id)?.immutable = true; + // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a + // sub-element or have circular pointers (e.g. `Rc`-cycles) + let allocs: Vec<_> = match self.alloc_map.get_mut(&alloc_id) { + Some(ref mut alloc) if !alloc.immutable => { + alloc.immutable = true; + alloc.relocations.values().cloned().collect() + }, + None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => Vec::new(), + None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), + _ => Vec::new(), + }; + // recurse into inner allocations + for alloc in allocs { + self.freeze(alloc)?; + } } Ok(()) } diff --git a/tests/compile-fail/modifying_constants.rs b/tests/compile-fail/modifying_constants.rs new file mode 100644 index 0000000000000..5a8fd189aae7d --- /dev/null +++ b/tests/compile-fail/modifying_constants.rs @@ -0,0 +1,6 @@ +fn main() { + let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is frozen, not the pointee + let y = unsafe { &mut *(x as *const i32 as *mut i32) }; + *y = 42; //~ ERROR tried to modify constant memory + assert_eq!(*x, 42); +} diff --git a/tests/run-pass/move-arg-3-unique.rs b/tests/run-pass/move-arg-3-unique.rs new file mode 100644 index 0000000000000..2e6320eb80257 --- /dev/null +++ b/tests/run-pass/move-arg-3-unique.rs @@ -0,0 +1,18 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_features, unused_variables)] +#![feature(box_syntax)] + +pub fn main() { + let x = box 10; + let y = x; + assert_eq!(*y, 10); +} From 11a0594a1d94aa6ce39692e9dc46059a0d7c4d08 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 18 Nov 2016 10:35:41 +0100 Subject: [PATCH 0655/1096] address comments --- src/interpreter/mod.rs | 6 +++++- src/memory.rs | 38 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index dbc6d7bfb8344..8f3d06f8301fb 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -389,12 +389,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, } - // check that all locals have been deallocated through StorageDead + // deallocate all locals that are backed by an allocation for (i, local) in frame.locals.into_iter().enumerate() { if let Some(Value::ByRef(ptr)) = local { trace!("deallocating local {}: {:?}", i + 1, ptr); self.memory.dump(ptr.alloc_id); match self.memory.deallocate(ptr) { + // Any frozen memory means that it belongs to a constant or something referenced + // by a constant. We could alternatively check whether the alloc_id is frozen + // before calling deallocate, but this is much simpler and is probably the + // rare case. Ok(()) | Err(EvalError::DeallocatedFrozenMemory) => {}, other => return other, } diff --git a/src/memory.rs b/src/memory.rs index 4421fe23ebab8..960e397fd04d0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian, self}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{fmt, iter, ptr}; +use std::{fmt, iter, ptr, mem}; use rustc::hir::def_id::DefId; use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt}; @@ -452,25 +452,25 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { - // FIXME: the comment is wrong and do we really still need this check? - // It's not possible to freeze the zero-sized allocation, because it doesn't exist. - if alloc_id != ZST_ALLOC_ID { - // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a - // sub-element or have circular pointers (e.g. `Rc`-cycles) - let allocs: Vec<_> = match self.alloc_map.get_mut(&alloc_id) { - Some(ref mut alloc) if !alloc.immutable => { - alloc.immutable = true; - alloc.relocations.values().cloned().collect() - }, - None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => Vec::new(), - None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), - _ => Vec::new(), - }; - // recurse into inner allocations - for alloc in allocs { - self.freeze(alloc)?; - } + // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a + // sub-element or have circular pointers (e.g. `Rc`-cycles) + let relocations = match self.alloc_map.get_mut(&alloc_id) { + Some(ref mut alloc) if !alloc.immutable => { + alloc.immutable = true; + // take out the relocations vector to free the borrow on self, so we can call + // freeze recursively + mem::replace(&mut alloc.relocations, Default::default()) + }, + None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()), + None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), + _ => return Ok(()), + }; + // recurse into inner allocations + for &alloc in relocations.values() { + self.freeze(alloc)?; } + // put back the relocations + self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; Ok(()) } From fd6a90860c97442df45101ee15ca4637b5ab963f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 18 Nov 2016 10:36:01 +0100 Subject: [PATCH 0656/1096] simplify dumping of pointers to the zst or never alloc --- src/memory.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 960e397fd04d0..548eb78c9678e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -339,11 +339,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id); + if id == ZST_ALLOC_ID || id == NEVER_ALLOC_ID { continue; } let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); - if id == ZST_ALLOC_ID { - trace!("{} zst allocation", msg); - continue; - } let prefix_len = msg.len(); let mut relocations = vec![]; @@ -385,7 +382,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let relocation_width = (self.pointer_size() - 1) * 3; for (i, target_id) in relocations { write!(msg, "{:1$}", "", (i - pos) * 3).unwrap(); - write!(msg, "└{0:─^1$}┘ ", format!("({})", target_id), relocation_width).unwrap(); + let target = match target_id { + ZST_ALLOC_ID => String::from("zst"), + NEVER_ALLOC_ID => String::from("int ptr"), + _ => format!("({})", target_id), + }; + write!(msg, "└{0:─^1$}┘ ", target, relocation_width).unwrap(); pos = i + self.pointer_size(); } trace!("{}", msg); From b10c53031ab87fc36fd0666467f5dd37b7f2cad1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 16:01:30 +0100 Subject: [PATCH 0657/1096] fix benchmarks and add benchmarks for repeat expressions --- benches/helpers/miri_helper.rs | 14 ++++++++------ benches/helpers/repeat.rs | 4 ++++ benches/helpers/repeat_manual.rs | 7 +++++++ benches/repeat.rs | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 benches/helpers/repeat.rs create mode 100644 benches/helpers/repeat_manual.rs create mode 100644 benches/repeat.rs diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index 18c18eee6daf8..3725df24cdca7 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -33,7 +33,7 @@ pub fn run(filename: &str, bencher: &mut Bencher) { find_sysroot() ]; let compiler_calls = &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher))); - rustc_driver::run_compiler(args, compiler_calls); + rustc_driver::run_compiler(args, compiler_calls, None, None); } impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { @@ -51,13 +51,15 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - let mir_map = state.mir_map.unwrap(); - let (node_id, _) = state.session.entry_fn.borrow() + let (entry_node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); + let entry_def_id = tcx.map.local_def_id(entry_node_id); - let mut mir_map = MirMap { map: mir_map.map.clone() }; - run_mir_passes(tcx, &mut mir_map); - bencher.borrow_mut().iter(|| { eval_main(tcx, &mir_map, node_id); }); + run_mir_passes(tcx); + let memory_size = 100*1024*1024; // 100MB + let step_limit = 1000_000; + let stack_limit = 100; + bencher.borrow_mut().iter(|| { eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); }); state.session.abort_if_errors(); }); diff --git a/benches/helpers/repeat.rs b/benches/helpers/repeat.rs new file mode 100644 index 0000000000000..0e8c5980b82bd --- /dev/null +++ b/benches/helpers/repeat.rs @@ -0,0 +1,4 @@ +fn main() { + let data: [u8; 1024] = [42; 1024]; + assert_eq!(data.len(), 1024); +} diff --git a/benches/helpers/repeat_manual.rs b/benches/helpers/repeat_manual.rs new file mode 100644 index 0000000000000..6ef6f724efcee --- /dev/null +++ b/benches/helpers/repeat_manual.rs @@ -0,0 +1,7 @@ +fn main() { + let mut data: [u8; 1024] = unsafe { std::mem::uninitialized() }; + for i in 0..data.len() { + unsafe { std::ptr::write(&mut data[i], 0); } + } + assert_eq!(data.len(), 1024); +} diff --git a/benches/repeat.rs b/benches/repeat.rs new file mode 100644 index 0000000000000..f5920e83d9b07 --- /dev/null +++ b/benches/repeat.rs @@ -0,0 +1,16 @@ +#![feature(test, rustc_private)] + +extern crate test; +use test::Bencher; +mod helpers; +use helpers::*; + +#[bench] +fn repeat(bencher: &mut Bencher) { + miri_helper::run("repeat", bencher); +} + +#[bench] +fn repeat_manual(bencher: &mut Bencher) { + miri_helper::run("repeat_manual", bencher); +} From 26ccc1e4bc48e54cf39b1c762dec4252520835a5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 17:22:34 +0100 Subject: [PATCH 0658/1096] add a step counter that can be changed during interpretation --- src/interpreter/mod.rs | 15 +++++++++++---- src/interpreter/step.rs | 12 +++++++++++- tests/compile-fail/repeat2.rs | 5 +++++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 tests/compile-fail/repeat2.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8f3d06f8301fb..d5057b5fdf31a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -40,6 +40,11 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The maximum number of stack frames allowed stack_limit: usize, + + /// The maximum number of operations that may be executed. + /// This prevents infinite loops and huge computations from freezing up const eval. + /// Remove once halting problem is solved. + steps_remaining: u64, } /// A stack frame. @@ -162,13 +167,14 @@ pub enum StackPopCleanup { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: usize, stack_limit: usize) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: usize, stack_limit: usize, step_limit: u64) -> Self { EvalContext { tcx: tcx, memory: Memory::new(&tcx.data_layout, memory_size), globals: HashMap::new(), stack: Vec::new(), stack_limit: stack_limit, + steps_remaining: step_limit, } } @@ -500,6 +506,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Aggregate(ref kind, ref operands) => { + self.inc_step_counter_and_check_limit(operands.len() as u64)?; use rustc::ty::layout::Layout::*; match *dest_layout { Univariant { ref variant, .. } => { @@ -619,6 +626,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; let elem_size = self.type_size(elem_ty).expect("repeat element type must be sized"); + self.inc_step_counter_and_check_limit(length as u64)?; let value = self.eval_operand(operand)?; // FIXME(solson) @@ -1696,7 +1704,7 @@ pub fn eval_main<'a, 'tcx: 'a>( step_limit: u64, stack_limit: usize, ) { - let mut ecx = EvalContext::new(tcx, memory_size, stack_limit); + let mut ecx = EvalContext::new(tcx, memory_size, stack_limit, step_limit); let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); ecx.push_stack_frame( @@ -1708,7 +1716,7 @@ pub fn eval_main<'a, 'tcx: 'a>( StackPopCleanup::None, ).expect("could not allocate first stack frame"); - for _ in 0..step_limit { + loop { match ecx.step() { Ok(true) => {} Ok(false) => return, @@ -1718,7 +1726,6 @@ pub fn eval_main<'a, 'tcx: 'a>( } } } - report(tcx, &ecx, EvalError::ExecutionTimeLimitReached); } fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 90564cd2fcc91..1d075fe0e9ed2 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -10,7 +10,7 @@ use super::{ Global, MirRef, }; -use error::EvalResult; +use error::{EvalResult, EvalError}; use rustc::mir; use rustc::ty::{subst, self}; use rustc::hir::def_id::DefId; @@ -20,8 +20,18 @@ use std::cell::Ref; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx, ()> { + self.steps_remaining = self.steps_remaining.saturating_sub(n); + if self.steps_remaining > 0 { + Ok(()) + } else { + Err(EvalError::ExecutionTimeLimitReached) + } + } + /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { + self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); } diff --git a/tests/compile-fail/repeat2.rs b/tests/compile-fail/repeat2.rs new file mode 100644 index 0000000000000..d489342b8599c --- /dev/null +++ b/tests/compile-fail/repeat2.rs @@ -0,0 +1,5 @@ +fn main() { + let data: [u8; 1024*1024*1024] = [42; 1024*1024*1024]; + //~^ ERROR: reached the configured maximum execution time + assert_eq!(data.len(), 1024*1024*1024); +} From 986b3a07c27a78ddb380178546a69f40c2fc1f77 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 17 Nov 2016 17:23:40 +0100 Subject: [PATCH 0659/1096] layout computation can fail, make it fail with a miri error --- src/error.rs | 9 +++- src/interpreter/mod.rs | 62 ++++++++++++------------ src/interpreter/terminator/intrinsics.rs | 22 ++++----- src/interpreter/terminator/mod.rs | 25 +++++----- src/interpreter/vtable.rs | 4 +- src/memory.rs | 4 +- tests/compile-fail/repeat.rs | 5 ++ 7 files changed, 71 insertions(+), 60 deletions(-) create mode 100644 tests/compile-fail/repeat.rs diff --git a/src/error.rs b/src/error.rs index 0fd35ff656526..65cbcc9701563 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,13 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::{BareFnTy, Ty, FnSig}; +use rustc::ty::{BareFnTy, Ty, FnSig, layout}; use syntax::abi::Abi; use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] pub enum EvalError<'tcx> { FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), @@ -50,6 +50,7 @@ pub enum EvalError<'tcx> { TypeNotPrimitive(Ty<'tcx>), ReallocatedFrozenMemory, DeallocatedFrozenMemory, + Layout(layout::LayoutError<'tcx>), } pub type EvalResult<'tcx, T> = Result>; @@ -116,6 +117,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to reallocate frozen memory", EvalError::DeallocatedFrozenMemory => "tried to deallocate frozen memory", + EvalError::Layout(_) => + "rustc layout computation failed", } } @@ -146,6 +149,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { has, required), EvalError::TypeNotPrimitive(ref ty) => write!(f, "expected primitive type, got {}", ty), + EvalError::Layout(ref err) => + write!(f, "rustc layout computation failed: {:?}", err), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d5057b5fdf31a..ca443472156fa 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -188,8 +188,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: Ty<'tcx>, substs: &'tcx Substs<'tcx> ) -> EvalResult<'tcx, Pointer> { - let size = self.type_size_with_substs(ty, substs).expect("cannot alloc memory for unsized type"); - let align = self.type_align_with_substs(ty, substs); + let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type"); + let align = self.type_align_with_substs(ty, substs)?; self.memory.allocate(size, align) } @@ -292,38 +292,38 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>) -> Option { + fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { self.type_size_with_substs(ty, self.substs()) } - fn type_align(&self, ty: Ty<'tcx>) -> usize { + fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { self.type_align_with_substs(ty, self.substs()) } - fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { - let layout = self.type_layout_with_substs(ty, substs); + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { + let layout = self.type_layout_with_substs(ty, substs)?; if layout.is_unsized() { - None + Ok(None) } else { - Some(layout.size(&self.tcx.data_layout).bytes() as usize) + Ok(Some(layout.size(&self.tcx.data_layout).bytes() as usize)) } } - fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { - self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).abi() as usize + fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, usize> { + self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi() as usize) } - fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { + fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { self.type_layout_with_substs(ty, self.substs()) } - fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout { + fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { // TODO(solson): Report this error properly. - ty.layout(&infcx).unwrap() + ty.layout(&infcx).map_err(EvalError::Layout) }) } @@ -482,7 +482,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { let dest = self.eval_lvalue(lvalue)?; let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty); + let dest_layout = self.type_layout(dest_ty)?; use rustc::mir::Rvalue::*; match *rvalue { @@ -516,7 +516,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty).expect("array elements are sized") as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty)?.expect("array elements are sized") as u64, _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); @@ -556,9 +556,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(operand) = operands.get(0) { assert_eq!(operands.len(), 1); let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty), Some(0)); + assert_eq!(self.type_size(operand_ty)?, Some(0)); } - let value_size = self.type_size(dest_ty).expect("pointer types are sized"); + let value_size = self.type_size(dest_ty)?.expect("pointer types are sized"); let zero = PrimVal::from_int_with_size(0, value_size); self.write_primval(dest, zero)?; } @@ -575,7 +575,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { for operand in operands { let operand_ty = self.operand_ty(operand); - assert_eq!(self.type_size(operand_ty), Some(0)); + assert_eq!(self.type_size(operand_ty)?, Some(0)); } let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; @@ -583,7 +583,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes() as isize); - let dest_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); + let dest_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); try!(self.memory.write_int(dest, 0, dest_size)); } } else { @@ -625,8 +625,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyArray(elem_ty, n) => (elem_ty, n), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - let elem_size = self.type_size(elem_ty).expect("repeat element type must be sized"); self.inc_step_counter_and_check_limit(length as u64)?; + let elem_size = self.type_size(elem_ty)?.expect("repeat element type must be sized"); let value = self.eval_operand(operand)?; // FIXME(solson) @@ -797,7 +797,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; match *layout { @@ -816,7 +816,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; match *layout { @@ -944,7 +944,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty); + let base_layout = self.type_layout(base_ty)?; use rustc::mir::ProjectionElem::*; let (ptr, extra) = match proj.elem { @@ -1043,7 +1043,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)? @@ -1059,7 +1059,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty).expect("sequence element must be sized"); + let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized"); assert!(n >= min_length as u64); let index = if from_end { @@ -1078,7 +1078,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, _) = base.to_ptr_and_extra(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!((from as u64) <= n - (to as u64)); let ptr = base_ptr.offset(from as isize * elem_size as isize); let extra = LvalueExtra::Length(n - to as u64 - from as u64); @@ -1098,8 +1098,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { - let size = self.type_size(ty).expect("cannot copy from an unsized type"); - let align = self.type_align(ty); + let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); + let align = self.type_align(ty)?; self.memory.copy(src, dest, size, align)?; Ok(()) } @@ -1368,7 +1368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; - if let CEnum { discr, signed, .. } = *self.type_layout(ty) { + if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes() as usize; if signed { PrimValKind::from_int_size(size) @@ -1464,7 +1464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; - if let CEnum { discr, signed, .. } = *self.type_layout(ty) { + if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes() as usize; if signed { let n = self.memory.read_int(ptr, size)?; @@ -1564,7 +1564,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); - if self.type_size(dst_fty) == Some(0) { + if self.type_size(dst_fty)? == Some(0) { continue; } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 5cea0bc2d0c70..013bacc30d2ff 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -144,8 +144,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs let elem_ty = substs.type_at(0); - let elem_size = self.type_size(elem_ty).expect("cannot copy unsized value"); - let elem_align = self.type_align(elem_ty); + let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); + let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)? @@ -252,14 +252,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "min_align_of" => { let elem_ty = substs.type_at(0); - let elem_align = self.type_align(elem_ty); + let elem_align = self.type_align(elem_ty)?; let align_val = self.usize_primval(elem_align as u64); self.write_primval(dest, align_val)?; } "pref_align_of" => { let ty = substs.type_at(0); - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); let align_val = self.usize_primval(align); self.write_primval(dest, align_val)?; @@ -280,7 +280,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty).expect("cannot offset a pointer to an unsized type") as isize; + let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as isize; let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("offset second arg not isize"); @@ -335,7 +335,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") // see https://github.com/rust-lang/rust/pull/37708 - let size = self.type_size(ty).unwrap_or(!0) as u64; + let size = self.type_size(ty)?.unwrap_or(!0) as u64; let size_val = self.usize_primval(size); self.write_primval(dest, size_val)?; } @@ -414,8 +414,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { value: Value, ) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - if let Some(size) = self.type_size(ty) { - Ok((size as u64, self.type_align(ty) as u64)) + if let Some(size) = self.type_size(ty)? { + Ok((size as u64, self.type_align(ty)? as u64)) } else { match ty.sty { ty::TyAdt(def, substs) => { @@ -424,7 +424,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // and it also rounds up to alignment, which we want to avoid, // as the unsized field's alignment could be smaller. assert!(!ty.is_simd()); - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; debug!("DST {} layout: {:?}", ty, layout); let (sized_size, sized_align) = match *layout { @@ -489,9 +489,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty).expect("slice element must be sized") as u64; + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; let (_, len) = value.expect_slice(&self.memory)?; - let align = self.type_align(elem_ty); + let align = self.type_align(elem_ty)?; Ok((len * elem_size, align as u64)) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index b0c24fc344f2a..ad0e139e6e1f4 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -183,7 +183,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match fn_ty.abi { Abi::RustIntrinsic => { let ty = fn_ty.sig.0.output; - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; let (ret, target) = destination.unwrap(); self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) @@ -191,7 +191,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { let ty = fn_ty.sig.0.output; - let size = self.type_size(ty).expect("function return type cannot be unsized"); + let size = self.type_size(ty)?.expect("function return type cannot be unsized"); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, size)?; self.goto_block(target); @@ -248,7 +248,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty); + let adt_layout = self.type_layout(adt_ty)?; trace!("read_discriminant_value {:?}", adt_layout); let discr_val = match *adt_layout { @@ -273,7 +273,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let nonnull = adt_ptr.offset(offset.bytes() as isize); trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization - let discr_size = self.type_size(ty).expect("bad StructWrappedNullablePointer discrfield"); + let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? } @@ -402,9 +402,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } - fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) { + fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx, ()> { if let Some((last, last_ty)) = args.pop() { - let last_layout = self.type_layout(last_ty); + let last_layout = self.type_layout(last_ty)?; match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -421,6 +421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), } } + Ok(()) } /// Trait method, which has to be resolved to an impl method. @@ -452,7 +453,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); - self.unpack_fn_args(args); + self.unpack_fn_args(args)?; match (closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | @@ -487,7 +488,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); - self.unpack_fn_args(args); + self.unpack_fn_args(args)?; Ok((did, substs)) } else { bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) @@ -583,7 +584,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(drop_def_id) = adt_def.destructor() { drop.push((drop_def_id, Value::ByVal(PrimVal::from_ptr(adt_ptr)), substs)); } - let layout = self.type_layout(ty); + let layout = self.type_layout(ty)?; let fields = match *layout { Layout::Univariant { ref variant, .. } => { adt_def.struct_variant().fields.iter().zip(&variant.offsets) @@ -630,7 +631,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; }, ty::TyTuple(fields) => { - let offsets = match *self.type_layout(ty) { + let offsets = match *self.type_layout(ty)? { Layout::Univariant { ref variant, .. } => &variant.offsets, _ => bug!("tuples must be univariant"), }; @@ -658,7 +659,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), _ => bug!("expected an lvalue with a length"), }; - let size = self.type_size(elem_ty).expect("slice element must be sized") as isize; + let size = self.type_size(elem_ty)?.expect("slice element must be sized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { @@ -671,7 +672,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("expected an lvalue with optional extra data"), }; - let size = self.type_size(elem_ty).expect("array element cannot be unsized") as isize; + let size = self.type_size(elem_ty)?.expect("array element cannot be unsized") as isize; // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 181dafa77d9c8..920e57a1f3413 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -80,8 +80,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }).collect(); - let size = self.type_size(trait_ref.self_ty()).expect("can't create a vtable for an unsized type"); - let align = self.type_align(trait_ref.self_ty()); + let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type"); + let align = self.type_align(trait_ref.self_ty())?; let ptr_size = self.memory.pointer_size(); let vtable = self.memory.allocate(ptr_size * (3 + methods.len()), ptr_size)?; diff --git a/src/memory.rs b/src/memory.rs index 548eb78c9678e..af10ce4352ccc 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -212,7 +212,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if ptr.points_to_zst() { return self.allocate(new_size, align); } - if self.get(ptr.alloc_id).map(|alloc| alloc.immutable) == Ok(true) { + if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) { return Err(EvalError::ReallocatedFrozenMemory); } @@ -245,7 +245,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - if self.get(ptr.alloc_id).map(|alloc| alloc.immutable) == Ok(true) { + if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) { return Err(EvalError::DeallocatedFrozenMemory); } diff --git a/tests/compile-fail/repeat.rs b/tests/compile-fail/repeat.rs new file mode 100644 index 0000000000000..70d26a6859220 --- /dev/null +++ b/tests/compile-fail/repeat.rs @@ -0,0 +1,5 @@ +fn main() { + let data: [u8; std::isize::MAX as usize] = [42; std::isize::MAX as usize]; + //~^ ERROR: rustc layout computation failed: SizeOverflow([u8; + assert_eq!(data.len(), 1024); +} From e361b63fa052ee9ee09794fa358d56c41c82ed5b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 18 Nov 2016 10:39:00 +0100 Subject: [PATCH 0660/1096] remove a TODO that has been fixed in the previous commit --- src/interpreter/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ca443472156fa..d61ba4ee3f711 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -322,7 +322,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.monomorphize(ty, substs); self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { - // TODO(solson): Report this error properly. ty.layout(&infcx).map_err(EvalError::Layout) }) } From 0039ebc9400af7575a1e11693e0e74007800062c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 18 Nov 2016 12:55:14 +0100 Subject: [PATCH 0661/1096] replace most uses of `usize` with `u64` so the host architecture isn't exposed anymore --- src/bin/miri.rs | 2 +- src/error.rs | 14 +- src/interpreter/cast.rs | 2 +- src/interpreter/mod.rs | 78 ++++----- src/interpreter/terminator/intrinsics.rs | 17 +- src/interpreter/terminator/mod.rs | 38 +++-- src/interpreter/value.rs | 4 +- src/interpreter/vtable.rs | 8 +- src/memory.rs | 206 +++++++++++++---------- src/primval.rs | 12 +- 10 files changed, 207 insertions(+), 174 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index d2a9bc6087ecc..96caa54468bd9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -53,7 +53,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { NestedMetaItemKind::MetaItem(ref inner) => match inner.node { MetaItemKind::NameValue(ref name, ref value) => { match &**name { - "memory_size" => memory_size = extract_int(value) as usize, + "memory_size" => memory_size = extract_int(value), "step_limit" => step_limit = extract_int(value), "stack_limit" => stack_limit = extract_int(value) as usize, _ => state.session.span_err(item.span, "unknown miri attribute"), diff --git a/src/error.rs b/src/error.rs index 65cbcc9701563..afc1855e8e755 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,8 +18,8 @@ pub enum EvalError<'tcx> { InvalidDiscriminant, PointerOutOfBounds { ptr: Pointer, - size: usize, - allocation_size: usize, + size: u64, + allocation_size: u64, }, ReadPointerAsBytes, InvalidPointerMath, @@ -32,15 +32,15 @@ pub enum EvalError<'tcx> { Math(Span, ConstMathErr), InvalidChar(u64), OutOfMemory { - allocation_size: usize, - memory_size: usize, - memory_usage: usize, + allocation_size: u64, + memory_size: u64, + memory_usage: u64, }, ExecutionTimeLimitReached, StackFrameLimitReached, AlignmentCheckFailed { - required: usize, - has: usize, + required: u64, + has: u64, }, CalledClosureAsFunction, VtableForArgumentlessMethod, diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 217a10a4c7bcc..5bf5d26c228e9 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v, Char)), TyChar => Err(EvalError::InvalidChar(v)), - TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v as usize))), + TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d61ba4ee3f711..abeea0790b84a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -167,7 +167,7 @@ pub enum StackPopCleanup { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: usize, stack_limit: usize, step_limit: u64) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: u64, stack_limit: usize, step_limit: u64) -> Self { EvalContext { tcx: tcx, memory: Memory::new(&tcx.data_layout, memory_size), @@ -211,7 +211,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs - let ptr = self.memory.allocate(s.len(), 1)?; + let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; Ok(Value::ByValPair(PrimVal::from_ptr(ptr), self.usize_primval(s.len() as u64))) @@ -255,7 +255,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { - let ptr = self.memory.allocate(bs.len(), 1)?; + let ptr = self.memory.allocate(bs.len() as u64, 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; PrimVal::from_ptr(ptr) @@ -292,25 +292,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { self.type_size_with_substs(ty, self.substs()) } - fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { + fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { self.type_align_with_substs(ty, self.substs()) } - fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { let layout = self.type_layout_with_substs(ty, substs)?; if layout.is_unsized() { Ok(None) } else { - Ok(Some(layout.size(&self.tcx.data_layout).bytes() as usize)) + Ok(Some(layout.size(&self.tcx.data_layout).bytes())) } } - fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, usize> { - self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi() as usize) + fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, u64> { + self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi()) } fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { @@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (offset, operand) in offsets.into_iter().zip(operands) { let value = self.eval_operand(operand)?; let value_ty = self.operand_ty(operand); - let field_dest = dest.offset(offset as isize); + let field_dest = dest.offset(offset); self.write_value_to_ptr(value, field_dest, value_ty)?; } Ok(()) @@ -525,8 +525,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); - let discr_size = discr.size().bytes() as usize; - let discr_offset = variants[variant].offsets[0].bytes() as isize; + let discr_size = discr.size().bytes(); + let discr_offset = variants[variant].offsets[0].bytes(); // FIXME(solson) let dest = self.force_allocation(dest)?; @@ -581,7 +581,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); - let dest = dest.offset(offset.bytes() as isize); + let dest = dest.offset(offset.bytes()); let dest_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); try!(self.memory.write_int(dest, 0, dest_size)); } @@ -594,7 +594,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let n = adt_def.variants[variant].disr_val.to_u64_unchecked(); - let size = discr.size().bytes() as usize; + let size = discr.size().bytes(); let val = if signed { PrimVal::from_int_with_size(n as i64, size) @@ -621,10 +621,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Repeat(ref operand, _) => { let (elem_ty, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (elem_ty, n), + ty::TyArray(elem_ty, n) => (elem_ty, n as u64), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - self.inc_step_counter_and_check_limit(length as u64)?; + self.inc_step_counter_and_check_limit(length)?; let elem_size = self.type_size(elem_ty)?.expect("repeat element type must be sized"); let value = self.eval_operand(operand)?; @@ -632,7 +632,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); for i in 0..length { - let elem_dest = dest.offset((i * elem_size) as isize); + let elem_dest = dest.offset(i * elem_size); self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -741,15 +741,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn nonnull_offset_and_ty(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { - // Skip the constant 0 at the start meant for LLVM GEP. - let mut path = discrfield.iter().skip(1).map(|&i| i as usize); + // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant + let path = discrfield.iter().skip(2).map(|&i| i as usize); // Handle the field index for the outer non-null variant. let inner_ty = match ty.sty { ty::TyAdt(adt_def, substs) => { let variant = &adt_def.variants[nndiscr as usize]; - let index = path.next().unwrap(); - let field = &variant.fields[index]; + let index = discrfield[1]; + let field = &variant.fields[index as usize]; field.ty(self.tcx, substs) } _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), @@ -804,8 +804,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(variant.offsets[field_index]) } FatPointer { .. } => { - let bytes = field_index * self.memory.pointer_size(); - Ok(Size::from_bytes(bytes as u64)) + let bytes = field_index as u64 * self.memory.pointer_size(); + Ok(Size::from_bytes(bytes)) } _ => { let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); @@ -980,7 +980,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let ptr = base_ptr.offset(offset.bytes() as isize); + let ptr = base_ptr.offset(offset.bytes()); let extra = if self.type_is_sized(field_ty) { LvalueExtra::None } else { @@ -1048,7 +1048,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let n = self.value_to_primval(n_ptr, usize)? .expect_uint("Projection::Index expected usize"); assert!(n < len); - let ptr = base_ptr.offset(n as isize * elem_size as isize); + let ptr = base_ptr.offset(n * elem_size); (ptr, LvalueExtra::None) } @@ -1062,12 +1062,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert!(n >= min_length as u64); let index = if from_end { - n as isize - offset as isize + n - u64::from(offset) } else { - offset as isize + u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size as isize); + let ptr = base_ptr.offset(index * elem_size); (ptr, LvalueExtra::None) } @@ -1078,9 +1078,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); - assert!((from as u64) <= n - (to as u64)); - let ptr = base_ptr.offset(from as isize * elem_size as isize); - let extra = LvalueExtra::Length(n - to as u64 - from as u64); + assert!(u64::from(from) <= n - u64::from(to)); + let ptr = base_ptr.offset(u64::from(from) * elem_size); + let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); (ptr, extra) } }; @@ -1318,8 +1318,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: Ty<'tcx> ) -> EvalResult<'tcx, ()> { assert_eq!(self.get_field_count(ty)?, 2); - let field_0 = self.get_field_offset(ty, 0)?.bytes() as isize; - let field_1 = self.get_field_offset(ty, 1)?.bytes() as isize; + let field_0 = self.get_field_offset(ty, 0)?.bytes(); + let field_1 = self.get_field_offset(ty, 1)?.bytes(); self.memory.write_primval(ptr.offset(field_0), a)?; self.memory.write_primval(ptr.offset(field_1), b)?; Ok(()) @@ -1368,7 +1368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { - let size = discr.size().bytes() as usize; + let size = discr.size().bytes(); if signed { PrimValKind::from_int_size(size) } else { @@ -1450,7 +1450,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::from_ptr(p) } else { trace!("reading fat pointer extra of type {}", ty); - let extra = ptr.offset(self.memory.pointer_size() as isize); + let extra = ptr.offset(self.memory.pointer_size()); let extra = match self.tcx.struct_tail(ty).sty { ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | @@ -1464,7 +1464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { - let size = discr.size().bytes() as usize; + let size = discr.size().bytes(); if signed { let n = self.memory.read_int(ptr, size)?; PrimVal::from_int_with_size(n, size) @@ -1566,8 +1566,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.type_size(dst_fty)? == Some(0) { continue; } - let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; - let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes() as isize; + let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); + let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); let src_f_ptr = src_ptr.offset(src_field_offset); let dst_f_ptr = dest.offset(dst_field_offset); if src_fty == dst_fty { @@ -1699,7 +1699,7 @@ impl<'tcx> Lvalue<'tcx> { pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - memory_size: usize, + memory_size: u64, step_limit: u64, stack_limit: usize, ) { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 013bacc30d2ff..87a9229aba0c4 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("arith_offset second arg not isize"); - let new_ptr = ptr.offset(offset as isize); + let new_ptr = ptr.signed_offset(offset); self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; } @@ -150,7 +150,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = arg_vals[1].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)? .expect_uint("arith_offset second arg not isize"); - self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; + self.memory.copy(src, dest, count * elem_size, elem_align)?; } "ctpop" | @@ -220,7 +220,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => {} "init" => { - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; + let size = dest_layout.size(&self.tcx.data_layout).bytes(); let init = |this: &mut Self, val: Option| { match val { Some(Value::ByRef(ptr)) => { @@ -280,12 +280,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); - let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as isize; + // FIXME: assuming here that type size is < i64::max_value() + let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)? .expect_int("offset second arg not isize"); let ptr = arg_vals[0].read_ptr(&self.memory)?; - let result_ptr = ptr.offset(offset as isize * pointee_size); + let result_ptr = ptr.signed_offset(offset * pointee_size); self.write_primval(dest, PrimVal::from_ptr(result_ptr))?; } @@ -378,7 +379,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "uninit" => { - let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize; + let size = dest_layout.size(&self.tcx.data_layout).bytes(); let uninit = |this: &mut Self, val: Option| { match val { Some(Value::ByRef(ptr)) => { @@ -482,8 +483,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyTrait(..) => { let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. - let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?; - let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?; + let size = self.memory.read_usize(vtable.offset(pointer_size))?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?; Ok((size, align)) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index ad0e139e6e1f4..ab5b695ff1dde 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -254,23 +254,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, signed: false, .. } => { let discr_size = discr.size().bytes(); - self.memory.read_uint(adt_ptr, discr_size as usize)? + self.memory.read_uint(adt_ptr, discr_size)? } CEnum { discr, signed: true, .. } => { let discr_size = discr.size().bytes(); - self.memory.read_int(adt_ptr, discr_size as usize)? as u64 + self.memory.read_int(adt_ptr, discr_size)? as u64 } RawNullablePointer { nndiscr, value } => { - let discr_size = value.size(&self.tcx.data_layout).bytes() as usize; + let discr_size = value.size(&self.tcx.data_layout).bytes(); trace!("rawnullablepointer with size {}", discr_size); self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes() as isize); + let nonnull = adt_ptr.offset(offset.bytes()); trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); @@ -285,7 +285,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: usize) -> EvalResult<'tcx, u64> { + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: u64) -> EvalResult<'tcx, u64> { let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, @@ -300,7 +300,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, - dest_size: usize, + dest_size: u64, ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); @@ -327,7 +327,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect_uint("__rust_allocate first arg not usize"); let align = self.value_to_primval(args[1], usize)? .expect_uint("__rust_allocate second arg not usize"); - let ptr = self.memory.allocate(size as usize, align as usize)?; + let ptr = self.memory.allocate(size, align)?; self.write_primval(dest, PrimVal::from_ptr(ptr))?; } @@ -345,14 +345,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = args[0].read_ptr(&self.memory)?; let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); - let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; + let new_ptr = self.memory.reallocate(ptr, size, align)?; self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; } "memcmp" => { let left = args[0].read_ptr(&self.memory)?; let right = args[1].read_ptr(&self.memory)?; - let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize") as usize; + let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize"); let result = { let left_bytes = self.memory.read_bytes(left, n)?; @@ -414,7 +414,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("rust-call ABI tuple argument wasn't Value::ByRef"), }; for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset as isize)); + let arg = Value::ByRef(last_ptr.offset(offset)); args.push((arg, ty)); } } @@ -496,13 +496,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } traits::VtableObject(ref data) => { - let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); + let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; *first_arg = Value::ByVal(PrimVal::from_ptr(self_ptr)); let idx = idx + 3; let offset = idx * self.memory.pointer_size(); - let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; + let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; *first_ty = sig.inputs[0]; Ok((def_id, substs)) @@ -600,6 +600,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; if discr == nndiscr { + assert_eq!(discr as usize as u64, discr); adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) } else { // FIXME: the zst variant might contain zst types that impl Drop @@ -609,6 +610,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::RawNullablePointer { nndiscr, .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; if discr == nndiscr { + assert_eq!(discr as usize as u64, discr); assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); let field_ty = &adt_def.variants[discr as usize].fields[0]; let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); @@ -656,10 +658,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, ty::TySlice(elem_ty) => { let (ptr, len) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), _ => bug!("expected an lvalue with a length"), }; - let size = self.type_size(elem_ty)?.expect("slice element must be sized") as isize; + let size = self.type_size(elem_ty)?.expect("slice element must be sized"); // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..len { @@ -672,11 +674,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("expected an lvalue with optional extra data"), }; - let size = self.type_size(elem_ty)?.expect("array element cannot be unsized") as isize; + let size = self.type_size(elem_ty)?.expect("array element cannot be unsized"); // FIXME: this creates a lot of stack frames if the element type has // a drop impl - for i in 0..len { - self.drop(Lvalue::Ptr { ptr: ptr.offset(i as isize * size), extra: extra }, elem_ty, drop)?; + for i in 0..(len as u64) { + self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra: extra }, elem_ty, drop)?; } }, // FIXME: what about TyClosure and TyAnon? @@ -699,7 +701,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); // manual iteration, because we need to be careful about the last field if it is unsized while let Some((field_ty, offset)) = fields.next() { - let ptr = adt_ptr.offset(offset.bytes() as isize); + let ptr = adt_ptr.offset(offset.bytes()); if self.type_is_sized(field_ty) { self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; } else { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index fa89d02ad77d1..f31f1ca24bc3c 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -34,7 +34,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size() as isize))?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size()))?; Ok((ptr, vtable)) } @@ -49,7 +49,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size() as isize))?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size()))?; Ok((ptr, len)) }, ByValPair(ptr, val) => { diff --git a/src/interpreter/vtable.rs b/src/interpreter/vtable.rs index 920e57a1f3413..89db8e111e40c 100644 --- a/src/interpreter/vtable.rs +++ b/src/interpreter/vtable.rs @@ -84,7 +84,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let align = self.type_align(trait_ref.self_ty())?; let ptr_size = self.memory.pointer_size(); - let vtable = self.memory.allocate(ptr_size * (3 + methods.len()), ptr_size)?; + let vtable = self.memory.allocate(ptr_size * (3 + methods.len() as u64), ptr_size)?; // in case there is no drop function to be called, this still needs to be initialized self.memory.write_usize(vtable, 0)?; @@ -99,12 +99,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.memory.write_usize(vtable.offset(ptr_size as isize), size as u64)?; - self.memory.write_usize(vtable.offset((ptr_size * 2) as isize), align as u64)?; + self.memory.write_usize(vtable.offset(ptr_size), size)?; + self.memory.write_usize(vtable.offset((ptr_size * 2)), align)?; for (i, method) in methods.into_iter().enumerate() { if let Some(method) = method { - self.memory.write_ptr(vtable.offset(ptr_size as isize * (3 + i as isize)), method)?; + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), method)?; } } diff --git a/src/memory.rs b/src/memory.rs index af10ce4352ccc..c4c045c121b10 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -33,11 +33,11 @@ pub struct Allocation { pub bytes: Vec, /// Maps from byte addresses to allocations. /// Only the first byte of a pointer is inserted into the map. - pub relocations: BTreeMap, + pub relocations: BTreeMap, /// Denotes undefined memory. Reading from undefined memory is forbidden in miri pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. - pub align: usize, + pub align: u64, /// Whether the allocation may be modified. /// Use the `freeze` method of `Memory` to ensure that an error occurs, if the memory of this /// allocation is modified in the future. @@ -47,24 +47,35 @@ pub struct Allocation { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Pointer { pub alloc_id: AllocId, - pub offset: usize, + pub offset: u64, } impl Pointer { - pub fn new(alloc_id: AllocId, offset: usize) -> Self { + pub fn new(alloc_id: AllocId, offset: u64) -> Self { Pointer { alloc_id: alloc_id, offset: offset } } - pub fn offset(self, i: isize) -> Self { - let new_offset = (self.offset as isize + i) as usize; - Pointer::new(self.alloc_id, new_offset) + pub fn signed_offset(self, i: i64) -> Self { + // FIXME: is it possible to over/underflow here? + if i < 0 { + // trickery to ensure that i64::min_value() works fine + // this formula only works for true negative values, it panics for zero! + let n = u64::max_value() - (i as u64) + 1; + Pointer::new(self.alloc_id, self.offset - n) + } else { + self.offset(i as u64) + } + } + + pub fn offset(self, i: u64) -> Self { + Pointer::new(self.alloc_id, self.offset + i) } pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } - pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> { + pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, u64> { match self.alloc_id { NEVER_ALLOC_ID | ZST_ALLOC_ID => Ok(self.offset), @@ -72,9 +83,7 @@ impl Pointer { } } - // FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger - // than host usize. - pub fn from_int(i: usize) -> Self { + pub fn from_int(i: u64) -> Self { Pointer::new(NEVER_ALLOC_ID, i) } @@ -103,9 +112,9 @@ pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, /// Number of virtual bytes allocated - memory_usage: usize, + memory_usage: u64, /// Maximum number of virtual bytes that may be allocated - memory_size: usize, + memory_size: u64, /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. functions: HashMap>, @@ -119,7 +128,7 @@ const ZST_ALLOC_ID: AllocId = AllocId(0); const NEVER_ALLOC_ID: AllocId = AllocId(1); impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { + pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), @@ -175,7 +184,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Pointer::new(id, 0) } - pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, Pointer> { if size == 0 { return Ok(Pointer::zst_ptr()); } @@ -189,8 +198,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }); } self.memory_usage += size; + assert_eq!(size as usize as u64, size); let alloc = Allocation { - bytes: vec![0; size], + bytes: vec![0; size as usize], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align: align, @@ -204,7 +214,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: Pointer, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); @@ -216,19 +226,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::ReallocatedFrozenMemory); } - let size = self.get(ptr.alloc_id)?.bytes.len(); + let size = self.get(ptr.alloc_id)?.bytes.len() as u64; if new_size > size { let amount = new_size - size; self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; - alloc.bytes.extend(iter::repeat(0).take(amount)); + assert_eq!(amount as usize as u64, amount); + alloc.bytes.extend(iter::repeat(0).take(amount as usize)); alloc.undef_mask.grow(amount, false); } else if size > new_size { self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; + self.clear_relocations(ptr.offset(new_size), size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; - alloc.bytes.truncate(new_size); + // `as usize` is fine here, since it is smaller than `size`, which came from a usize + alloc.bytes.truncate(new_size as usize); alloc.bytes.shrink_to_fit(); alloc.undef_mask.truncate(new_size); } @@ -250,7 +262,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { - self.memory_usage -= alloc.bytes.len(); + self.memory_usage -= alloc.bytes.len() as u64; } else { debug!("deallocated a pointer twice: {}", ptr.alloc_id); // TODO(solson): Report error about erroneous free. This is blocked on properly tracking @@ -262,15 +274,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn pointer_size(&self) -> usize { - self.layout.pointer_size.bytes() as usize + pub fn pointer_size(&self) -> u64 { + self.layout.pointer_size.bytes() } pub fn endianess(&self) -> layout::Endian { self.layout.endian } - pub fn check_align(&self, ptr: Pointer, align: usize) -> EvalResult<'tcx, ()> { + pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx, ()> { let alloc = self.get(ptr.alloc_id)?; if alloc.align < align { return Err(EvalError::AlignmentCheckFailed { @@ -358,7 +370,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { (Some(_), Some(_)) => bug!("miri invariant broken: an allocation id exists that points to both a function and a memory location"), }; - for i in 0..alloc.bytes.len() { + for i in 0..(alloc.bytes.len() as u64) { if let Some(&target_id) = alloc.relocations.get(&i) { if !allocs_seen.contains(&target_id) { allocs_to_print.push_back(target_id); @@ -366,7 +378,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations.push((i, target_id)); } if alloc.undef_mask.is_range_defined(i, i + 1) { - write!(msg, "{:02x} ", alloc.bytes[i]).unwrap(); + // this `as usize` is fine, since `i` came from a `usize` + write!(msg, "{:02x} ", alloc.bytes[i as usize]).unwrap(); } else { msg.push_str("__ "); } @@ -381,13 +394,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let mut pos = 0; let relocation_width = (self.pointer_size() - 1) * 3; for (i, target_id) in relocations { - write!(msg, "{:1$}", "", (i - pos) * 3).unwrap(); + // this `as usize` is fine, since we can't print more chars than `usize::MAX` + write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap(); let target = match target_id { ZST_ALLOC_ID => String::from("zst"), NEVER_ALLOC_ID => String::from("int ptr"), _ => format!("({})", target_id), }; - write!(msg, "└{0:─^1$}┘ ", target, relocation_width).unwrap(); + // this `as usize` is fine, since we can't print more chars than `usize::MAX` + write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap(); pos = i + self.pointer_size(); } trace!("{}", msg); @@ -398,37 +413,43 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { - fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + fn get_bytes_unchecked(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } let alloc = self.get(ptr.alloc_id)?; - if ptr.offset + size > alloc.bytes.len() { + if ptr.offset + size > alloc.bytes.len() as u64 { return Err(EvalError::PointerOutOfBounds { ptr: ptr, size: size, - allocation_size: alloc.bytes.len(), + allocation_size: alloc.bytes.len() as u64, }); } - Ok(&alloc.bytes[ptr.offset..ptr.offset + size]) + assert_eq!(ptr.offset as usize as u64, ptr.offset); + assert_eq!(size as usize as u64, size); + let offset = ptr.offset as usize; + Ok(&alloc.bytes[offset..offset + size as usize]) } - fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &mut [u8]> { if size == 0 { return Ok(&mut []); } let alloc = self.get_mut(ptr.alloc_id)?; - if ptr.offset + size > alloc.bytes.len() { + if ptr.offset + size > alloc.bytes.len() as u64 { return Err(EvalError::PointerOutOfBounds { ptr: ptr, size: size, - allocation_size: alloc.bytes.len(), + allocation_size: alloc.bytes.len() as u64, }); } - Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) + assert_eq!(ptr.offset as usize as u64, ptr.offset); + assert_eq!(size as usize as u64, size); + let offset = ptr.offset as usize; + Ok(&mut alloc.bytes[offset..offset + size as usize]) } - fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + fn get_bytes(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } @@ -440,7 +461,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes_unchecked(ptr, size) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { if size == 0 { return Ok(&mut []); } @@ -476,7 +497,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64) -> EvalResult<'tcx, ()> { if size == 0 { return Ok(()); } @@ -489,10 +510,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and // `dest` could possibly overlap. unsafe { + assert_eq!(size as usize as u64, size); if src.alloc_id == dest.alloc_id { - ptr::copy(src_bytes, dest_bytes, size); + ptr::copy(src_bytes, dest_bytes, size as usize); } else { - ptr::copy_nonoverlapping(src_bytes, dest_bytes, size); + ptr::copy_nonoverlapping(src_bytes, dest_bytes, size as usize); } } @@ -502,17 +524,17 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { self.get_bytes(ptr, size, 1) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, src.len(), 1)?; + let bytes = self.get_bytes_mut(ptr, src.len() as u64, 1)?; bytes.clone_from_slice(src); Ok(()) } - pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx, ()> { let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) @@ -523,7 +545,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.check_defined(ptr, size)?; let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size)?; - let offset = read_target_uint(endianess, bytes).unwrap() as usize; + let offset = read_target_uint(endianess, bytes).unwrap(); let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)), @@ -539,7 +561,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { if let Some(alloc_id) = val.relocation { - return self.write_ptr(dest, Pointer::new(alloc_id, val.bits as usize)); + return self.write_ptr(dest, Pointer::new(alloc_id, val.bits)); } use primval::PrimValKind::*; @@ -556,7 +578,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { - let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi() as usize)?; + let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi())?; match bytes[0] { 0 => Ok(false), 1 => Ok(true), @@ -565,27 +587,27 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { - let align = self.layout.i1_align.abi() as usize; + let align = self.layout.i1_align.abi(); self.get_bytes_mut(ptr, 1, align) .map(|bytes| bytes[0] = b as u8) } - fn int_align(&self, size: usize) -> EvalResult<'tcx, usize> { + fn int_align(&self, size: u64) -> EvalResult<'tcx, u64> { match size { - 1 => Ok(self.layout.i8_align.abi() as usize), - 2 => Ok(self.layout.i16_align.abi() as usize), - 4 => Ok(self.layout.i32_align.abi() as usize), - 8 => Ok(self.layout.i64_align.abi() as usize), + 1 => Ok(self.layout.i8_align.abi()), + 2 => Ok(self.layout.i16_align.abi()), + 4 => Ok(self.layout.i32_align.abi()), + 8 => Ok(self.layout.i64_align.abi()), _ => bug!("bad integer size: {}", size), } } - pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { + pub fn read_int(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, i64> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } - pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { + pub fn write_int(&mut self, ptr: Pointer, n: i64, size: u64) -> EvalResult<'tcx, ()> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -593,12 +615,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { + pub fn read_uint(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, u64> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } - pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { + pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: u64) -> EvalResult<'tcx, ()> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -626,7 +648,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - let align = self.layout.f32_align.abi() as usize; + let align = self.layout.f32_align.abi(); let b = self.get_bytes_mut(ptr, 4, align)?; write_target_f32(endianess, b, f).unwrap(); Ok(()) @@ -634,34 +656,34 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - let align = self.layout.f64_align.abi() as usize; + let align = self.layout.f64_align.abi(); let b = self.get_bytes_mut(ptr, 8, align)?; write_target_f64(endianess, b, f).unwrap(); Ok(()) } pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { - self.get_bytes(ptr, 4, self.layout.f32_align.abi() as usize) + self.get_bytes(ptr, 4, self.layout.f32_align.abi()) .map(|b| read_target_f32(self.endianess(), b).unwrap()) } pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { - self.get_bytes(ptr, 8, self.layout.f64_align.abi() as usize) + self.get_bytes(ptr, 8, self.layout.f64_align.abi()) .map(|b| read_target_f64(self.endianess(), b).unwrap()) } } /// Relocations impl<'a, 'tcx> Memory<'a, 'tcx> { - fn relocations(&self, ptr: Pointer, size: usize) - -> EvalResult<'tcx, btree_map::Range> + fn relocations(&self, ptr: Pointer, size: u64) + -> EvalResult<'tcx, btree_map::Range> { let start = ptr.offset.saturating_sub(self.pointer_size() - 1); let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } - fn clear_relocations(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { return Ok(()); } @@ -685,16 +707,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn check_relocation_edges(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size as isize), 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size), 0)?.count(); if overlapping_start + overlapping_end != 0 { return Err(EvalError::ReadPointerAsBytes); } Ok(()) } - fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx, ()> { let relocations: Vec<_> = self.relocations(src, size)? .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. @@ -709,20 +731,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Undefined bytes impl<'a, 'tcx> Memory<'a, 'tcx> { // FIXME(solson): This is a very naive, slow version. - fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx, ()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. - let mut v = Vec::with_capacity(size); + assert_eq!(size as usize as u64, size); + let mut v = Vec::with_capacity(size as usize); for i in 0..size { let defined = self.get(src.alloc_id)?.undef_mask.get(src.offset + i); v.push(defined); } for (i, defined) in v.into_iter().enumerate() { - self.get_mut(dest.alloc_id)?.undef_mask.set(dest.offset + i, defined); + self.get_mut(dest.alloc_id)?.undef_mask.set(dest.offset + i as u64, defined); } Ok(()) } - fn check_defined(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, ()> { + fn check_defined(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); @@ -730,7 +753,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) + pub fn mark_definedness(&mut self, ptr: Pointer, size: u64, new_state: bool) -> EvalResult<'tcx, ()> { if size == 0 { @@ -809,16 +832,16 @@ fn read_target_f64(endianess: layout::Endian, mut source: &[u8]) -> Result, - len: usize, + len: u64, } impl UndefMask { - fn new(size: usize) -> Self { + fn new(size: u64) -> Self { let mut m = UndefMask { blocks: vec![], len: 0, @@ -828,7 +851,7 @@ impl UndefMask { } /// Check whether the range `start..end` (end-exclusive) is entirely defined. - pub fn is_range_defined(&self, start: usize, end: usize) -> bool { + pub fn is_range_defined(&self, start: u64, end: u64) -> bool { if end > self.len { return false; } for i in start..end { if !self.get(i) { return false; } @@ -836,22 +859,22 @@ impl UndefMask { true } - fn set_range(&mut self, start: usize, end: usize, new_state: bool) { + fn set_range(&mut self, start: u64, end: u64, new_state: bool) { let len = self.len; if end > len { self.grow(end - len, new_state); } self.set_range_inbounds(start, end, new_state); } - fn set_range_inbounds(&mut self, start: usize, end: usize, new_state: bool) { + fn set_range_inbounds(&mut self, start: u64, end: u64, new_state: bool) { for i in start..end { self.set(i, new_state); } } - fn get(&self, i: usize) -> bool { + fn get(&self, i: u64) -> bool { let (block, bit) = bit_index(i); (self.blocks[block] & 1 << bit) != 0 } - fn set(&mut self, i: usize, new_state: bool) { + fn set(&mut self, i: u64, new_state: bool) { let (block, bit) = bit_index(i); if new_state { self.blocks[block] |= 1 << bit; @@ -860,24 +883,31 @@ impl UndefMask { } } - fn grow(&mut self, amount: usize, new_state: bool) { - let unused_trailing_bits = self.blocks.len() * BLOCK_SIZE - self.len; + fn grow(&mut self, amount: u64, new_state: bool) { + let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len; if amount > unused_trailing_bits { let additional_blocks = amount / BLOCK_SIZE + 1; - self.blocks.extend(iter::repeat(0).take(additional_blocks)); + assert_eq!(additional_blocks as usize as u64, additional_blocks); + self.blocks.extend(iter::repeat(0).take(additional_blocks as usize)); } let start = self.len; self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } - fn truncate(&mut self, length: usize) { + fn truncate(&mut self, length: u64) { self.len = length; - self.blocks.truncate(self.len / BLOCK_SIZE + 1); + let truncate = self.len / BLOCK_SIZE + 1; + assert_eq!(truncate as usize as u64, truncate); + self.blocks.truncate(truncate as usize); self.blocks.shrink_to_fit(); } } -fn bit_index(bits: usize) -> (usize, usize) { - (bits / BLOCK_SIZE, bits % BLOCK_SIZE) +fn bit_index(bits: u64) -> (usize, usize) { + let a = bits / BLOCK_SIZE; + let b = bits % BLOCK_SIZE; + assert_eq!(a as usize as u64, a); + assert_eq!(b as usize as u64, b); + (a as usize, b as usize) } diff --git a/src/primval.rs b/src/primval.rs index 15cc88ca4cf9e..20b08b5a0a1b6 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -65,7 +65,7 @@ impl PrimValKind { } } - pub fn from_uint_size(size: usize) -> Self { + pub fn from_uint_size(size: u64) -> Self { match size { 1 => PrimValKind::U8, 2 => PrimValKind::U16, @@ -75,7 +75,7 @@ impl PrimValKind { } } - pub fn from_int_size(size: usize) -> Self { + pub fn from_int_size(size: u64) -> Self { match size { 1 => PrimValKind::I8, 2 => PrimValKind::I16, @@ -119,11 +119,11 @@ impl PrimVal { PrimVal::new(f64_to_bits(f), PrimValKind::F64) } - pub fn from_uint_with_size(n: u64, size: usize) -> Self { + pub fn from_uint_with_size(n: u64, size: u64) -> Self { PrimVal::new(n, PrimValKind::from_uint_size(size)) } - pub fn from_int_with_size(n: i64, size: usize) -> Self { + pub fn from_int_with_size(n: i64, size: u64) -> Self { PrimVal::new(n as u64, PrimValKind::from_int_size(size)) } @@ -139,8 +139,8 @@ impl PrimVal { pub fn to_ptr(self) -> Pointer { self.relocation.map(|alloc_id| { - Pointer::new(alloc_id, self.bits as usize) - }).unwrap_or_else(|| Pointer::from_int(self.bits as usize)) + Pointer::new(alloc_id, self.bits) + }).unwrap_or_else(|| Pointer::from_int(self.bits)) } pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { From ca7ae5a3d0b15a146e9f42bbfb9f52102b8e7509 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 19 Nov 2016 08:33:13 +0100 Subject: [PATCH 0662/1096] don't freeze globals twice --- src/interpreter/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d61ba4ee3f711..4e7241c8f00f3 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -383,9 +383,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, } - if let Value::ByRef(ptr) = global_value.data.expect("global should have been initialized") { - self.memory.freeze(ptr.alloc_id)?; - } assert!(global_value.mutable); global_value.mutable = false; } else { From 3f764a5cfd36f4b41e3295c489eb7ed571198942 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 17:36:31 -0800 Subject: [PATCH 0663/1096] Update for changes in rustc and refactor. --- src/bin/miri.rs | 110 +++++++++++++++--------------- src/interpreter/terminator/mod.rs | 7 +- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 96caa54468bd9..41e1714b60981 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -11,71 +11,73 @@ extern crate syntax; use miri::{eval_main, run_mir_passes}; use rustc::session::Session; -use rustc_driver::{driver, CompilerCalls, Compilation}; +use rustc_driver::{CompilerCalls, Compilation}; +use rustc_driver::driver::{CompileState, CompileController}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; struct MiriCompilerCalls; impl<'a> CompilerCalls<'a> for MiriCompilerCalls { - fn build_controller( - &mut self, - _: &Session, - _: &getopts::Matches - ) -> driver::CompileController<'a> { - let mut control = driver::CompileController::basic(); - control.after_hir_lowering.callback = Box::new(|state| { - state.session.plugin_attributes.borrow_mut().push(("miri".to_owned(), syntax::feature_gate::AttributeType::Whitelisted)); - }); + fn build_controller(&mut self, _: &Session, _: &getopts::Matches) -> CompileController<'a> { + let mut control = CompileController::basic(); + control.after_hir_lowering.callback = Box::new(after_hir_lowering); + control.after_analysis.callback = Box::new(after_analysis); control.after_analysis.stop = Compilation::Stop; - control.after_analysis.callback = Box::new(|state| { - state.session.abort_if_errors(); - - let tcx = state.tcx.unwrap(); - let (entry_node_id, _) = state.session.entry_fn.borrow() - .expect("no main or start function found"); - let entry_def_id = tcx.map.local_def_id(entry_node_id); - - let krate = state.hir_crate.as_ref().unwrap(); - let mut memory_size = 100*1024*1024; // 100MB - let mut step_limit = 1000_000; - let mut stack_limit = 100; - let extract_int = |lit: &syntax::ast::Lit| -> u64 { - match lit.node { - syntax::ast::LitKind::Int(i, _) => i, - _ => state.session.span_fatal(lit.span, "expected an integer literal"), - } - }; - for attr in krate.attrs.iter() { - match attr.node.value.node { - MetaItemKind::List(ref name, _) if name != "miri" => {} - MetaItemKind::List(_, ref items) => for item in items { - match item.node { - NestedMetaItemKind::MetaItem(ref inner) => match inner.node { - MetaItemKind::NameValue(ref name, ref value) => { - match &**name { - "memory_size" => memory_size = extract_int(value), - "step_limit" => step_limit = extract_int(value), - "stack_limit" => stack_limit = extract_int(value) as usize, - _ => state.session.span_err(item.span, "unknown miri attribute"), - } - } - _ => state.session.span_err(inner.span, "miri attributes need to be of key = value kind"), - }, - _ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"), + control + } +} + +fn after_hir_lowering(state: &mut CompileState) { + let attr = (String::from("miri"), syntax::feature_gate::AttributeType::Whitelisted); + state.session.plugin_attributes.borrow_mut().push(attr); +} + +fn after_analysis(state: &mut CompileState) { + state.session.abort_if_errors(); + + let tcx = state.tcx.unwrap(); + let (entry_node_id, _) = state.session.entry_fn.borrow() + .expect("no main or start function found"); + let entry_def_id = tcx.map.local_def_id(entry_node_id); + let krate = state.hir_crate.as_ref().unwrap(); + let mut memory_size = 100 * 1024 * 1024; // 100 MB + let mut step_limit = 1_000_000; + let mut stack_limit = 100; + let extract_int = |lit: &syntax::ast::Lit| -> u64 { + match lit.node { + syntax::ast::LitKind::Int(i, _) => i, + _ => state.session.span_fatal(lit.span, "expected an integer literal"), + } + }; + let err_msg = "miri attributes need to be in the form `miri(key = value)`"; + + for attr in krate.attrs.iter().filter(|a| a.name() == "miri") { + if let MetaItemKind::List(ref items) = attr.value.node { + for item in items { + if let NestedMetaItemKind::MetaItem(ref inner) = item.node { + if let MetaItemKind::NameValue(ref value) = inner.node { + match &inner.name().as_str()[..] { + "memory_size" => memory_size = extract_int(value), + "step_limit" => step_limit = extract_int(value), + "stack_limit" => stack_limit = extract_int(value) as usize, + _ => state.session.span_err(item.span, "unknown miri attribute"), } - }, - _ => {}, + } else { + state.session.span_err(inner.span, err_msg); + } + } else { + state.session.span_err(item.span, err_msg); } } + } else { + state.session.span_err(attr.span, err_msg); + } + } - run_mir_passes(tcx); - eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); - - state.session.abort_if_errors(); - }); + run_mir_passes(tcx); + eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); - control - } + state.session.abort_if_errors(); } fn init_logger() { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index ab5b695ff1dde..7696b0e0fc151 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -304,10 +304,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); - let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { - Some(ln) => ln.clone(), - None => name.as_str(), - }; + let link_name = attr::first_attr_value_str_by_name(&attrs, "link_name") + .unwrap_or(name) + .as_str(); let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) From 020f0b782b965cbe6b1f4c469b7defd0e2c52e85 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 17:54:19 -0800 Subject: [PATCH 0664/1096] Refactor passing of resource limits. --- src/bin/miri.rs | 27 ++++++++++++++------------- src/interpreter/mod.rs | 31 +++++++++++++++++++++++-------- src/lib.rs | 9 +++++---- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 41e1714b60981..6c0161ef0754a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -9,7 +9,6 @@ extern crate log_settings; extern crate syntax; #[macro_use] extern crate log; -use miri::{eval_main, run_mir_passes}; use rustc::session::Session; use rustc_driver::{CompilerCalls, Compilation}; use rustc_driver::driver::{CompileState, CompileController}; @@ -39,17 +38,23 @@ fn after_analysis(state: &mut CompileState) { let (entry_node_id, _) = state.session.entry_fn.borrow() .expect("no main or start function found"); let entry_def_id = tcx.map.local_def_id(entry_node_id); + let limits = resource_limits_from_attributes(state); + miri::run_mir_passes(tcx); + miri::eval_main(tcx, entry_def_id, limits); + + state.session.abort_if_errors(); +} + +fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits { + let mut limits = miri::ResourceLimits::default(); let krate = state.hir_crate.as_ref().unwrap(); - let mut memory_size = 100 * 1024 * 1024; // 100 MB - let mut step_limit = 1_000_000; - let mut stack_limit = 100; + let err_msg = "miri attributes need to be in the form `miri(key = value)`"; let extract_int = |lit: &syntax::ast::Lit| -> u64 { match lit.node { syntax::ast::LitKind::Int(i, _) => i, _ => state.session.span_fatal(lit.span, "expected an integer literal"), } }; - let err_msg = "miri attributes need to be in the form `miri(key = value)`"; for attr in krate.attrs.iter().filter(|a| a.name() == "miri") { if let MetaItemKind::List(ref items) = attr.value.node { @@ -57,9 +62,9 @@ fn after_analysis(state: &mut CompileState) { if let NestedMetaItemKind::MetaItem(ref inner) = item.node { if let MetaItemKind::NameValue(ref value) = inner.node { match &inner.name().as_str()[..] { - "memory_size" => memory_size = extract_int(value), - "step_limit" => step_limit = extract_int(value), - "stack_limit" => stack_limit = extract_int(value) as usize, + "memory_size" => limits.memory_size = extract_int(value), + "step_limit" => limits.step_limit = extract_int(value), + "stack_limit" => limits.stack_limit = extract_int(value) as usize, _ => state.session.span_err(item.span, "unknown miri attribute"), } } else { @@ -73,11 +78,7 @@ fn after_analysis(state: &mut CompileState) { state.session.span_err(attr.span, err_msg); } } - - run_mir_passes(tcx); - eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); - - state.session.abort_if_errors(); + limits } fn init_logger() { diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a8e473ead818b..b1b3081a2d76e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -166,15 +166,32 @@ pub enum StackPopCleanup { None, } +#[derive(Copy, Clone, Debug)] +pub struct ResourceLimits { + pub memory_size: u64, + pub step_limit: u64, + pub stack_limit: usize, +} + +impl Default for ResourceLimits { + fn default() -> Self { + ResourceLimits { + memory_size: 100 * 1024 * 1024, // 100 MB + step_limit: 1_000_000, + stack_limit: 100, + } + } +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, memory_size: u64, stack_limit: usize, step_limit: u64) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { EvalContext { tcx: tcx, - memory: Memory::new(&tcx.data_layout, memory_size), + memory: Memory::new(&tcx.data_layout, limits.memory_size), globals: HashMap::new(), stack: Vec::new(), - stack_limit: stack_limit, - steps_remaining: step_limit, + stack_limit: limits.stack_limit, + steps_remaining: limits.step_limit, } } @@ -1696,11 +1713,9 @@ impl<'tcx> Lvalue<'tcx> { pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - memory_size: u64, - step_limit: u64, - stack_limit: usize, + limits: ResourceLimits, ) { - let mut ecx = EvalContext::new(tcx, memory_size, stack_limit, step_limit); + let mut ecx = EvalContext::new(tcx, limits); let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); ecx.push_stack_frame( diff --git a/src/lib.rs b/src/lib.rs index d957e947e386e..e2158189144da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,12 +33,13 @@ pub use error::{ pub use interpreter::{ EvalContext, Frame, - eval_main, - run_mir_passes, - StackPopCleanup, - Value, Lvalue, LvalueExtra, + ResourceLimits, + StackPopCleanup, + Value, + eval_main, + run_mir_passes, }; pub use memory::{ From 78b29b360adb82bf57ae77d083833d0135482f96 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 19:13:22 -0800 Subject: [PATCH 0665/1096] Dump return value when returning. --- src/interpreter/terminator/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 7696b0e0fc151..8f58105de4bb8 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -29,7 +29,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { use rustc::mir::TerminatorKind::*; match terminator.kind { - Return => self.pop_stack_frame()?, + Return => { + self.dump_local(self.frame().return_lvalue); + self.pop_stack_frame()? + } Goto { target } => self.goto_block(target), From 16f3b590e46062a0db728e7764dc64d23851c4db Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 19:18:39 -0800 Subject: [PATCH 0666/1096] Remove unnecessary qualification. --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index b1b3081a2d76e..59b3db7aa8279 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -23,7 +23,7 @@ mod cast; mod vtable; mod value; -pub type MirRef<'tcx> = ::std::cell::Ref<'tcx, mir::Mir<'tcx>>; +pub type MirRef<'tcx> = Ref<'tcx, mir::Mir<'tcx>>; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. From 49f784a3e4a6e88fb9f1ff8c18ae2dfa3757ea46 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 22:58:01 -0800 Subject: [PATCH 0667/1096] Remove PrimValKind field from PrimVal. --- src/interpreter/cast.rs | 43 +++--- src/interpreter/mod.rs | 129 ++++++++---------- src/interpreter/terminator/intrinsics.rs | 159 ++++++++++------------- src/interpreter/terminator/mod.rs | 41 +++--- src/memory.rs | 11 +- src/primval.rs | 154 ++++++++++------------ 6 files changed, 246 insertions(+), 291 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 5bf5d26c228e9..4b0c91b00ab35 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -11,17 +11,24 @@ use rustc::ty::Ty; use syntax::ast::{FloatTy, IntTy, UintTy}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + pub(super) fn cast_primval( + &self, + val: PrimVal, + src_ty: Ty<'tcx>, + dest_ty: Ty<'tcx> + ) -> EvalResult<'tcx, PrimVal> { + let kind = self.ty_to_primval_kind(src_ty)?; + use primval::PrimValKind::*; - match val.kind { - F32 => self.cast_float(val.to_f32() as f64, ty), - F64 => self.cast_float(val.to_f64(), ty), + match kind { + F32 => self.cast_float(val.to_f32() as f64, dest_ty), + F64 => self.cast_float(val.to_f64(), dest_ty), - I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits as i64, ty), + I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits as i64, dest_ty), - Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false), + Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, dest_ty, false), - FnPtr | Ptr => self.cast_ptr(val.to_ptr(), ty), + FnPtr | Ptr => self.cast_ptr(val.to_ptr(), dest_ty), } } @@ -30,22 +37,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn cast_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { - use primval::PrimValKind::*; use rustc::ty::TypeVariants::*; match ty.sty { TyBool if v == 0 => Ok(PrimVal::from_bool(false)), TyBool if v == 1 => Ok(PrimVal::from_bool(true)), TyBool => Err(EvalError::InvalidBool), - TyInt(IntTy::I8) => Ok(PrimVal::new(v as i64 as i8 as u64, I8)), - TyInt(IntTy::I16) => Ok(PrimVal::new(v as i64 as i16 as u64, I16)), - TyInt(IntTy::I32) => Ok(PrimVal::new(v as i64 as i32 as u64, I32)), - TyInt(IntTy::I64) => Ok(PrimVal::new(v as i64 as i64 as u64, I64)), + TyInt(IntTy::I8) => Ok(PrimVal::new(v as i64 as i8 as u64)), + TyInt(IntTy::I16) => Ok(PrimVal::new(v as i64 as i16 as u64)), + TyInt(IntTy::I32) => Ok(PrimVal::new(v as i64 as i32 as u64)), + TyInt(IntTy::I64) => Ok(PrimVal::new(v as i64 as i64 as u64)), - TyUint(UintTy::U8) => Ok(PrimVal::new(v as u8 as u64, U8)), - TyUint(UintTy::U16) => Ok(PrimVal::new(v as u16 as u64, U16)), - TyUint(UintTy::U32) => Ok(PrimVal::new(v as u32 as u64, U32)), - TyUint(UintTy::U64) => Ok(PrimVal::new(v, U64)), + TyUint(UintTy::U8) => Ok(PrimVal::new(v as u8 as u64)), + TyUint(UintTy::U16) => Ok(PrimVal::new(v as u16 as u64)), + TyUint(UintTy::U32) => Ok(PrimVal::new(v as u32 as u64)), + TyUint(UintTy::U64) => Ok(PrimVal::new(v)), TyInt(IntTy::Is) => { let int_ty = self.tcx.sess.target.int_type; @@ -64,7 +70,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i64 as f32)), TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), - TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v, Char)), + TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v)), TyChar => Err(EvalError::InvalidChar(v)), TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v))), @@ -91,8 +97,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { - TyRef(..) | TyRawPtr(_) => Ok(PrimVal::from_ptr(ptr)), - TyFnPtr(_) => Ok(PrimVal::from_fn_ptr(ptr)), + TyRef(..) | TyRawPtr(_) | TyFnPtr(_) => Ok(PrimVal::from_ptr(ptr)), TyInt(_) | TyUint(_) => self.transmute_primval(PrimVal::from_ptr(ptr), ty), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 59b3db7aa8279..82b949d453fb5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -222,16 +222,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - fn usize_primval(&self, n: u64) -> PrimVal { - PrimVal::from_uint_with_size(n, self.memory.pointer_size()) - } - fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::from_ptr(ptr), self.usize_primval(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::from_ptr(ptr), PrimVal::from_uint(s.len() as u64))) } fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -239,27 +235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::ConstFloat; let primval = match *const_val { - Integral(const_int) => { - use rustc_const_math::ConstInt::*; - use rustc_const_math::ConstIsize::*; - use rustc_const_math::ConstUsize::*; - - let kind = match const_int { - I8(_) => PrimValKind::I8, - I16(_) | Isize(Is16(_)) => PrimValKind::I16, - I32(_) | Isize(Is32(_)) => PrimValKind::I32, - I64(_) | Isize(Is64(_)) => PrimValKind::I64, - U8(_) => PrimValKind::U8, - U16(_) | Usize(Us16(_)) => PrimValKind::U16, - U32(_) | Usize(Us32(_)) => PrimValKind::U32, - U64(_) | Usize(Us64(_)) => PrimValKind::U64, - - Infer(_) | InferSigned(_) => - bug!("uninferred constants only exist before typeck"), - }; - - PrimVal::new(const_int.to_u64_unchecked(), kind) - } + Integral(const_int) => PrimVal::new(const_int.to_u64_unchecked()), Float(ConstFloat::F32(f)) => PrimVal::from_f32(f), Float(ConstFloat::F64(f)) => PrimVal::from_f64(f), @@ -306,7 +282,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, substs); - self.tcx.normalize_associated_type(&substituted) + let new = self.tcx.normalize_associated_type(&substituted); + new } fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { @@ -432,9 +409,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, ) -> EvalResult<'tcx, (PrimVal, bool)> { - let left_primval = self.eval_operand_to_primval(left)?; - let right_primval = self.eval_operand_to_primval(right)?; - primval::binary_op(op, left_primval, right_primval) + let left_ty = self.operand_ty(left); + let right_ty = self.operand_ty(right); + let left_kind = self.ty_to_primval_kind(left_ty)?; + let right_kind = self.ty_to_primval_kind(right_ty)?; + let left_val = self.eval_operand_to_primval(left)?; + let right_val = self.eval_operand_to_primval(right)?; + primval::binary_op(op, left_val, left_kind, right_val, right_kind) } /// Applies the binary operation `op` to the two operands and writes a tuple of the result @@ -460,9 +441,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { left: &mir::Operand<'tcx>, right: &mir::Operand<'tcx>, dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, bool> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; - self.write_primval(dest, val)?; + self.write_primval(dest, val, dest_ty)?; Ok(overflowed) } @@ -506,7 +488,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { BinaryOp(bin_op, ref left, ref right) => { // ignore overflow bit, rustc inserts check branches for us - self.intrinsic_overflowing(bin_op, left, right, dest)?; + self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?; } CheckedBinaryOp(bin_op, ref left, ref right) => { @@ -515,7 +497,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; - self.write_primval(dest, primval::unary_op(un_op, val)?)?; + let kind = self.ty_to_primval_kind(dest_ty)?; + self.write_primval(dest, primval::unary_op(un_op, val, kind)?, dest_ty)?; } Aggregate(ref kind, ref operands) => { @@ -571,9 +554,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - let value_size = self.type_size(dest_ty)?.expect("pointer types are sized"); - let zero = PrimVal::from_int_with_size(0, value_size); - self.write_primval(dest, zero)?; + self.write_primval(dest, PrimVal::from_int(0), dest_ty)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -604,19 +585,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - CEnum { discr, signed, .. } => { + CEnum { .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let n = adt_def.variants[variant].disr_val.to_u64_unchecked(); - let size = discr.size().bytes(); - - let val = if signed { - PrimVal::from_int_with_size(n as i64, size) - } else { - PrimVal::from_uint_with_size(n, size) - }; - - self.write_primval(dest, val)?; + self.write_primval(dest, PrimVal::new(n), dest_ty)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); } @@ -655,8 +628,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); - let len_val = self.usize_primval(len); - self.write_primval(dest, len_val)?; + self.write_primval(dest, PrimVal::from_uint(len), dest_ty)?; } Ref(_, _, ref lvalue) => { @@ -666,7 +638,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = match extra { LvalueExtra::None => Value::ByVal(ptr), - LvalueExtra::Length(len) => Value::ByValPair(ptr, self.usize_primval(len)), + LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_uint(len)), LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::from_ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -677,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let ptr = self.alloc_ptr(ty)?; - self.write_primval(dest, PrimVal::from_ptr(ptr))?; + self.write_primval(dest, PrimVal::from_ptr(ptr), dest_ty)?; } Cast(kind, ref operand, cast_ty) => { @@ -707,7 +679,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } else { let src_val = self.value_to_primval(src, src_ty)?; - let dest_val = self.cast_primval(src_val, dest_ty)?; + let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?; self.write_value(Value::ByVal(dest_val), dest, dest_ty)?; } } @@ -716,7 +688,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(def_id, substs, fn_ty) => { let fn_ty = self.tcx.erase_regions(&fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); - self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::from_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), }, @@ -728,7 +700,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); - self.write_value(Value::ByVal(PrimVal::from_fn_ptr(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::from_ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), }, @@ -1059,8 +1031,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; - let n = self.value_to_primval(n_ptr, usize)? - .expect_uint("Projection::Index expected usize"); + let n = self.value_to_primval(n_ptr, usize)?.to_u64(); assert!(n < len); let ptr = base_ptr.offset(n * elem_size); (ptr, LvalueExtra::None) @@ -1124,6 +1095,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), opt_val => { let ty = self.stack[frame].mir.local_decls[local].ty; + let ty = self.monomorphize(ty, self.stack[frame].substs); let substs = self.stack[frame].substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].set_local(local, Value::ByRef(ptr)); @@ -1168,7 +1140,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(primval) => { let ptr = self.alloc_ptr(ty)?; - self.memory.write_primval(ptr, primval)?; + let kind = self.ty_to_primval_kind(ty)?; + self.memory.write_primval(ptr, primval, kind)?; Ok(ptr) } @@ -1202,19 +1175,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn transmute_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - Ok(PrimVal { kind: self.ty_to_primval_kind(ty)?, ..val }) + // FIXME(solson): Delete this. + fn transmute_primval(&self, val: PrimVal, _ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + Ok(val) } fn write_primval( &mut self, dest: Lvalue<'tcx>, val: PrimVal, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match dest { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.memory.write_primval(ptr, val) + let kind = self.ty_to_primval_kind(dest_ty)?; + self.memory.write_primval(ptr, val, kind) } Lvalue::Local { frame, local } => { self.stack[frame].set_local(local, Value::ByVal(val)); @@ -1319,7 +1295,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, ()> { match value { Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), - Value::ByVal(primval) => self.memory.write_primval(dest, primval), + Value::ByVal(primval) => { + let kind = self.ty_to_primval_kind(dest_ty)?; + self.memory.write_primval(dest, primval, kind) + } Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest, dest_ty), } } @@ -1334,8 +1313,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(self.get_field_count(ty)?, 2); let field_0 = self.get_field_offset(ty, 0)?.bytes(); let field_1 = self.get_field_offset(ty, 1)?.bytes(); - self.memory.write_primval(ptr.offset(field_0), a)?; - self.memory.write_primval(ptr.offset(field_1), b)?; + let field_0_ty = self.get_field_ty(ty, 0)?; + let field_1_ty = self.get_field_ty(ty, 1)?; + let field_0_kind = self.ty_to_primval_kind(field_0_ty)?; + let field_1_kind = self.ty_to_primval_kind(field_1_ty)?; + self.memory.write_primval(ptr.offset(field_0), a, field_0_kind)?; + self.memory.write_primval(ptr.offset(field_1), b, field_1_kind)?; Ok(()) } @@ -1432,8 +1415,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I64 => 8, Is => self.memory.pointer_size(), }; - let n = self.memory.read_int(ptr, size)?; - PrimVal::from_int_with_size(n, size) + PrimVal::from_int(self.memory.read_int(ptr, size)?) } ty::TyUint(uint_ty) => { @@ -1445,17 +1427,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64 => 8, Us => self.memory.pointer_size(), }; - let n = self.memory.read_uint(ptr, size)?; - PrimVal::from_uint_with_size(n, size) + PrimVal::from_uint(self.memory.read_uint(ptr, size)?) } ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), + // TODO(solson): Should this even be here? Fn items aren't primvals, are they? ty::TyFnDef(def_id, substs, fn_ty) => { - PrimVal::from_fn_ptr(self.memory.create_fn_ptr(self.tcx, def_id, substs, fn_ty)) + PrimVal::from_ptr(self.memory.create_fn_ptr(self.tcx, def_id, substs, fn_ty)) }, - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?, + + ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_ptr)?, ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { @@ -1468,7 +1451,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let extra = match self.tcx.struct_tail(ty).sty { ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => self.usize_primval(self.memory.read_usize(extra)?), + ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; return Ok(Value::ByValPair(PrimVal::from_ptr(p), extra)); @@ -1480,11 +1463,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes(); if signed { - let n = self.memory.read_int(ptr, size)?; - PrimVal::from_int_with_size(n, size) + PrimVal::from_int(self.memory.read_int(ptr, size)?) } else { - let n = self.memory.read_uint(ptr, size)?; - PrimVal::from_uint_with_size(n, size) + PrimVal::from_uint(self.memory.read_uint(ptr, size)?) } } else { bug!("primitive read of non-clike enum: {:?}", ty); @@ -1531,7 +1512,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - let len = self.usize_primval(length as u64); + let len = PrimVal::from_uint(length as u64); let ptr = PrimVal::from_ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 87a9229aba0c4..64562ccb9178e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -44,10 +44,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let ptr = arg_vals[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(arg_vals[1], isize)? - .expect_int("arith_offset second arg not isize"); + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); let new_ptr = ptr.signed_offset(offset); - self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(new_ptr), dest_ty)?; } "assume" => { @@ -85,8 +84,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"), }; - self.write_primval(dest, old)?; - self.write_primval(Lvalue::from_ptr(ptr), change)?; + self.write_primval(dest, old, ty)?; + self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } "atomic_cxchg" => { @@ -100,10 +99,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), }; - let (val, _) = primval::binary_op(mir::BinOp::Eq, old, expect_old)?; + let kind = self.ty_to_primval_kind(ty)?; + let (val, _) = primval::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?; let dest = self.force_allocation(dest)?.to_ptr(); self.write_pair_to_ptr(old, val, dest, dest_ty)?; - self.write_primval(Lvalue::from_ptr(ptr), change)?; + self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } "atomic_xadd_relaxed" => { @@ -116,10 +116,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"), }; - self.write_primval(dest, old)?; + self.write_primval(dest, old, ty)?; + let kind = self.ty_to_primval_kind(ty)?; // FIXME: what do atomics do on overflow? - let (val, _) = primval::binary_op(mir::BinOp::Add, old, change)?; - self.write_primval(Lvalue::from_ptr(ptr), val)?; + let (val, _) = primval::binary_op(mir::BinOp::Add, old, kind, change, kind)?; + self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; }, "atomic_xsub_rel" => { @@ -132,10 +133,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_xsub_rel doesn't work with nonprimitives"), }; - self.write_primval(dest, old)?; + self.write_primval(dest, old, ty)?; + let kind = self.ty_to_primval_kind(ty)?; // FIXME: what do atomics do on overflow? - let (val, _) = primval::binary_op(mir::BinOp::Sub, old, change)?; - self.write_primval(Lvalue::from_ptr(ptr), val)?; + let (val, _) = primval::binary_op(mir::BinOp::Sub, old, kind, change, kind)?; + self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; } "breakpoint" => unimplemented!(), // halt miri @@ -148,8 +150,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; - let count = self.value_to_primval(arg_vals[2], usize)? - .expect_uint("arith_offset second arg not isize"); + let count = self.value_to_primval(arg_vals[2], usize)?.to_u64(); self.memory.copy(src, dest, count * elem_size, elem_align)?; } @@ -157,17 +158,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "cttz" | "ctlz" | "bswap" => { - let elem_ty = substs.type_at(0); - let num = self.value_to_primval(arg_vals[0], elem_ty)?; - let num = numeric_intrinsic(intrinsic_name, num); - self.write_primval(dest, num)?; + let ty = substs.type_at(0); + let num = self.value_to_primval(arg_vals[0], ty)?; + let kind = self.ty_to_primval_kind(ty)?; + let num = numeric_intrinsic(intrinsic_name, num, kind); + self.write_primval(dest, num, ty)?; } "discriminant_value" => { let ty = substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?; + self.write_primval(dest, PrimVal::new(discr_val), dest_ty)?; } "drop_in_place" => { @@ -196,23 +198,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fabsf32" => { - let f = self.value_to_primval(arg_vals[2], f32)? - .expect_f32("fabsf32 read non f32"); - self.write_primval(dest, PrimVal::from_f32(f.abs()))?; + let f = self.value_to_primval(arg_vals[2], f32)?.to_f32(); + self.write_primval(dest, PrimVal::from_f32(f.abs()), dest_ty)?; } "fabsf64" => { - let f = self.value_to_primval(arg_vals[2], f64)? - .expect_f64("fabsf64 read non f64"); - self.write_primval(dest, PrimVal::from_f64(f.abs()))?; + let f = self.value_to_primval(arg_vals[2], f64)?.to_f64(); + self.write_primval(dest, PrimVal::from_f64(f.abs()), dest_ty)?; } "fadd_fast" => { let ty = substs.type_at(0); + let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[0], ty)?; - let result = primval::binary_op(mir::BinOp::Add, a, b)?; - self.write_primval(dest, result.0)?; + let result = primval::binary_op(mir::BinOp::Add, a, kind, b, kind)?; + self.write_primval(dest, result.0, dest_ty)?; } "likely" | @@ -220,27 +221,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => {} "init" => { - let size = dest_layout.size(&self.tcx.data_layout).bytes(); + let size = self.type_size(dest_ty)?.expect("cannot init unsized value");; let init = |this: &mut Self, val: Option| { - match val { + let zero_val = match val { Some(Value::ByRef(ptr)) => { this.memory.write_repeat(ptr, 0, size)?; - Ok(Some(Value::ByRef(ptr))) + Value::ByRef(ptr) }, None => match this.ty_to_primval_kind(dest_ty) { - Ok(kind) => Ok(Some(Value::ByVal(PrimVal::new(0, kind)))), + Ok(_) => Value::ByVal(PrimVal::new(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; this.memory.write_repeat(ptr, 0, size)?; - Ok(Some(Value::ByRef(ptr))) + Value::ByRef(ptr) } }, - Some(Value::ByVal(value)) => Ok(Some(Value::ByVal(PrimVal::new(0, value.kind)))), - Some(Value::ByValPair(a, b)) => Ok(Some(Value::ByValPair( - PrimVal::new(0, a.kind), - PrimVal::new(0, b.kind), - ))), - } + Some(Value::ByVal(_)) => Value::ByVal(PrimVal::new(0)), + Some(Value::ByValPair(..)) => + Value::ByValPair(PrimVal::new(0), PrimVal::new(0)), + }; + Ok(Some(zero_val)) }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, @@ -253,16 +253,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "min_align_of" => { let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty)?; - let align_val = self.usize_primval(elem_align as u64); - self.write_primval(dest, align_val)?; + let align_val = PrimVal::from_uint(elem_align as u64); + self.write_primval(dest, align_val, dest_ty)?; } "pref_align_of" => { let ty = substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); - let align_val = self.usize_primval(align); - self.write_primval(dest, align_val)?; + let align_val = PrimVal::from_uint(align); + self.write_primval(dest, align_val, dest_ty)?; } "move_val_init" => { @@ -275,59 +275,52 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); - self.write_primval(dest, PrimVal::from_bool(needs_drop))?; + self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } "offset" => { let pointee_ty = substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - let offset = self.value_to_primval(arg_vals[1], isize)? - .expect_int("offset second arg not isize"); + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.signed_offset(offset * pointee_size); - self.write_primval(dest, PrimVal::from_ptr(result_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(result_ptr), dest_ty)?; } "overflowing_sub" => { - self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest)?; + self.intrinsic_overflowing(mir::BinOp::Sub, &args[0], &args[1], dest, dest_ty)?; } "overflowing_mul" => { - self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest)?; + self.intrinsic_overflowing(mir::BinOp::Mul, &args[0], &args[1], dest, dest_ty)?; } "overflowing_add" => { - self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest)?; + self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?; } "powif32" => { - let f = self.value_to_primval(arg_vals[0], f32)? - .expect_f32("powif32 first arg not f32"); - let i = self.value_to_primval(arg_vals[1], i32)? - .expect_int("powif32 second arg not i32"); - self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?; + let f = self.value_to_primval(arg_vals[0], f32)?.to_f32(); + let i = self.value_to_primval(arg_vals[1], i32)?.to_i64(); + self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?; } "powif64" => { - let f = self.value_to_primval(arg_vals[0], f64)? - .expect_f64("powif64 first arg not f64"); - let i = self.value_to_primval(arg_vals[1], i32)? - .expect_int("powif64 second arg not i32"); - self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?; + let f = self.value_to_primval(arg_vals[0], f64)?.to_f64(); + let i = self.value_to_primval(arg_vals[1], i32)?.to_i64(); + self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?; } "sqrtf32" => { - let f = self.value_to_primval(arg_vals[0], f32)? - .expect_f32("sqrtf32 first arg not f32"); - self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?; + let f = self.value_to_primval(arg_vals[0], f32)?.to_f32(); + self.write_primval(dest, PrimVal::from_f32(f.sqrt()), dest_ty)?; } "sqrtf64" => { - let f = self.value_to_primval(arg_vals[0], f64)? - .expect_f64("sqrtf64 first arg not f64"); - self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?; + let f = self.value_to_primval(arg_vals[0], f64)?.to_f64(); + self.write_primval(dest, PrimVal::from_f64(f.sqrt()), dest_ty)?; } "size_of" => { @@ -337,23 +330,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // .expect("size_of intrinsic called on unsized value") // see https://github.com/rust-lang/rust/pull/37708 let size = self.type_size(ty)?.unwrap_or(!0) as u64; - let size_val = self.usize_primval(size); - self.write_primval(dest, size_val)?; + self.write_primval(dest, PrimVal::from_uint(size), dest_ty)?; } "size_of_val" => { let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; - let size_val = self.usize_primval(size); - self.write_primval(dest, size_val)?; + self.write_primval(dest, PrimVal::from_uint(size), dest_ty)?; } "min_align_of_val" | "align_of_val" => { let ty = substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; - let align_val = self.usize_primval(align); - self.write_primval(dest, align_val)?; + self.write_primval(dest, PrimVal::from_uint(align), dest_ty)?; } "type_name" => { @@ -365,17 +355,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); - self.write_primval(dest, PrimVal::new(n, PrimValKind::U64))?; + self.write_primval(dest, PrimVal::new(n), dest_ty)?; } "transmute" => { let dest_ty = substs.type_at(1); - let val = match arg_vals[0] { - Value::ByVal(primval) => - Value::ByVal(self.transmute_primval(primval, dest_ty)?), - v => v, - }; - self.write_value(val, dest, dest_ty)?; + self.write_value(arg_vals[0], dest, dest_ty)?; } "uninit" => { @@ -511,11 +496,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } macro_rules! integer_intrinsic { - ($name:expr, $val:expr, $method:ident) => ({ + ($name:expr, $val:expr, $kind:expr, $method:ident) => ({ let val = $val; use primval::PrimValKind::*; - let bits = match val.kind { + let bits = match $kind { I8 => (val.bits as i8).$method() as u64, U8 => (val.bits as u8).$method() as u64, I16 => (val.bits as i16).$method() as u64, @@ -527,16 +512,16 @@ macro_rules! integer_intrinsic { _ => bug!("invalid `{}` argument: {:?}", $name, val), }; - PrimVal::new(bits, val.kind) + PrimVal::new(bits) }); } -fn numeric_intrinsic(name: &str, val: PrimVal) -> PrimVal { +fn numeric_intrinsic(name: &str, val: PrimVal, kind: PrimValKind) -> PrimVal { match name { - "bswap" => integer_intrinsic!("bswap", val, swap_bytes), - "ctlz" => integer_intrinsic!("ctlz", val, leading_zeros), - "ctpop" => integer_intrinsic!("ctpop", val, count_ones), - "cttz" => integer_intrinsic!("cttz", val, trailing_zeros), + "bswap" => integer_intrinsic!("bswap", val, kind, swap_bytes), + "ctlz" => integer_intrinsic!("ctlz", val, kind, leading_zeros), + "ctpop" => integer_intrinsic!("ctpop", val, kind, count_ones), + "cttz" => integer_intrinsic!("cttz", val, kind, trailing_zeros), _ => bug!("not a numeric intrinsic: {}", name), } } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 8f58105de4bb8..a346b8fafa5c6 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -100,7 +100,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { terminator.source_info.span)? } - _ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))), + _ => { + let msg = format!("can't handle callee of type {:?}", func_ty); + return Err(EvalError::Unimplemented(msg)); + } } } @@ -126,11 +129,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { let span = terminator.source_info.span; - let len = self.eval_operand_to_primval(len).expect("can't eval len") - .expect_uint("BoundsCheck len wasn't a uint"); + let len = self.eval_operand_to_primval(len) + .expect("can't eval len") + .to_u64(); let index = self.eval_operand_to_primval(index) .expect("can't eval index") - .expect_uint("BoundsCheck index wasn't a uint"); + .to_u64(); Err(EvalError::ArrayIndexOutOfBounds(span, len, index)) }, mir::AssertMessage::Math(ref err) => @@ -194,9 +198,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::C => { let ty = fn_ty.sig.0.output; - let size = self.type_size(ty)?.expect("function return type cannot be unsized"); let (ret, target) = destination.unwrap(); - self.call_c_abi(def_id, arg_operands, ret, size)?; + self.call_c_abi(def_id, arg_operands, ret, ty)?; self.goto_block(target); Ok(()) } @@ -303,7 +306,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, - dest_size: u64, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); @@ -325,36 +328,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { - let size = self.value_to_primval(args[0], usize)? - .expect_uint("__rust_allocate first arg not usize"); - let align = self.value_to_primval(args[1], usize)? - .expect_uint("__rust_allocate second arg not usize"); + let size = self.value_to_primval(args[0], usize)?.to_u64(); + let align = self.value_to_primval(args[1], usize)?.to_u64(); let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::from_ptr(ptr))?; + self.write_primval(dest, PrimVal::from_ptr(ptr), dest_ty)?; } "__rust_deallocate" => { let ptr = args[0].read_ptr(&self.memory)?; // FIXME: insert sanity check for size and align? - let _old_size = self.value_to_primval(args[1], usize)? - .expect_uint("__rust_deallocate second arg not usize"); - let _align = self.value_to_primval(args[2], usize)? - .expect_uint("__rust_deallocate third arg not usize"); + let _old_size = self.value_to_primval(args[1], usize)?.to_u64(); + let _align = self.value_to_primval(args[2], usize)?.to_u64(); self.memory.deallocate(ptr)?; }, "__rust_reallocate" => { let ptr = args[0].read_ptr(&self.memory)?; - let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); - let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); + let size = self.value_to_primval(args[2], usize)?.to_u64(); + let align = self.value_to_primval(args[3], usize)?.to_u64(); let new_ptr = self.memory.reallocate(ptr, size, align)?; - self.write_primval(dest, PrimVal::from_ptr(new_ptr))?; + self.write_primval(dest, PrimVal::from_ptr(new_ptr), dest_ty)?; } "memcmp" => { let left = args[0].read_ptr(&self.memory)?; let right = args[1].read_ptr(&self.memory)?; - let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize"); + let n = self.value_to_primval(args[2], usize)?.to_u64(); let result = { let left_bytes = self.memory.read_bytes(left, n)?; @@ -368,7 +367,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - self.write_primval(dest, PrimVal::from_int_with_size(result, dest_size))?; + self.write_primval(dest, PrimVal::new(result as u64), dest_ty)?; } _ => { diff --git a/src/memory.rs b/src/memory.rs index c4c045c121b10..3d05300f0ab59 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -11,7 +11,7 @@ use rustc::ty::layout::{self, TargetDataLayout}; use syntax::abi::Abi; use error::{EvalError, EvalResult}; -use primval::PrimVal; +use primval::{PrimVal, PrimValKind}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -559,13 +559,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { + pub fn write_primval( + &mut self, + dest: Pointer, + val: PrimVal, + kind: PrimValKind, + ) -> EvalResult<'tcx, ()> { if let Some(alloc_id) = val.relocation { return self.write_ptr(dest, Pointer::new(alloc_id, val.bits)); } use primval::PrimValKind::*; - let (size, bits) = match val.kind { + let (size, bits) = match kind { I8 | U8 | Bool => (1, val.bits as u8 as u64), I16 | U16 => (2, val.bits as u16 as u64), I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), diff --git a/src/primval.rs b/src/primval.rs index 20b08b5a0a1b6..86bd573518ac4 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -38,11 +38,6 @@ pub struct PrimVal { /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only /// large enough to contain one, hence the `Option`. pub relocation: Option, - - // FIXME(solson): I think we can make this field unnecessary, or at least move it outside of - // this struct. We can either match over `Ty`s or generate simple `PrimVal`s from `Ty`s and - // match over those to decide which operations to perform on `PrimVal`s. - pub kind: PrimValKind, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -87,44 +82,40 @@ impl PrimValKind { } impl PrimVal { - pub fn new(bits: u64, kind: PrimValKind) -> Self { - PrimVal { bits: bits, relocation: None, kind: kind } + pub fn new(bits: u64) -> Self { + PrimVal { bits: bits, relocation: None } } - pub fn new_with_relocation(bits: u64, kind: PrimValKind, alloc_id: AllocId) -> Self { - PrimVal { bits: bits, relocation: Some(alloc_id), kind: kind } + pub fn new_with_relocation(bits: u64, alloc_id: AllocId) -> Self { + PrimVal { bits: bits, relocation: Some(alloc_id) } } pub fn from_ptr(ptr: Pointer) -> Self { - PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::Ptr, ptr.alloc_id) - } - - pub fn from_fn_ptr(ptr: Pointer) -> Self { - PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::FnPtr, ptr.alloc_id) + PrimVal::new_with_relocation(ptr.offset as u64, ptr.alloc_id) } pub fn from_bool(b: bool) -> Self { - PrimVal::new(b as u64, PrimValKind::Bool) + PrimVal::new(b as u64) } pub fn from_char(c: char) -> Self { - PrimVal::new(c as u64, PrimValKind::Char) + PrimVal::new(c as u64) } pub fn from_f32(f: f32) -> Self { - PrimVal::new(f32_to_bits(f), PrimValKind::F32) + PrimVal::new(f32_to_bits(f)) } pub fn from_f64(f: f64) -> Self { - PrimVal::new(f64_to_bits(f), PrimValKind::F64) + PrimVal::new(f64_to_bits(f)) } - pub fn from_uint_with_size(n: u64, size: u64) -> Self { - PrimVal::new(n, PrimValKind::from_uint_size(size)) + pub fn from_uint(n: u64) -> Self { + PrimVal::new(n) } - pub fn from_int_with_size(n: i64, size: u64) -> Self { - PrimVal::new(n as u64, PrimValKind::from_int_size(size)) + pub fn from_int(n: i64) -> Self { + PrimVal::new(n as u64) } pub fn to_f32(self) -> f32 { @@ -144,31 +135,27 @@ impl PrimVal { } pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { - self.to_ptr().to_int().map(|val| val as u64) + self.to_ptr().to_int().map(|val| val) } - pub fn expect_uint(self, error_msg: &str) -> u64 { - if let Ok(int) = self.try_as_uint() { - return int; - } - - use self::PrimValKind::*; - match self.kind { - U8 | U16 | U32 | U64 => self.bits, - _ => bug!("{}", error_msg), + pub fn to_u64(self) -> u64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as u64; } + self.bits } - pub fn expect_int(self, error_msg: &str) -> i64 { - if let Ok(int) = self.try_as_uint() { - return int as i64; + pub fn to_i64(self) -> i64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as i64; } + self.bits as i64 + } - use self::PrimValKind::*; - match self.kind { - I8 | I16 | I32 | I64 => self.bits as i64, - _ => bug!("{}", error_msg), - } + pub fn try_as_ptr(self) -> Option { + self.relocation.map(|alloc_id| { + Pointer::new(alloc_id, self.bits) + }) } pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { @@ -179,19 +166,6 @@ impl PrimVal { } } - pub fn expect_f32(self, error_msg: &str) -> f32 { - match self.kind { - PrimValKind::F32 => bits_to_f32(self.bits), - _ => bug!("{}", error_msg), - } - } - - pub fn expect_f64(self, error_msg: &str) -> f64 { - match self.kind { - PrimValKind::F32 => bits_to_f64(self.bits), - _ => bug!("{}", error_msg), - } - } } //////////////////////////////////////////////////////////////////////////////// @@ -199,9 +173,9 @@ impl PrimVal { //////////////////////////////////////////////////////////////////////////////// macro_rules! overflow { - ($kind:expr, $op:ident, $l:expr, $r:expr) => ({ + ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); - let primval = PrimVal::new(val as u64, $kind); + let primval = PrimVal::new(val as u64); Ok((primval, overflowed)) }) } @@ -211,14 +185,14 @@ macro_rules! int_arithmetic { let l = $l; let r = $r; match $kind { - I8 => overflow!(I8, $int_op, l as i8, r as i8), - I16 => overflow!(I16, $int_op, l as i16, r as i16), - I32 => overflow!(I32, $int_op, l as i32, r as i32), - I64 => overflow!(I64, $int_op, l as i64, r as i64), - U8 => overflow!(U8, $int_op, l as u8, r as u8), - U16 => overflow!(U16, $int_op, l as u16, r as u16), - U32 => overflow!(U32, $int_op, l as u32, r as u32), - U64 => overflow!(U64, $int_op, l as u64, r as u64), + I8 => overflow!($int_op, l as i8, r as i8), + I16 => overflow!($int_op, l as i16, r as i16), + I32 => overflow!($int_op, l as i32, r as i32), + I64 => overflow!($int_op, l as i64, r as i64), + U8 => overflow!($int_op, l as u8, r as u8), + U16 => overflow!($int_op, l as u16, r as u16), + U32 => overflow!($int_op, l as u32, r as u32), + U64 => overflow!($int_op, l as u64, r as u64), _ => bug!("int_arithmetic should only be called on int primvals"), } }) @@ -229,37 +203,37 @@ macro_rules! int_shift { let l = $l; let r = $r; match $kind { - I8 => overflow!(I8, $int_op, l as i8, r), - I16 => overflow!(I16, $int_op, l as i16, r), - I32 => overflow!(I32, $int_op, l as i32, r), - I64 => overflow!(I64, $int_op, l as i64, r), - U8 => overflow!(U8, $int_op, l as u8, r), - U16 => overflow!(U16, $int_op, l as u16, r), - U32 => overflow!(U32, $int_op, l as u32, r), - U64 => overflow!(U64, $int_op, l as u64, r), + I8 => overflow!($int_op, l as i8, r), + I16 => overflow!($int_op, l as i16, r), + I32 => overflow!($int_op, l as i32, r), + I64 => overflow!($int_op, l as i64, r), + U8 => overflow!($int_op, l as u8, r), + U16 => overflow!($int_op, l as u16, r), + U32 => overflow!($int_op, l as u32, r), + U64 => overflow!($int_op, l as u64, r), _ => bug!("int_shift should only be called on int primvals"), } }) } macro_rules! float_arithmetic { - ($kind:expr, $from_bits:ident, $to_bits:ident, $float_op:tt, $l:expr, $r:expr) => ({ + ($from_bits:ident, $to_bits:ident, $float_op:tt, $l:expr, $r:expr) => ({ let l = $from_bits($l); let r = $from_bits($r); let bits = $to_bits(l $float_op r); - PrimVal::new(bits, $kind) + PrimVal::new(bits) }) } macro_rules! f32_arithmetic { ($float_op:tt, $l:expr, $r:expr) => ( - float_arithmetic!(F32, bits_to_f32, f32_to_bits, $float_op, $l, $r) + float_arithmetic!(bits_to_f32, f32_to_bits, $float_op, $l, $r) ) } macro_rules! f64_arithmetic { ($float_op:tt, $l:expr, $r:expr) => ( - float_arithmetic!(F64, bits_to_f64, f64_to_bits, $float_op, $l, $r) + float_arithmetic!(bits_to_f64, f64_to_bits, $float_op, $l, $r) ) } @@ -267,7 +241,9 @@ macro_rules! f64_arithmetic { pub fn binary_op<'tcx>( bin_op: mir::BinOp, left: PrimVal, - right: PrimVal + left_kind: PrimValKind, + right: PrimVal, + right_kind: PrimValKind, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; use self::PrimValKind::*; @@ -288,7 +264,7 @@ pub fn binary_op<'tcx>( // These are the maximum values a bitshift RHS could possibly have. For example, u16 // can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in // that range. - let type_bits: u32 = match left.kind { + let type_bits: u32 = match left_kind { I8 | U8 => 8, I16 | U16 => 16, I32 | U32 => 32, @@ -301,18 +277,18 @@ pub fn binary_op<'tcx>( let r = (right.bits as u32) & (type_bits - 1); return match bin_op { - Shl => int_shift!(left.kind, overflowing_shl, l, r), - Shr => int_shift!(left.kind, overflowing_shr, l, r), + Shl => int_shift!(left_kind, overflowing_shl, l, r), + Shr => int_shift!(left_kind, overflowing_shr, l, r), _ => bug!("it has already been checked that this is a shift op"), }; } - if left.kind != right.kind { + if left_kind != right_kind { let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); return Err(EvalError::Unimplemented(msg)); } - let val = match (bin_op, left.kind) { + let val = match (bin_op, left_kind) { (Eq, F32) => PrimVal::from_bool(bits_to_f32(l) == bits_to_f32(r)), (Ne, F32) => PrimVal::from_bool(bits_to_f32(l) != bits_to_f32(r)), (Lt, F32) => PrimVal::from_bool(bits_to_f32(l) < bits_to_f32(r)), @@ -346,9 +322,9 @@ pub fn binary_op<'tcx>( (Gt, _) => PrimVal::from_bool(l > r), (Ge, _) => PrimVal::from_bool(l >= r), - (BitOr, k) => PrimVal::new(l | r, k), - (BitAnd, k) => PrimVal::new(l & r, k), - (BitXor, k) => PrimVal::new(l ^ r, k), + (BitOr, _) => PrimVal::new(l | r), + (BitAnd, _) => PrimVal::new(l & r), + (BitXor, _) => PrimVal::new(l ^ r), (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), @@ -378,11 +354,15 @@ fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> } } -pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { +pub fn unary_op<'tcx>( + un_op: mir::UnOp, + val: PrimVal, + val_kind: PrimValKind, +) -> EvalResult<'tcx, PrimVal> { use rustc::mir::UnOp::*; use self::PrimValKind::*; - let bits = match (un_op, val.kind) { + let bits = match (un_op, val_kind) { (Not, Bool) => !bits_to_bool(val.bits) as u64, (Not, U8) => !(val.bits as u8) as u64, @@ -409,5 +389,5 @@ pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVa } }; - Ok(PrimVal::new(bits, val.kind)) + Ok(PrimVal::new(bits)) } From fc3e1c0064abbc39650b8d46e6e8c459e739e8ad Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 22:59:21 -0800 Subject: [PATCH 0668/1096] compiletest: Don't automatically enable MIRI_LOG=trace. --- tests/compiletest.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 181f06ba1ebd2..1ee86a07b22f5 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -61,8 +61,6 @@ fn compile_test() { let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { Box::new(files.chain(std::fs::read_dir(path).unwrap())) } else { - // print traces only when not running on the rust run-pass test suite (since tracing is slow) - std::env::set_var("MIRI_LOG", "trace"); Box::new(files) }; let mut mir_not_found = 0; From f7cd07a6158fbbc1760922a95166ce3ede65fee4 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 23:20:15 -0800 Subject: [PATCH 0669/1096] Produce PrimValKinds for small, simple layout ADTs. --- src/interpreter/mod.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 82b949d453fb5..ac2e5cd7967bd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1364,17 +1364,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; - if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { - let size = discr.size().bytes(); - if signed { - PrimValKind::from_int_size(size) - } else { - PrimValKind::from_uint_size(size) + match *self.type_layout(ty)? { + CEnum { discr, signed, .. } => { + let size = discr.size().bytes(); + if signed { + PrimValKind::from_int_size(size) + } else { + PrimValKind::from_uint_size(size) + } } - } else { - return Err(EvalError::TypeNotPrimitive(ty)); + + RawNullablePointer { value, .. } => { + use rustc::ty::layout::Primitive::*; + match value { + // TODO(solson): Does signedness matter here? What should the sign be? + Int(int) => PrimValKind::from_uint_size(int.size().bytes()), + F32 => PrimValKind::F32, + F64 => PrimValKind::F64, + Pointer => PrimValKind::Ptr, + } + } + + _ => return Err(EvalError::TypeNotPrimitive(ty)), } - }, + } _ => return Err(EvalError::TypeNotPrimitive(ty)), }; From 0929201d605076ae03bddbeb3b3458e6eb4d338a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 23:37:38 -0800 Subject: [PATCH 0670/1096] Remove useless binding. --- src/interpreter/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ac2e5cd7967bd..c5794e63e8f6a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -282,8 +282,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = ty.subst(self.tcx, substs); - let new = self.tcx.normalize_associated_type(&substituted); - new + self.tcx.normalize_associated_type(&substituted) } fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { From fd022857880175900dd22533ec7243ff0fa5ad49 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 26 Nov 2016 23:42:17 -0800 Subject: [PATCH 0671/1096] Remove unnecessary transmute_primval function. --- src/interpreter/cast.rs | 4 ++-- src/interpreter/mod.rs | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 4b0c91b00ab35..7a0285da440ed 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -97,8 +97,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { - TyRef(..) | TyRawPtr(_) | TyFnPtr(_) => Ok(PrimVal::from_ptr(ptr)), - TyInt(_) | TyUint(_) => self.transmute_primval(PrimVal::from_ptr(ptr), ty), + TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => + Ok(PrimVal::from_ptr(ptr)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c5794e63e8f6a..63d3029e311e9 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1165,20 +1165,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("follow_by_ref_value can't result in `ByRef`"), Value::ByVal(primval) => { - let new_primval = self.transmute_primval(primval, ty)?; - self.ensure_valid_value(new_primval, ty)?; - Ok(new_primval) + self.ensure_valid_value(primval, ty)?; + Ok(primval) } Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), } } - // FIXME(solson): Delete this. - fn transmute_primval(&self, val: PrimVal, _ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - Ok(val) - } - fn write_primval( &mut self, dest: Lvalue<'tcx>, From 71cc1226c7ad1ee1491473c850811f8aa638936b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 27 Nov 2016 13:46:34 -0800 Subject: [PATCH 0672/1096] s/init/zero/ --- src/interpreter/terminator/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 64562ccb9178e..16cdffe24ceb0 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -221,7 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => {} "init" => { - let size = self.type_size(dest_ty)?.expect("cannot init unsized value");; + let size = self.type_size(dest_ty)?.expect("cannot zero unsized value");; let init = |this: &mut Self, val: Option| { let zero_val = match val { Some(Value::ByRef(ptr)) => { From e4910e437b006fe4ad3dce191136cb81d04c0d72 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 27 Nov 2016 19:08:06 -0800 Subject: [PATCH 0673/1096] Remove useless map. --- src/primval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primval.rs b/src/primval.rs index 86bd573518ac4..83d9c18f34f67 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -135,7 +135,7 @@ impl PrimVal { } pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { - self.to_ptr().to_int().map(|val| val) + self.to_ptr().to_int() } pub fn to_u64(self) -> u64 { From 244ae8eac7b4e0ea668f69feb25c525fb4b22281 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 28 Nov 2016 20:22:21 -0800 Subject: [PATCH 0674/1096] Introduce try_read_value to avoid allocations. Attempt reading a primitive value out of any source lvalue and write that into the destination without making an allocation if possible. --- src/interpreter/mod.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 63d3029e311e9..c1b6e1e63b6e8 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1265,12 +1265,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // if they referred to the same allocation, since then a change to one would // implicitly change the other. // - // TODO(solson): It would be valid to attempt reading a primitive value out of - // the source and writing that into the destination without making an - // allocation. This would be a pure optimization. - let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, dest_ptr, dest_ty)?; - write_dest(self, Value::ByRef(dest_ptr)); + // It is a valid optimization to attempt reading a primitive value out of the + // source and write that into the destination without making an allocation, so + // we do so here. + if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { + write_dest(self, src_val); + } else { + let dest_ptr = self.alloc_ptr(dest_ty)?; + self.copy(src_ptr, dest_ptr, dest_ty)?; + write_dest(self, Value::ByRef(dest_ptr)); + } } else { // Finally, we have the simple case where neither source nor destination are @@ -1400,6 +1404,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + if let Some(val) = self.try_read_value(ptr, ty)? { + Ok(val) + } else { + bug!("primitive read failed for type: {:?}", ty); + } + } + + fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; let val = match ty.sty { @@ -1439,11 +1451,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), - // TODO(solson): Should this even be here? Fn items aren't primvals, are they? - ty::TyFnDef(def_id, substs, fn_ty) => { - PrimVal::from_ptr(self.memory.create_fn_ptr(self.tcx, def_id, substs, fn_ty)) - }, - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_ptr)?, ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut { ty, .. }) | @@ -1460,7 +1467,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; - return Ok(Value::ByValPair(PrimVal::from_ptr(p), extra)); + return Ok(Some(Value::ByValPair(PrimVal::from_ptr(p), extra))); } } @@ -1474,14 +1481,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::from_uint(self.memory.read_uint(ptr, size)?) } } else { - bug!("primitive read of non-clike enum: {:?}", ty); + return Ok(None); } }, - _ => bug!("primitive read of non-primitive type: {:?}", ty), + _ => return Ok(None), }; - Ok(Value::ByVal(val)) + Ok(Some(Value::ByVal(val))) } fn frame(&self) -> &Frame<'tcx> { From b96202b3cd20765126aeb3fe9f2931af9809ff4d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 2 Dec 2016 19:44:59 -0800 Subject: [PATCH 0675/1096] Update for changes in rustc. --- src/interpreter/mod.rs | 12 ++++++------ src/interpreter/terminator/intrinsics.rs | 4 ++-- src/interpreter/terminator/mod.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c1b6e1e63b6e8..1a52434f9fc1f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -771,7 +771,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (field_index, &self.tcx.struct_tail(ty).sty) { (1, &ty::TyStr) | (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyTrait(_)) | + (1, &ty::TyDynamic(..)) | (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), _ => bug!("invalid fat pointee type: {}", ty), } @@ -1009,7 +1009,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("deref to {} on {:?}", pointee_type, val); match self.tcx.struct_tail(pointee_type).sty { - ty::TyTrait(_) => { + ty::TyDynamic(..) => { let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; (ptr, LvalueExtra::Vtable(vtable)) }, @@ -1462,7 +1462,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("reading fat pointer extra of type {}", ty); let extra = ptr.offset(self.memory.pointer_size()); let extra = match self.tcx.struct_tail(ty).sty { - ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), + ty::TyDynamic(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), @@ -1529,14 +1529,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = PrimVal::from_ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } - (&ty::TyTrait(_), &ty::TyTrait(_)) => { + (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. self.write_value(src, dest, dest_ty)?; }, - (_, &ty::TyTrait(ref data)) => { - let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); + (_, &ty::TyDynamic(ref data, _)) => { + let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 16cdffe24ceb0..706fadd0ab41a 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(ptr, extra) => Lvalue::Ptr { ptr: ptr.to_ptr(), extra: match self.tcx.struct_tail(ty).sty { - ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()), ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), _ => bug!("invalid fat pointer type: {}", ptr_ty), }, @@ -465,7 +465,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((size, align)) } } - ty::TyTrait(..) => { + ty::TyDynamic(..) => { let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. let size = self.memory.read_usize(vtable.offset(pointer_size))?; diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index a346b8fafa5c6..e95d68a67db1e 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(prim_ptr, extra) => { let ptr = prim_ptr.to_ptr(); let extra = match self.tcx.struct_tail(contents_ty).sty { - ty::TyTrait(_) => LvalueExtra::Vtable(extra.to_ptr()), + ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()), ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), _ => bug!("invalid fat pointer type: {}", ty), }; @@ -640,7 +640,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; }, - ty::TyTrait(_) => { + ty::TyDynamic(..) => { let (ptr, vtable) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), _ => bug!("expected an lvalue with a vtable"), From c303ac001d7c2a47c12a93918d9b73aab5906da6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Dec 2016 15:41:28 +0100 Subject: [PATCH 0676/1096] rustup --- src/interpreter/mod.rs | 2 +- src/interpreter/terminator/intrinsics.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1a52434f9fc1f..999fec73bd774 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1798,7 +1798,7 @@ impl IntegerExt for layout::Integer { } -pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: ty::FieldDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { +pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::FieldDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = &f.ty(tcx, substs); tcx.normalize_associated_type(&substituted) } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 706fadd0ab41a..1781211c9c70e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -489,8 +489,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn field_ty( &self, param_substs: &Substs<'tcx>, - f: ty::FieldDef<'tcx>, - )-> ty::Ty<'tcx> { + f: &ty::FieldDef, + ) -> ty::Ty<'tcx> { self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) } } From 360ef490f47040e7baac28affb6a0ce531410f2f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Dec 2016 16:16:22 +0100 Subject: [PATCH 0677/1096] supply a real "caller" span to drop calls --- src/interpreter/terminator/intrinsics.rs | 6 +++++- src/interpreter/terminator/mod.rs | 6 ++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 1781211c9c70e..9c7b13adf76b6 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -190,11 +190,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let mut drops = Vec::new(); self.drop(lvalue, ty, &mut drops)?; + let span = { + let frame = self.frame(); + frame.mir[frame.block].terminator().source_info.span + }; // need to change the block before pushing the drop impl stack frames // we could do this for all intrinsics before evaluating the intrinsics, but if // the evaluation fails, we should not have moved forward self.goto_block(target); - return self.eval_drop_impls(drops); + return self.eval_drop_impls(drops, span); } "fabsf32" => { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index e95d68a67db1e..0b80b633829c5 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut drops = Vec::new(); self.drop(lval, ty, &mut drops)?; self.goto_block(target); - self.eval_drop_impls(drops)?; + self.eval_drop_impls(drops, terminator.source_info.span)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -151,12 +151,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>) -> EvalResult<'tcx, ()> { - let span = self.frame().span; + pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx, ()> { // add them to the stack in reverse order, because the impl that needs to run the last // is the one that needs to be at the bottom of the stack for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { - // FIXME: supply a real span let mir = self.load_mir(drop_def_id)?; trace!("substs for drop glue: {:?}", substs); self.push_stack_frame( From bfe1efcbf8a30974800ec797bc9fb7307e4b38fb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Dec 2016 18:13:11 +0100 Subject: [PATCH 0678/1096] stop leaking memory on closure calls --- src/interpreter/mod.rs | 35 +++++++++------------------- src/interpreter/step.rs | 5 ++-- src/interpreter/terminator/mod.rs | 38 ++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 999fec73bd774..f5da33d4b3782 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -82,6 +82,12 @@ pub struct Frame<'tcx> { /// Before being initialized, a local is simply marked as None. pub locals: Vec>, + /// Temporaries introduced to save stackframes + /// This is pure interpreter magic and has nothing to do with how rustc does it + /// An example is calling an FnMut closure that has been converted to a FnOnce closure + /// If they are Value::ByRef, their memory will be freed when the stackframe finishes + pub interpreter_temporaries: Vec, + //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// @@ -327,6 +333,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, + temporaries: Vec, ) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation += 1; @@ -341,6 +348,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block: return_to_block, return_lvalue: return_lvalue, locals: locals, + interpreter_temporaries: temporaries, span: span, def_id: def_id, substs: substs, @@ -385,9 +393,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::None => {}, } // deallocate all locals that are backed by an allocation - for (i, local) in frame.locals.into_iter().enumerate() { - if let Some(Value::ByRef(ptr)) = local { - trace!("deallocating local {}: {:?}", i + 1, ptr); + for local in frame.locals.into_iter().filter_map(|l| l).chain(frame.interpreter_temporaries) { + if let Value::ByRef(ptr) = local { self.memory.dump(ptr.alloc_id); match self.memory.deallocate(ptr) { // Any frozen memory means that it belongs to a constant or something referenced @@ -1131,27 +1138,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(new_lvalue) } - // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can - // remove it as soon as PrimVal can represent fat pointers. - fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { - match value { - Value::ByRef(ptr) => Ok(ptr), - - Value::ByVal(primval) => { - let ptr = self.alloc_ptr(ty)?; - let kind = self.ty_to_primval_kind(ty)?; - self.memory.write_primval(ptr, primval, kind)?; - Ok(ptr) - } - - Value::ByValPair(a, b) => { - let ptr = self.alloc_ptr(ty)?; - self.write_pair_to_ptr(a, b, ptr, ty)?; - Ok(ptr) - } - } - } - /// ensures this Value is not a ByRef fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match value { @@ -1719,6 +1705,7 @@ pub fn eval_main<'a, 'tcx: 'a>( tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, + Vec::new(), ).expect("could not allocate first stack frame"); loop { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 1d075fe0e9ed2..ff4f3b3aa7003 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -145,7 +145,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } else { StackPopCleanup::None }; - this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup) + this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new()) }); } fn try EvalResult<'tcx, ()>>(&mut self, f: F) { @@ -194,7 +194,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { mir, this.substs, Lvalue::Global(cid), - StackPopCleanup::Freeze) + StackPopCleanup::Freeze, + Vec::new()) }); } } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 0b80b633829c5..8c7d06af41907 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -164,6 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs, Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, + Vec::new(), )?; let mut arg_locals = self.frame().mir.args_iter(); let first = arg_locals.next().expect("drop impl has self arg"); @@ -211,11 +212,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs) = + let (resolved_def_id, resolved_substs, temporaries) = if let Some(trait_id) = self.tcx.trait_of_item(def_id) { self.trait_method(trait_id, def_id, substs, &mut args)? } else { - (def_id, substs) + (def_id, substs, Vec::new()) }; let mir = self.load_mir(resolved_def_id)?; @@ -235,6 +236,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { resolved_substs, return_lvalue, return_to_block, + temporaries, )?; let arg_locals = self.frame().mir.args_iter(); @@ -430,7 +432,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>)> { + ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -442,7 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // and those from the method: let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - Ok((did, substs)) + Ok((did, substs, Vec::new())) } traits::VtableClosure(vtable_closure) => { @@ -453,6 +455,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); self.unpack_fn_args(args)?; + let mut temporaries = Vec::new(); match (closure_kind, trait_closure_kind) { (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | @@ -472,23 +475,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Interpreter magic: insert an intermediate pointer, so we can skip the // intermediate function call. - // FIXME: this is a memory leak, should probably add the pointer to the - // current stack. - let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?; - args[0].0 = Value::ByVal(PrimVal::from_ptr(first)); + let ptr = match args[0].0 { + Value::ByRef(ptr) => ptr, + Value::ByVal(primval) => { + let ptr = self.alloc_ptr(args[0].1)?; + let kind = self.ty_to_primval_kind(args[0].1)?; + self.memory.write_primval(ptr, primval, kind)?; + temporaries.push(Value::ByRef(ptr)); + ptr + }, + Value::ByValPair(a, b) => { + let ptr = self.alloc_ptr(args[0].1)?; + self.write_pair_to_ptr(a, b, ptr, args[0].1)?; + temporaries.push(Value::ByRef(ptr)); + ptr + }, + }; + args[0].0 = Value::ByVal(PrimVal::from_ptr(ptr)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), } - Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs)) + Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries)) } traits::VtableFnPointer(vtable_fn_ptr) => { if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); self.unpack_fn_args(args)?; - Ok((did, substs)) + Ok((did, substs, Vec::new())) } else { bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) } @@ -504,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; *first_ty = sig.inputs[0]; - Ok((def_id, substs)) + Ok((def_id, substs, Vec::new())) } else { Err(EvalError::VtableForArgumentlessMethod) } From c076321a94e626968606610f491439fb8b409a25 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 6 Dec 2016 15:41:28 +0100 Subject: [PATCH 0679/1096] rustup --- src/interpreter/mod.rs | 2 +- src/interpreter/terminator/intrinsics.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1a52434f9fc1f..999fec73bd774 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1798,7 +1798,7 @@ impl IntegerExt for layout::Integer { } -pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: ty::FieldDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { +pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::FieldDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { let substituted = &f.ty(tcx, substs); tcx.normalize_associated_type(&substituted) } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 706fadd0ab41a..1781211c9c70e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -489,8 +489,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn field_ty( &self, param_substs: &Substs<'tcx>, - f: ty::FieldDef<'tcx>, - )-> ty::Ty<'tcx> { + f: &ty::FieldDef, + ) -> ty::Ty<'tcx> { self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs)) } } From 3065273601f2fe7865ffaebbdf750c41f545399c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Dec 2016 09:19:14 +0100 Subject: [PATCH 0680/1096] simplify the interpreter locals, since they always must be backed by an allocation --- src/interpreter/mod.rs | 15 +++++++++++---- src/interpreter/terminator/mod.rs | 6 +++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f5da33d4b3782..a8ccfae3dc7be 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -86,7 +86,7 @@ pub struct Frame<'tcx> { /// This is pure interpreter magic and has nothing to do with how rustc does it /// An example is calling an FnMut closure that has been converted to a FnOnce closure /// If they are Value::ByRef, their memory will be freed when the stackframe finishes - pub interpreter_temporaries: Vec, + pub interpreter_temporaries: Vec, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -333,7 +333,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, - temporaries: Vec, + temporaries: Vec, ) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation += 1; @@ -393,8 +393,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::None => {}, } // deallocate all locals that are backed by an allocation - for local in frame.locals.into_iter().filter_map(|l| l).chain(frame.interpreter_temporaries) { - if let Value::ByRef(ptr) = local { + for local in frame.locals.into_iter() { + if let Some(Value::ByRef(ptr)) = local { + trace!("deallocating local"); self.memory.dump(ptr.alloc_id); match self.memory.deallocate(ptr) { // Any frozen memory means that it belongs to a constant or something referenced @@ -406,6 +407,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } + // deallocate all temporary allocations + for ptr in frame.interpreter_temporaries { + trace!("deallocating temporary allocation"); + self.memory.dump(ptr.alloc_id); + self.memory.deallocate(ptr)?; + } Ok(()) } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 8c7d06af41907..837c701112527 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -432,7 +432,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { + ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -481,13 +481,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.alloc_ptr(args[0].1)?; let kind = self.ty_to_primval_kind(args[0].1)?; self.memory.write_primval(ptr, primval, kind)?; - temporaries.push(Value::ByRef(ptr)); + temporaries.push(ptr); ptr }, Value::ByValPair(a, b) => { let ptr = self.alloc_ptr(args[0].1)?; self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - temporaries.push(Value::ByRef(ptr)); + temporaries.push(ptr); ptr }, }; From 5dd01c309fbe730d378c4a55f8d6aadfe9eabeea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Dec 2016 09:52:22 +0100 Subject: [PATCH 0681/1096] fix documentation --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a8ccfae3dc7be..1b8072190ba6c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -82,10 +82,10 @@ pub struct Frame<'tcx> { /// Before being initialized, a local is simply marked as None. pub locals: Vec>, - /// Temporaries introduced to save stackframes + /// Temporary allocations introduced to save stackframes /// This is pure interpreter magic and has nothing to do with how rustc does it /// An example is calling an FnMut closure that has been converted to a FnOnce closure - /// If they are Value::ByRef, their memory will be freed when the stackframe finishes + /// The memory will be freed when the stackframe finishes pub interpreter_temporaries: Vec, //////////////////////////////////////////////////////////////////////////////// From 4702d970939ae1eff389b1c334c10125a7666d1a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 20:30:37 -0800 Subject: [PATCH 0682/1096] Flatten 'interpreter' mod tree into the root. --- src/{interpreter => }/cast.rs | 0 src/{interpreter/mod.rs => eval_context.rs} | 122 ++++++++++-------- src/lib.rs | 24 ++-- src/{interpreter => }/step.rs | 19 +-- .../terminator/intrinsics.rs | 4 +- src/{interpreter => }/terminator/mod.rs | 4 +- src/{interpreter => }/value.rs | 0 src/{interpreter => }/vtable.rs | 0 8 files changed, 93 insertions(+), 80 deletions(-) rename src/{interpreter => }/cast.rs (100%) rename src/{interpreter/mod.rs => eval_context.rs} (95%) rename src/{interpreter => }/step.rs (98%) rename src/{interpreter => }/terminator/intrinsics.rs (99%) rename src/{interpreter => }/terminator/mod.rs (99%) rename src/{interpreter => }/value.rs (100%) rename src/{interpreter => }/vtable.rs (100%) diff --git a/src/interpreter/cast.rs b/src/cast.rs similarity index 100% rename from src/interpreter/cast.rs rename to src/cast.rs diff --git a/src/interpreter/mod.rs b/src/eval_context.rs similarity index 95% rename from src/interpreter/mod.rs rename to src/eval_context.rs index 1b8072190ba6c..ab6bda12677e0 100644 --- a/src/interpreter/mod.rs +++ b/src/eval_context.rs @@ -1,6 +1,9 @@ -use rustc::middle::const_val::ConstVal; +use std::cell::Ref; +use std::collections::HashMap; + use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; +use rustc::middle::const_val::ConstVal; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; @@ -12,39 +15,32 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; use primval::{self, PrimVal, PrimValKind}; -pub use self::value::Value; -use std::collections::HashMap; -use std::cell::Ref; - -mod step; -mod terminator; -mod cast; -mod vtable; -mod value; +// FIXME(solson): Remove this. +pub use value::Value; pub type MirRef<'tcx> = Ref<'tcx, mir::Mir<'tcx>>; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. - tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub(super) tcx: TyCtxt<'a, 'tcx, 'tcx>, /// The virtual memory system. - memory: Memory<'a, 'tcx>, + pub(super) memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. - globals: HashMap, Global<'tcx>>, + pub(super) globals: HashMap, Global<'tcx>>, /// The virtual call stack. - stack: Vec>, + pub(super) stack: Vec>, /// The maximum number of stack frames allowed - stack_limit: usize, + pub(super) stack_limit: usize, /// The maximum number of operations that may be executed. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. - steps_remaining: u64, + pub(super) steps_remaining: u64, } /// A stack frame. @@ -132,14 +128,16 @@ pub enum LvalueExtra { /// Uniquely identifies a specific constant or static pub struct GlobalId<'tcx> { /// the def id of the constant/static or in case of promoteds, the def id of the function they belong to - def_id: DefId, + pub(super) def_id: DefId, + /// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated /// constants actually have something useful here. We could special case statics and constants, /// but that would only require more branching when working with constants, and not bring any /// real benefits. - substs: &'tcx Substs<'tcx>, - /// this `Option` is `Some` for promoted constants - promoted: Option, + pub(super) substs: &'tcx Substs<'tcx>, + + /// The promoted index for this global, if it is a promoted. + pub(super) promoted: Option, } #[derive(Copy, Clone, Debug)] @@ -150,7 +148,7 @@ pub struct Global<'tcx> { } impl<'tcx> Global<'tcx> { - fn uninitialized(ty: Ty<'tcx>) -> Self { + pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { Global { data: None, mutable: true, @@ -228,7 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { + pub(super) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; @@ -236,7 +234,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByValPair(PrimVal::from_ptr(ptr), PrimVal::from_uint(s.len() as u64))) } - fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { + pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::ConstFloat; @@ -271,7 +269,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(primval)) } - fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) @@ -291,15 +289,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } - fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + pub(super) fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { self.type_size_with_substs(ty, self.substs()) } - fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { + pub(super) fn type_align(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { self.type_align_with_substs(ty, self.substs()) } - fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option> { + fn type_size_with_substs( + &self, + ty: Ty<'tcx>, + substs: &'tcx Substs<'tcx>, + ) -> EvalResult<'tcx, Option> { let layout = self.type_layout_with_substs(ty, substs)?; if layout.is_unsized() { Ok(None) @@ -312,7 +314,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi()) } - fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { + pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> { self.type_layout_with_substs(ty, self.substs()) } @@ -362,7 +364,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn pop_stack_frame(&mut self) -> EvalResult<'tcx, ()> { + pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx, ()> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { @@ -433,7 +435,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Applies the binary operation `op` to the two operands and writes a tuple of the result /// and a boolean signifying the potential overflow to the destination. - fn intrinsic_with_overflow( + pub(super) fn intrinsic_with_overflow( &mut self, op: mir::BinOp, left: &mir::Operand<'tcx>, @@ -448,7 +450,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Applies the binary operation `op` to the arguments and writes the result to the /// destination. Returns `true` if the operation overflowed. - fn intrinsic_overflowing( + pub(super) fn intrinsic_overflowing( &mut self, op: mir::BinOp, left: &mir::Operand<'tcx>, @@ -483,7 +485,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue /// type writes its results directly into the memory specified by the lvalue. - fn eval_rvalue_into_lvalue( + pub(super) fn eval_rvalue_into_lvalue( &mut self, rvalue: &mir::Rvalue<'tcx>, lvalue: &mir::Lvalue<'tcx>, @@ -739,7 +741,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn nonnull_offset_and_ty(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { + pub(super) fn nonnull_offset_and_ty( + &self, + ty: Ty<'tcx>, + nndiscr: u64, + discrfield: &[u32], + ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant let path = discrfield.iter().skip(2).map(|&i| i as usize); @@ -827,13 +834,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { + pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { let value = self.eval_operand(op)?; let ty = self.operand_ty(op); self.value_to_primval(value, ty) } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::Operand::*; match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), @@ -872,7 +879,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Local(index) = proj.base { if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) { @@ -904,7 +911,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { + pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, @@ -1086,11 +1093,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Lvalue::Ptr { ptr: ptr, extra: extra }) } - fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { + pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } - fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { + pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } @@ -1101,7 +1108,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn force_allocation(&mut self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { + pub(super) fn force_allocation( + &mut self, + lvalue: Lvalue<'tcx>, + ) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { Lvalue::Local { frame, local } => { match self.stack[frame].get_local(local) { @@ -1146,14 +1156,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } /// ensures this Value is not a ByRef - fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match value { Value::ByRef(ptr) => self.read_value(ptr, ty), other => Ok(other), } } - fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + pub(super) fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match self.follow_by_ref_value(value, ty)? { Value::ByRef(_) => bug!("follow_by_ref_value can't result in `ByRef`"), @@ -1166,7 +1176,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn write_primval( + pub(super) fn write_primval( &mut self, dest: Lvalue<'tcx>, val: PrimVal, @@ -1194,7 +1204,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn write_value( + pub(super) fn write_value( &mut self, src_val: Value, dest: Lvalue<'tcx>, @@ -1277,7 +1287,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn write_value_to_ptr( + pub(super) fn write_value_to_ptr( &mut self, value: Value, dest: Pointer, @@ -1293,7 +1303,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn write_pair_to_ptr( + pub(super) fn write_pair_to_ptr( &mut self, a: PrimVal, b: PrimVal, @@ -1312,7 +1322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { + pub(super) fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { use syntax::ast::FloatTy; let kind = match ty.sty { @@ -1396,7 +1406,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, ty)? { Ok(val) } else { @@ -1484,19 +1494,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Some(Value::ByVal(val))) } - fn frame(&self) -> &Frame<'tcx> { + pub(super) fn frame(&self) -> &Frame<'tcx> { self.stack.last().expect("no call frames exist") } - pub fn frame_mut(&mut self) -> &mut Frame<'tcx> { + pub(super) fn frame_mut(&mut self) -> &mut Frame<'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn mir(&self) -> MirRef<'tcx> { + pub(super) fn mir(&self) -> MirRef<'tcx> { Ref::clone(&self.frame().mir) } - fn substs(&self) -> &'tcx Substs<'tcx> { + pub(super) fn substs(&self) -> &'tcx Substs<'tcx> { self.frame().substs } @@ -1585,7 +1595,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn dump_local(&self, lvalue: Lvalue<'tcx>) { + pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { if let Lvalue::Local { frame, local } = lvalue { if let Some(val) = self.stack[frame].get_local(local) { match val { @@ -1667,7 +1677,7 @@ impl<'tcx> Lvalue<'tcx> { Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } } - fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { + pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { match self { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), @@ -1675,7 +1685,7 @@ impl<'tcx> Lvalue<'tcx> { } } - fn to_ptr(self) -> Pointer { + pub(super) fn to_ptr(self) -> Pointer { let (ptr, extra) = self.to_ptr_and_extra(); assert_eq!(extra, LvalueExtra::None); ptr @@ -1775,7 +1785,7 @@ pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // TODO(solson): Upstream these methods into rustc::ty::layout. -trait IntegerExt { +pub(super) trait IntegerExt { fn size(self) -> Size; } diff --git a/src/lib.rs b/src/lib.rs index e2158189144da..a54839b45375d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,36 +1,44 @@ #![feature( btree_range, + cell_extras, collections, collections_bound, - rustc_private, pub_restricted, - cell_extras, + rustc_private, )] // From rustc. -#[macro_use] extern crate rustc; +#[macro_use] +extern crate log; +extern crate log_settings; +#[macro_use] +extern crate rustc; extern crate rustc_borrowck; +extern crate rustc_const_math; extern crate rustc_data_structures; extern crate rustc_mir; -extern crate rustc_const_math; extern crate syntax; -#[macro_use] extern crate log; -extern crate log_settings; // From crates.io. extern crate byteorder; +mod cast; mod error; -mod interpreter; +mod eval_context; mod memory; mod primval; +mod step; +mod terminator; +mod value; +mod vtable; + pub use error::{ EvalError, EvalResult, }; -pub use interpreter::{ +pub use eval_context::{ EvalContext, Frame, Lvalue, diff --git a/src/interpreter/step.rs b/src/step.rs similarity index 98% rename from src/interpreter/step.rs rename to src/step.rs index ff4f3b3aa7003..fba8e48731c80 100644 --- a/src/interpreter/step.rs +++ b/src/step.rs @@ -2,21 +2,16 @@ //! //! The main entry point is the `step` method. -use super::{ - GlobalId, - EvalContext, - Lvalue, - StackPopCleanup, - Global, - MirRef, -}; -use error::{EvalResult, EvalError}; -use rustc::mir; -use rustc::ty::{subst, self}; +use std::cell::Ref; + use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; -use std::cell::Ref; +use rustc::mir; +use rustc::ty::{subst, self}; + +use error::{EvalResult, EvalError}; +use eval_context::{GlobalId, EvalContext, Lvalue, StackPopCleanup, Global, MirRef}; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/terminator/intrinsics.rs similarity index 99% rename from src/interpreter/terminator/intrinsics.rs rename to src/terminator/intrinsics.rs index 9c7b13adf76b6..75ca778d39cd0 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/terminator/intrinsics.rs @@ -5,9 +5,9 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; -use interpreter::value::Value; -use interpreter::{EvalContext, Lvalue, LvalueExtra}; +use eval_context::{EvalContext, Lvalue, LvalueExtra}; use primval::{self, PrimVal, PrimValKind}; +use value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( diff --git a/src/interpreter/terminator/mod.rs b/src/terminator/mod.rs similarity index 99% rename from src/interpreter/terminator/mod.rs rename to src/terminator/mod.rs index 837c701112527..d47166651c83b 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,10 +9,10 @@ use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; use error::{EvalError, EvalResult}; +use eval_context::{EvalContext, Lvalue, IntegerExt, StackPopCleanup, LvalueExtra, monomorphize_field_ty}; use memory::Pointer; use primval::PrimVal; -use super::{EvalContext, Lvalue, IntegerExt, StackPopCleanup, LvalueExtra, monomorphize_field_ty}; -use super::value::Value; +use value::Value; mod intrinsics; diff --git a/src/interpreter/value.rs b/src/value.rs similarity index 100% rename from src/interpreter/value.rs rename to src/value.rs diff --git a/src/interpreter/vtable.rs b/src/vtable.rs similarity index 100% rename from src/interpreter/vtable.rs rename to src/vtable.rs From fe19a014ffbafdb27b1fbb6bfec7ea41f553b3be Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 20:58:48 -0800 Subject: [PATCH 0683/1096] Move lvalue data structures out of eval_context. --- src/eval_context.rs | 62 +------------------------------- src/lib.rs | 9 +++-- src/lvalue.rs | 69 ++++++++++++++++++++++++++++++++++++ src/step.rs | 3 +- src/terminator/intrinsics.rs | 3 +- src/terminator/mod.rs | 3 +- 6 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 src/lvalue.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index ab6bda12677e0..2605732d526ea 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -13,6 +13,7 @@ use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; +use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; use memory::{Memory, Pointer}; use primval::{self, PrimVal, PrimValKind}; @@ -96,67 +97,6 @@ pub struct Frame<'tcx> { pub stmt: usize, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Lvalue<'tcx> { - /// An lvalue referring to a value allocated in the `Memory` system. - Ptr { - ptr: Pointer, - extra: LvalueExtra, - }, - - /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with - /// a Mir local index. - Local { - frame: usize, - local: mir::Local, - }, - - Global(GlobalId<'tcx>), - - // TODO(solson): None/Never? -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum LvalueExtra { - None, - Length(u64), - Vtable(Pointer), - DowncastVariant(usize), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -/// Uniquely identifies a specific constant or static -pub struct GlobalId<'tcx> { - /// the def id of the constant/static or in case of promoteds, the def id of the function they belong to - pub(super) def_id: DefId, - - /// In case of statics and constants this is `Substs::empty()`, so only promoteds and associated - /// constants actually have something useful here. We could special case statics and constants, - /// but that would only require more branching when working with constants, and not bring any - /// real benefits. - pub(super) substs: &'tcx Substs<'tcx>, - - /// The promoted index for this global, if it is a promoted. - pub(super) promoted: Option, -} - -#[derive(Copy, Clone, Debug)] -pub struct Global<'tcx> { - data: Option, - mutable: bool, - ty: Ty<'tcx>, -} - -impl<'tcx> Global<'tcx> { - pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { - Global { - data: None, - mutable: true, - ty: ty, - } - } -} - #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum StackPopCleanup { /// The stackframe existed to compute the initial value of a static/constant, make sure it diff --git a/src/lib.rs b/src/lib.rs index a54839b45375d..697c4ffa302c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ extern crate byteorder; mod cast; mod error; mod eval_context; +mod lvalue; mod memory; mod primval; mod step; @@ -32,7 +33,6 @@ mod terminator; mod value; mod vtable; - pub use error::{ EvalError, EvalResult, @@ -41,8 +41,6 @@ pub use error::{ pub use eval_context::{ EvalContext, Frame, - Lvalue, - LvalueExtra, ResourceLimits, StackPopCleanup, Value, @@ -50,6 +48,11 @@ pub use eval_context::{ run_mir_passes, }; +pub use lvalue::{ + Lvalue, + LvalueExtra, +}; + pub use memory::{ Memory, Pointer, diff --git a/src/lvalue.rs b/src/lvalue.rs new file mode 100644 index 0000000000000..ad3de8a6f4f08 --- /dev/null +++ b/src/lvalue.rs @@ -0,0 +1,69 @@ +use rustc::hir::def_id::DefId; +use rustc::mir; +use rustc::ty::Ty; +use rustc::ty::subst::Substs; + +use memory::Pointer; +use eval_context::Value; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Lvalue<'tcx> { + /// An lvalue referring to a value allocated in the `Memory` system. + Ptr { + ptr: Pointer, + extra: LvalueExtra, + }, + + /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with + /// a Mir local index. + Local { + frame: usize, + local: mir::Local, + }, + + /// An lvalue referring to a global + Global(GlobalId<'tcx>), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum LvalueExtra { + None, + Length(u64), + Vtable(Pointer), + DowncastVariant(usize), +} + +/// Uniquely identifies a specific constant or static. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct GlobalId<'tcx> { + /// For a constant or static, the `DefId` of the item itself. + /// For a promoted global, the `DefId` of the function they belong to. + pub(super) def_id: DefId, + + /// For statics and constants this is `Substs::empty()`, so only promoteds and associated + /// constants actually have something useful here. We could special case statics and constants, + /// but that would only require more branching when working with constants, and not bring any + /// real benefits. + pub(super) substs: &'tcx Substs<'tcx>, + + /// The index for promoted globals within their function's `Mir`. + pub(super) promoted: Option, +} + +#[derive(Copy, Clone, Debug)] +pub struct Global<'tcx> { + pub(super) data: Option, + pub(super) mutable: bool, + pub(super) ty: Ty<'tcx>, +} + +impl<'tcx> Global<'tcx> { + pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { + Global { + data: None, + mutable: true, + ty: ty, + } + } +} + diff --git a/src/step.rs b/src/step.rs index fba8e48731c80..ddd6c40e4c79b 100644 --- a/src/step.rs +++ b/src/step.rs @@ -11,7 +11,8 @@ use rustc::mir; use rustc::ty::{subst, self}; use error::{EvalResult, EvalError}; -use eval_context::{GlobalId, EvalContext, Lvalue, StackPopCleanup, Global, MirRef}; +use eval_context::{EvalContext, StackPopCleanup, MirRef}; +use lvalue::{Global, GlobalId, Lvalue}; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { diff --git a/src/terminator/intrinsics.rs b/src/terminator/intrinsics.rs index 75ca778d39cd0..9a2939001a121 100644 --- a/src/terminator/intrinsics.rs +++ b/src/terminator/intrinsics.rs @@ -5,7 +5,8 @@ use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, Lvalue, LvalueExtra}; +use eval_context::EvalContext; +use lvalue::{Lvalue, LvalueExtra}; use primval::{self, PrimVal, PrimValKind}; use value::Value; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d47166651c83b..0f2484fa9f624 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,8 @@ use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, Lvalue, IntegerExt, StackPopCleanup, LvalueExtra, monomorphize_field_ty}; +use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty}; +use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; use primval::PrimVal; use value::Value; From 5ce6514f236794ce13af807194e811099b7794cc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 22:00:46 -0800 Subject: [PATCH 0684/1096] Dump allocations within PrimVal pointers. --- src/eval_context.rs | 25 +++++++++++++++---------- src/memory.rs | 16 +++++++++++----- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 2605732d526ea..d1aca7947ffdc 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -338,7 +338,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for local in frame.locals.into_iter() { if let Some(Value::ByRef(ptr)) = local { trace!("deallocating local"); - self.memory.dump(ptr.alloc_id); + self.memory.dump_alloc(ptr.alloc_id); match self.memory.deallocate(ptr) { // Any frozen memory means that it belongs to a constant or something referenced // by a constant. We could alternatively check whether the alloc_id is frozen @@ -352,7 +352,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // deallocate all temporary allocations for ptr in frame.interpreter_temporaries { trace!("deallocating temporary allocation"); - self.memory.dump(ptr.alloc_id); + self.memory.dump_alloc(ptr.alloc_id); self.memory.deallocate(ptr)?; } Ok(()) @@ -1528,30 +1528,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } - _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", - src_ty, - dest_ty), + _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", src_ty, dest_ty), } Ok(()) } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { + let mut allocs = Vec::new(); + if let Lvalue::Local { frame, local } = lvalue { if let Some(val) = self.stack[frame].get_local(local) { match val { Value::ByRef(ptr) => { trace!("frame[{}] {:?}:", frame, local); - self.memory.dump(ptr.alloc_id); + allocs.push(ptr.alloc_id); } - Value::ByVal(a) => { - trace!("frame[{}] {:?}: {:?}", frame, local, a); + Value::ByVal(val) => { + trace!("frame[{}] {:?}: {:?}", frame, local, val); + if let Some(alloc_id) = val.relocation { allocs.push(alloc_id); } } - Value::ByValPair(a, b) => { - trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, a, b); + Value::ByValPair(val1, val2) => { + trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); + if let Some(alloc_id) = val1.relocation { allocs.push(alloc_id); } + if let Some(alloc_id) = val2.relocation { allocs.push(alloc_id); } } } } } + + self.memory.dump_allocs(allocs); } /// convenience function to ensure correct usage of globals and code-sharing with locals diff --git a/src/memory.rs b/src/memory.rs index 3d05300f0ab59..077616d073f5b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -17,7 +17,7 @@ use primval::{PrimVal, PrimValKind}; // Allocations and pointers //////////////////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub u64); impl fmt::Display for AllocId { @@ -342,12 +342,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - /// Print an allocation and all allocations it points to, recursively. - pub fn dump(&self, id: AllocId) { + /// For debugging, print an allocation and all allocations it points to, recursively. + pub fn dump_alloc(&self, id: AllocId) { + self.dump_allocs(vec![id]); + } + + /// For debugging, print a list of allocations and all allocations they point to, recursively. + pub fn dump_allocs(&self, mut allocs: Vec) { use std::fmt::Write; + allocs.sort(); + allocs.dedup(); + let mut allocs_to_print = VecDeque::from(allocs); let mut allocs_seen = HashSet::new(); - let mut allocs_to_print = VecDeque::new(); - allocs_to_print.push_back(id); while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id); From 829d97bde289ad3dbc6d3417c657e942f9b404fe Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 22:01:11 -0800 Subject: [PATCH 0685/1096] Move lvalue-related methods to lvalue mod. --- src/eval_context.rs | 253 ------------------------------------------ src/lvalue.rs | 260 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 258 insertions(+), 255 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index d1aca7947ffdc..85c829ede12b2 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -819,224 +819,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { - if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Local(index) = proj.base { - if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) { - if let mir::ProjectionElem::Field(ref field, _) = proj.elem { - let val = [a, b][field.index()]; - return Ok(Value::ByVal(val)); - } - } - } - } - let lvalue = self.eval_lvalue(lvalue)?; - self.read_lvalue(lvalue) - } - - pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { - match lvalue { - Lvalue::Ptr { ptr, extra } => { - assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) - } - Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) - } - Lvalue::Global(cid) => self.globals - .get(&cid) - .expect("global not cached") - .data - .ok_or(EvalError::ReadUndefBytes), - } - } - - pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { - use rustc::mir::Lvalue::*; - let lvalue = match *mir_lvalue { - Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - - Local(local) => { - Lvalue::Local { - frame: self.stack.len() - 1, - local: local, - } - } - - Static(def_id) => { - let substs = self.tcx.intern_substs(&[]); - let cid = GlobalId { - def_id: def_id, - substs: substs, - promoted: None, - }; - Lvalue::Global(cid) - } - - Projection(ref proj) => return self.eval_lvalue_projection(proj), - }; - - if log_enabled!(::log::LogLevel::Trace) { - self.dump_local(lvalue); - } - - Ok(lvalue) - } - - fn eval_lvalue_projection( - &mut self, - proj: &mir::LvalueProjection<'tcx>, - ) -> EvalResult<'tcx, Lvalue<'tcx>> { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty)?; - - use rustc::mir::ProjectionElem::*; - let (ptr, extra) = match proj.elem { - Field(field, field_ty) => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - let field_ty = self.monomorphize(field_ty, self.substs()); - let field = field.index(); - - use rustc::ty::layout::Layout::*; - let offset = match *base_layout { - Univariant { ref variant, .. } => variant.offsets[field], - - General { ref variants, .. } => { - if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { - // +1 for the discriminant, which is field 0 - variants[variant_idx].offsets[field + 1] - } else { - bug!("field access on enum had no variant index"); - } - } - - RawNullablePointer { .. } => { - assert_eq!(field.index(), 0); - return Ok(base); - } - - StructWrappedNullablePointer { ref nonnull, .. } => { - nonnull.offsets[field] - } - - _ => bug!("field access on non-product type: {:?}", base_layout), - }; - - let ptr = base_ptr.offset(offset.bytes()); - let extra = if self.type_is_sized(field_ty) { - LvalueExtra::None - } else { - match base_extra { - LvalueExtra::None => bug!("expected fat pointer"), - LvalueExtra::DowncastVariant(..) => - bug!("Rust doesn't support unsized fields in enum variants"), - LvalueExtra::Vtable(_) | - LvalueExtra::Length(_) => {}, - } - base_extra - }; - - (ptr, extra) - } - - Downcast(_, variant) => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - use rustc::ty::layout::Layout::*; - let extra = match *base_layout { - General { .. } => LvalueExtra::DowncastVariant(variant), - RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra, - _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), - }; - (base_ptr, extra) - } - - Deref => { - let val = self.eval_and_read_lvalue(&proj.base)?; - - let pointee_type = match base_ty.sty { - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyBox(ty) => ty, - _ => bug!("can only deref pointer types"), - }; - - trace!("deref to {} on {:?}", pointee_type, val); - - match self.tcx.struct_tail(pointee_type).sty { - ty::TyDynamic(..) => { - let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; - (ptr, LvalueExtra::Vtable(vtable)) - }, - ty::TyStr | ty::TySlice(_) => { - let (ptr, len) = val.expect_slice(&self.memory)?; - (ptr, LvalueExtra::Length(len)) - }, - _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), - } - } - - Index(ref operand) => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); - - let (elem_ty, len) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); - let n_ptr = self.eval_operand(operand)?; - let usize = self.tcx.types.usize; - let n = self.value_to_primval(n_ptr, usize)?.to_u64(); - assert!(n < len); - let ptr = base_ptr.offset(n * elem_size); - (ptr, LvalueExtra::None) - } - - ConstantIndex { offset, min_length, from_end } => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); - - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized"); - assert!(n >= min_length as u64); - - let index = if from_end { - n - u64::from(offset) - } else { - u64::from(offset) - }; - - let ptr = base_ptr.offset(index * elem_size); - (ptr, LvalueExtra::None) - } - - Subslice { from, to } => { - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); - - let (elem_ty, n) = base.elem_ty_and_len(base_ty); - let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); - assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size); - let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); - (ptr, extra) - } - }; - - Ok(Lvalue::Ptr { ptr: ptr, extra: extra }) - } - - pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) - } - pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } @@ -1617,41 +1399,6 @@ impl<'tcx> Frame<'tcx> { } } -impl<'tcx> Lvalue<'tcx> { - pub fn from_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } - } - - pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { - match self { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), - - } - } - - pub(super) fn to_ptr(self) -> Pointer { - let (ptr, extra) = self.to_ptr_and_extra(); - assert_eq!(extra, LvalueExtra::None); - ptr - } - - fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { - match ty.sty { - ty::TyArray(elem, n) => (elem, n as u64), - - ty::TySlice(elem) => { - match self { - Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len), - _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self), - } - } - - _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), - } - } -} - pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, diff --git a/src/lvalue.rs b/src/lvalue.rs index ad3de8a6f4f08..4f3c47800fe4f 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,10 +1,12 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc::ty::subst::Substs; +use rustc_data_structures::indexed_vec::Idx; +use error::{EvalError, EvalResult}; use memory::Pointer; -use eval_context::Value; +use eval_context::{EvalContext, Value}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Lvalue<'tcx> { @@ -57,6 +59,41 @@ pub struct Global<'tcx> { pub(super) ty: Ty<'tcx>, } +impl<'tcx> Lvalue<'tcx> { + pub fn from_ptr(ptr: Pointer) -> Self { + Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + } + + pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { + match self { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), + + } + } + + pub(super) fn to_ptr(self) -> Pointer { + let (ptr, extra) = self.to_ptr_and_extra(); + assert_eq!(extra, LvalueExtra::None); + ptr + } + + pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { + match ty.sty { + ty::TyArray(elem, n) => (elem, n as u64), + + ty::TySlice(elem) => { + match self { + Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len), + _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self), + } + } + + _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty), + } + } +} + impl<'tcx> Global<'tcx> { pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { Global { @@ -67,3 +104,222 @@ impl<'tcx> Global<'tcx> { } } +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + if let mir::Lvalue::Projection(ref proj) = *lvalue { + if let mir::Lvalue::Local(index) = proj.base { + if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) { + if let mir::ProjectionElem::Field(ref field, _) = proj.elem { + let val = [a, b][field.index()]; + return Ok(Value::ByVal(val)); + } + } + } + } + let lvalue = self.eval_lvalue(lvalue)?; + self.read_lvalue(lvalue) + } + + pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + match lvalue { + Lvalue::Ptr { ptr, extra } => { + assert_eq!(extra, LvalueExtra::None); + Ok(Value::ByRef(ptr)) + } + Lvalue::Local { frame, local } => { + self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) + } + Lvalue::Global(cid) => self.globals + .get(&cid) + .expect("global not cached") + .data + .ok_or(EvalError::ReadUndefBytes), + } + } + + pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { + use rustc::mir::Lvalue::*; + let lvalue = match *mir_lvalue { + Local(mir::RETURN_POINTER) => self.frame().return_lvalue, + + Local(local) => { + Lvalue::Local { + frame: self.stack.len() - 1, + local: local, + } + } + + Static(def_id) => { + let substs = self.tcx.intern_substs(&[]); + let cid = GlobalId { + def_id: def_id, + substs: substs, + promoted: None, + }; + Lvalue::Global(cid) + } + + Projection(ref proj) => return self.eval_lvalue_projection(proj), + }; + + if log_enabled!(::log::LogLevel::Trace) { + self.dump_local(lvalue); + } + + Ok(lvalue) + } + + fn eval_lvalue_projection( + &mut self, + proj: &mir::LvalueProjection<'tcx>, + ) -> EvalResult<'tcx, Lvalue<'tcx>> { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty)?; + + use rustc::mir::ProjectionElem::*; + let (ptr, extra) = match proj.elem { + Field(field, field_ty) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + + let field_ty = self.monomorphize(field_ty, self.substs()); + let field = field.index(); + + use rustc::ty::layout::Layout::*; + let offset = match *base_layout { + Univariant { ref variant, .. } => variant.offsets[field], + + General { ref variants, .. } => { + if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { + // +1 for the discriminant, which is field 0 + variants[variant_idx].offsets[field + 1] + } else { + bug!("field access on enum had no variant index"); + } + } + + RawNullablePointer { .. } => { + assert_eq!(field.index(), 0); + return Ok(base); + } + + StructWrappedNullablePointer { ref nonnull, .. } => { + nonnull.offsets[field] + } + + _ => bug!("field access on non-product type: {:?}", base_layout), + }; + + let ptr = base_ptr.offset(offset.bytes()); + let extra = if self.type_is_sized(field_ty) { + LvalueExtra::None + } else { + match base_extra { + LvalueExtra::None => bug!("expected fat pointer"), + LvalueExtra::DowncastVariant(..) => + bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::Vtable(_) | + LvalueExtra::Length(_) => {}, + } + base_extra + }; + + (ptr, extra) + } + + Downcast(_, variant) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + + use rustc::ty::layout::Layout::*; + let extra = match *base_layout { + General { .. } => LvalueExtra::DowncastVariant(variant), + RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra, + _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), + }; + (base_ptr, extra) + } + + Deref => { + let val = self.eval_and_read_lvalue(&proj.base)?; + + let pointee_type = match base_ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => ty, + _ => bug!("can only deref pointer types"), + }; + + trace!("deref to {} on {:?}", pointee_type, val); + + match self.tcx.struct_tail(pointee_type).sty { + ty::TyDynamic(..) => { + let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; + (ptr, LvalueExtra::Vtable(vtable)) + }, + ty::TyStr | ty::TySlice(_) => { + let (ptr, len) = val.expect_slice(&self.memory)?; + (ptr, LvalueExtra::Length(len)) + }, + _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), + } + } + + Index(ref operand) => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + + let (elem_ty, len) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); + let n_ptr = self.eval_operand(operand)?; + let usize = self.tcx.types.usize; + let n = self.value_to_primval(n_ptr, usize)?.to_u64(); + assert!(n < len); + let ptr = base_ptr.offset(n * elem_size); + (ptr, LvalueExtra::None) + } + + ConstantIndex { offset, min_length, from_end } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized"); + assert!(n >= min_length as u64); + + let index = if from_end { + n - u64::from(offset) + } else { + u64::from(offset) + }; + + let ptr = base_ptr.offset(index * elem_size); + (ptr, LvalueExtra::None) + } + + Subslice { from, to } => { + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, _) = base.to_ptr_and_extra(); + + let (elem_ty, n) = base.elem_ty_and_len(base_ty); + let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); + assert!(u64::from(from) <= n - u64::from(to)); + let ptr = base_ptr.offset(u64::from(from) * elem_size); + let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); + (ptr, extra) + } + }; + + Ok(Lvalue::Ptr { ptr: ptr, extra: extra }) + } + + pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { + self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) + } +} From a64d30b2c19d3e48978bcb2eb39d53115da331f5 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 22:56:28 -0800 Subject: [PATCH 0686/1096] Replace some stray `try!`s with `?`. --- src/eval_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 85c829ede12b2..f2070b961f099 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -520,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { if nndiscr == variant as u64 { let offsets = nonnull.offsets.iter().map(|s| s.bytes()); - try!(self.assign_fields(dest, offsets, operands)); + self.assign_fields(dest, offsets, operands)?; } else { for operand in operands { let operand_ty = self.operand_ty(operand); @@ -533,7 +533,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = dest.offset(offset.bytes()); let dest_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); - try!(self.memory.write_int(dest, 0, dest_size)); + self.memory.write_int(dest, 0, dest_size)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); From bc5bd719229baaffb19bb64ad11f9e40996b0030 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 7 Dec 2016 23:25:47 -0800 Subject: [PATCH 0687/1096] Add support for untagged unions. --- src/eval_context.rs | 20 +++++++++++++++++++- src/lvalue.rs | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index f2070b961f099..19f756c34124b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -557,7 +557,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.assign_fields(dest, offsets, operands)?; } - _ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))), + UntaggedUnion { .. } => { + assert_eq!(operands.len(), 1); + let operand = &operands[0]; + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); + + // FIXME(solson) + let dest = self.force_allocation(dest)?; + + self.write_value(value, dest, value_ty)?; + } + + _ => { + return Err(EvalError::Unimplemented(format!( + "can't handle destination layout {:?} when assigning {:?}", + dest_layout, + kind + ))); + } } } diff --git a/src/lvalue.rs b/src/lvalue.rs index 4f3c47800fe4f..e88b65bbe47cb 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -209,6 +209,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { nonnull.offsets[field] } + UntaggedUnion { .. } => return Ok(base), + _ => bug!("field access on non-product type: {:?}", base_layout), }; From 1af63171f8264b552adec3dabe069731e750e05d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 10 Dec 2016 16:23:07 -0800 Subject: [PATCH 0688/1096] Split primval into operator and value. --- src/cast.rs | 14 +-- src/eval_context.rs | 7 +- src/lib.rs | 6 +- src/memory.rs | 4 +- src/{primval.rs => operator.rs} | 184 +++----------------------------- src/terminator/intrinsics.rs | 14 +-- src/terminator/mod.rs | 2 +- src/value.rs | 171 ++++++++++++++++++++++++++++- 8 files changed, 202 insertions(+), 200 deletions(-) rename src/{primval.rs => operator.rs} (65%) diff --git a/src/cast.rs b/src/cast.rs index 7a0285da440ed..a49b3b1248079 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -1,14 +1,10 @@ +use rustc::ty::{self, Ty}; +use syntax::ast::{FloatTy, IntTy, UintTy}; -use super::{ - EvalContext, -}; use error::{EvalResult, EvalError}; -use rustc::ty; -use primval::PrimVal; +use eval_context::EvalContext; use memory::Pointer; - -use rustc::ty::Ty; -use syntax::ast::{FloatTy, IntTy, UintTy}; +use value::PrimVal; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval( @@ -19,7 +15,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, PrimVal> { let kind = self.ty_to_primval_kind(src_ty)?; - use primval::PrimValKind::*; + use value::PrimValKind::*; match kind { F32 => self.cast_float(val.to_f32() as f64, dest_ty), F64 => self.cast_float(val.to_f64(), dest_ty), diff --git a/src/eval_context.rs b/src/eval_context.rs index 19f756c34124b..b0c01aed9e134 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -15,7 +15,8 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; use memory::{Memory, Pointer}; -use primval::{self, PrimVal, PrimValKind}; +use operator; +use value::{PrimVal, PrimValKind}; // FIXME(solson): Remove this. pub use value::Value; @@ -370,7 +371,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let right_kind = self.ty_to_primval_kind(right_ty)?; let left_val = self.eval_operand_to_primval(left)?; let right_val = self.eval_operand_to_primval(right)?; - primval::binary_op(op, left_val, left_kind, right_val, right_kind) + operator::binary_op(op, left_val, left_kind, right_val, right_kind) } /// Applies the binary operation `op` to the two operands and writes a tuple of the result @@ -453,7 +454,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; let kind = self.ty_to_primval_kind(dest_ty)?; - self.write_primval(dest, primval::unary_op(un_op, val, kind)?, dest_ty)?; + self.write_primval(dest, operator::unary_op(un_op, val, kind)?, dest_ty)?; } Aggregate(ref kind, ref operands) => { diff --git a/src/lib.rs b/src/lib.rs index 697c4ffa302c0..1400d637767dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ mod error; mod eval_context; mod lvalue; mod memory; -mod primval; +mod operator; mod step; mod terminator; mod value; @@ -54,12 +54,12 @@ pub use lvalue::{ }; pub use memory::{ + AllocId, Memory, Pointer, - AllocId, }; -pub use primval::{ +pub use value::{ PrimVal, PrimValKind, }; diff --git a/src/memory.rs b/src/memory.rs index 077616d073f5b..671174fd75690 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -11,7 +11,7 @@ use rustc::ty::layout::{self, TargetDataLayout}; use syntax::abi::Abi; use error::{EvalError, EvalResult}; -use primval::{PrimVal, PrimValKind}; +use value::{PrimVal, PrimValKind}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -575,7 +575,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return self.write_ptr(dest, Pointer::new(alloc_id, val.bits)); } - use primval::PrimValKind::*; + use value::PrimValKind::*; let (size, bits) = match kind { I8 | U8 | Bool => (1, val.bits as u8 as u64), I16 | U16 => (2, val.bits as u16 as u64), diff --git a/src/primval.rs b/src/operator.rs similarity index 65% rename from src/primval.rs rename to src/operator.rs index 83d9c18f34f67..5a6182d0be768 100644 --- a/src/primval.rs +++ b/src/operator.rs @@ -1,176 +1,16 @@ -#![allow(unknown_lints)] -#![allow(float_cmp)] - -use std::mem::transmute; - use rustc::mir; use error::{EvalError, EvalResult}; -use memory::{AllocId, Pointer}; - -fn bits_to_f32(bits: u64) -> f32 { - unsafe { transmute::(bits as u32) } -} - -fn bits_to_f64(bits: u64) -> f64 { - unsafe { transmute::(bits) } -} - -fn f32_to_bits(f: f32) -> u64 { - unsafe { transmute::(f) as u64 } -} - -fn f64_to_bits(f: f64) -> u64 { - unsafe { transmute::(f) } -} - -fn bits_to_bool(n: u64) -> bool { - // FIXME(solson): Can we reach here due to user error? - debug_assert!(n == 0 || n == 1, "bits interpreted as bool were {}", n); - n & 1 == 1 -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct PrimVal { - pub bits: u64, - - /// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An - /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only - /// large enough to contain one, hence the `Option`. - pub relocation: Option, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PrimValKind { - I8, I16, I32, I64, - U8, U16, U32, U64, - F32, F64, - Bool, - Char, - Ptr, - FnPtr, -} - -impl PrimValKind { - pub fn is_int(self) -> bool { - use self::PrimValKind::*; - match self { - I8 | I16 | I32 | I64 | U8 | U16 | U32 | U64 => true, - _ => false, - } - } - - pub fn from_uint_size(size: u64) -> Self { - match size { - 1 => PrimValKind::U8, - 2 => PrimValKind::U16, - 4 => PrimValKind::U32, - 8 => PrimValKind::U64, - _ => bug!("can't make uint with size {}", size), - } - } - - pub fn from_int_size(size: u64) -> Self { - match size { - 1 => PrimValKind::I8, - 2 => PrimValKind::I16, - 4 => PrimValKind::I32, - 8 => PrimValKind::I64, - _ => bug!("can't make int with size {}", size), - } - } -} - -impl PrimVal { - pub fn new(bits: u64) -> Self { - PrimVal { bits: bits, relocation: None } - } - - pub fn new_with_relocation(bits: u64, alloc_id: AllocId) -> Self { - PrimVal { bits: bits, relocation: Some(alloc_id) } - } - - pub fn from_ptr(ptr: Pointer) -> Self { - PrimVal::new_with_relocation(ptr.offset as u64, ptr.alloc_id) - } - - pub fn from_bool(b: bool) -> Self { - PrimVal::new(b as u64) - } - - pub fn from_char(c: char) -> Self { - PrimVal::new(c as u64) - } - - pub fn from_f32(f: f32) -> Self { - PrimVal::new(f32_to_bits(f)) - } - - pub fn from_f64(f: f64) -> Self { - PrimVal::new(f64_to_bits(f)) - } - - pub fn from_uint(n: u64) -> Self { - PrimVal::new(n) - } - - pub fn from_int(n: i64) -> Self { - PrimVal::new(n as u64) - } - - pub fn to_f32(self) -> f32 { - assert!(self.relocation.is_none()); - bits_to_f32(self.bits) - } - - pub fn to_f64(self) -> f64 { - assert!(self.relocation.is_none()); - bits_to_f64(self.bits) - } - - pub fn to_ptr(self) -> Pointer { - self.relocation.map(|alloc_id| { - Pointer::new(alloc_id, self.bits) - }).unwrap_or_else(|| Pointer::from_int(self.bits)) - } - - pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { - self.to_ptr().to_int() - } - - pub fn to_u64(self) -> u64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as u64; - } - self.bits - } - - pub fn to_i64(self) -> i64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as i64; - } - self.bits as i64 - } - - pub fn try_as_ptr(self) -> Option { - self.relocation.map(|alloc_id| { - Pointer::new(alloc_id, self.bits) - }) - } - - pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { - match self.bits { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(EvalError::InvalidBool), - } - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// MIR operator evaluation -//////////////////////////////////////////////////////////////////////////////// +use memory::Pointer; +use value::{ + PrimVal, + PrimValKind, + bits_to_f32, + bits_to_f64, + f32_to_bits, + f64_to_bits, + bits_to_bool, +}; macro_rules! overflow { ($op:ident, $l:expr, $r:expr) => ({ @@ -246,7 +86,7 @@ pub fn binary_op<'tcx>( right_kind: PrimValKind, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use self::PrimValKind::*; + use value::PrimValKind::*; // If the pointers are into the same allocation, fall through to the more general match // later, which will do comparisons on the `bits` fields, which are the pointer offsets @@ -360,7 +200,7 @@ pub fn unary_op<'tcx>( val_kind: PrimValKind, ) -> EvalResult<'tcx, PrimVal> { use rustc::mir::UnOp::*; - use self::PrimValKind::*; + use value::PrimValKind::*; let bits = match (un_op, val_kind) { (Not, Bool) => !bits_to_bool(val.bits) as u64, diff --git a/src/terminator/intrinsics.rs b/src/terminator/intrinsics.rs index 9a2939001a121..9be094d9bd977 100644 --- a/src/terminator/intrinsics.rs +++ b/src/terminator/intrinsics.rs @@ -7,8 +7,8 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::{Lvalue, LvalueExtra}; -use primval::{self, PrimVal, PrimValKind}; -use value::Value; +use operator; +use value::{PrimVal, PrimValKind, Value}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -101,7 +101,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), }; let kind = self.ty_to_primval_kind(ty)?; - let (val, _) = primval::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?; + let (val, _) = operator::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?; let dest = self.force_allocation(dest)?.to_ptr(); self.write_pair_to_ptr(old, val, dest, dest_ty)?; self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; @@ -120,7 +120,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, old, ty)?; let kind = self.ty_to_primval_kind(ty)?; // FIXME: what do atomics do on overflow? - let (val, _) = primval::binary_op(mir::BinOp::Add, old, kind, change, kind)?; + let (val, _) = operator::binary_op(mir::BinOp::Add, old, kind, change, kind)?; self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; }, @@ -137,7 +137,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, old, ty)?; let kind = self.ty_to_primval_kind(ty)?; // FIXME: what do atomics do on overflow? - let (val, _) = primval::binary_op(mir::BinOp::Sub, old, kind, change, kind)?; + let (val, _) = operator::binary_op(mir::BinOp::Sub, old, kind, change, kind)?; self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; } @@ -217,7 +217,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[0], ty)?; - let result = primval::binary_op(mir::BinOp::Add, a, kind, b, kind)?; + let result = operator::binary_op(mir::BinOp::Add, a, kind, b, kind)?; self.write_primval(dest, result.0, dest_ty)?; } @@ -504,7 +504,7 @@ macro_rules! integer_intrinsic { ($name:expr, $val:expr, $kind:expr, $method:ident) => ({ let val = $val; - use primval::PrimValKind::*; + use value::PrimValKind::*; let bits = match $kind { I8 => (val.bits as i8).$method() as u64, U8 => (val.bits as u8).$method() as u64, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 0f2484fa9f624..31234807d729d 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -12,7 +12,7 @@ use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty}; use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; -use primval::PrimVal; +use value::PrimVal; use value::Value; mod intrinsics; diff --git a/src/value.rs b/src/value.rs index f31f1ca24bc3c..4642018c12465 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,32 @@ -use error::EvalResult; -use memory::{Memory, Pointer}; -use primval::PrimVal; +#![allow(unknown_lints)] +#![allow(float_cmp)] + +use std::mem::transmute; + +use error::{EvalError, EvalResult}; +use memory::{AllocId, Memory, Pointer}; + +pub(super) fn bits_to_f32(bits: u64) -> f32 { + unsafe { transmute::(bits as u32) } +} + +pub(super) fn bits_to_f64(bits: u64) -> f64 { + unsafe { transmute::(bits) } +} + +pub(super) fn f32_to_bits(f: f32) -> u64 { + unsafe { transmute::(f) as u64 } +} + +pub(super) fn f64_to_bits(f: f64) -> u64 { + unsafe { transmute::(f) } +} + +pub(super) fn bits_to_bool(n: u64) -> bool { + // FIXME(solson): Can we reach here due to user error? + debug_assert!(n == 0 || n == 1, "bits interpreted as bool were {}", n); + n & 1 == 1 +} /// A `Value` represents a single self-contained Rust value. /// @@ -17,6 +43,29 @@ pub enum Value { ByValPair(PrimVal, PrimVal), } +/// A `PrimVal` represents an immediate, primitive value existing outside of an allocation. It is +/// considered to be like a +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct PrimVal { + pub bits: u64, + + /// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An + /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only + /// large enough to contain one, hence the `Option`. + pub relocation: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PrimValKind { + I8, I16, I32, I64, + U8, U16, U32, U64, + F32, F64, + Bool, + Char, + Ptr, + FnPtr, +} + impl<'a, 'tcx: 'a> Value { pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; @@ -59,3 +108,119 @@ impl<'a, 'tcx: 'a> Value { } } } + +impl PrimVal { + pub fn new(bits: u64) -> Self { + PrimVal { bits: bits, relocation: None } + } + + pub fn new_with_relocation(bits: u64, alloc_id: AllocId) -> Self { + PrimVal { bits: bits, relocation: Some(alloc_id) } + } + + pub fn from_ptr(ptr: Pointer) -> Self { + PrimVal::new_with_relocation(ptr.offset as u64, ptr.alloc_id) + } + + pub fn from_bool(b: bool) -> Self { + PrimVal::new(b as u64) + } + + pub fn from_char(c: char) -> Self { + PrimVal::new(c as u64) + } + + pub fn from_f32(f: f32) -> Self { + PrimVal::new(f32_to_bits(f)) + } + + pub fn from_f64(f: f64) -> Self { + PrimVal::new(f64_to_bits(f)) + } + + pub fn from_uint(n: u64) -> Self { + PrimVal::new(n) + } + + pub fn from_int(n: i64) -> Self { + PrimVal::new(n as u64) + } + + pub fn to_f32(self) -> f32 { + assert!(self.relocation.is_none()); + bits_to_f32(self.bits) + } + + pub fn to_f64(self) -> f64 { + assert!(self.relocation.is_none()); + bits_to_f64(self.bits) + } + + pub fn to_ptr(self) -> Pointer { + self.relocation.map(|alloc_id| { + Pointer::new(alloc_id, self.bits) + }).unwrap_or_else(|| Pointer::from_int(self.bits)) + } + + pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { + self.to_ptr().to_int() + } + + pub fn to_u64(self) -> u64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as u64; + } + self.bits + } + + pub fn to_i64(self) -> i64 { + if let Some(ptr) = self.try_as_ptr() { + return ptr.to_int().expect("non abstract ptr") as i64; + } + self.bits as i64 + } + + pub fn try_as_ptr(self) -> Option { + self.relocation.map(|alloc_id| { + Pointer::new(alloc_id, self.bits) + }) + } + + pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { + match self.bits { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(EvalError::InvalidBool), + } + } +} + +impl PrimValKind { + pub fn is_int(self) -> bool { + use self::PrimValKind::*; + match self { + I8 | I16 | I32 | I64 | U8 | U16 | U32 | U64 => true, + _ => false, + } + } + + pub fn from_uint_size(size: u64) -> Self { + match size { + 1 => PrimValKind::U8, + 2 => PrimValKind::U16, + 4 => PrimValKind::U32, + 8 => PrimValKind::U64, + _ => bug!("can't make uint with size {}", size), + } + } + + pub fn from_int_size(size: u64) -> Self { + match size { + 1 => PrimValKind::I8, + 2 => PrimValKind::I16, + 4 => PrimValKind::I32, + 8 => PrimValKind::I64, + _ => bug!("can't make int with size {}", size), + } + } +} From 636b476edad97b18f2a99a46912b27297994ddb2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 10 Dec 2016 16:27:45 -0800 Subject: [PATCH 0689/1096] Rename intrinsics to intrinsic for consistency. --- src/terminator/{intrinsics.rs => intrinsic.rs} | 0 src/terminator/mod.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/terminator/{intrinsics.rs => intrinsic.rs} (100%) diff --git a/src/terminator/intrinsics.rs b/src/terminator/intrinsic.rs similarity index 100% rename from src/terminator/intrinsics.rs rename to src/terminator/intrinsic.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 31234807d729d..fdc7c1db6faa7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -15,7 +15,7 @@ use memory::Pointer; use value::PrimVal; use value::Value; -mod intrinsics; +mod intrinsic; impl<'a, 'tcx> EvalContext<'a, 'tcx> { From e0013b2ae48cb8918f4837b9ed6f7101afeecf75 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 10 Dec 2016 16:58:13 -0800 Subject: [PATCH 0690/1096] Clean up vtable imports. --- src/vtable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vtable.rs b/src/vtable.rs index 89db8e111e40c..a2cd2d2729d16 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -3,10 +3,10 @@ use rustc::traits::{self, Reveal, SelectionContext}; use rustc::ty::subst::Substs; use rustc::ty; -use super::EvalContext; use error::EvalResult; +use eval_context::EvalContext; use memory::Pointer; -use super::terminator::{get_impl_method, ImplMethod}; +use terminator::{get_impl_method, ImplMethod}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for From ee0dc452aa90afe8a15a175e1add1d7b388868aa Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 10 Dec 2016 17:03:12 -0800 Subject: [PATCH 0691/1096] Move binop functions to operator module. --- src/eval_context.rs | 45 --------------------------------------- src/operator.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b0c01aed9e134..713c01602c786 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -359,51 +359,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn binop_with_overflow( - &mut self, - op: mir::BinOp, - left: &mir::Operand<'tcx>, - right: &mir::Operand<'tcx>, - ) -> EvalResult<'tcx, (PrimVal, bool)> { - let left_ty = self.operand_ty(left); - let right_ty = self.operand_ty(right); - let left_kind = self.ty_to_primval_kind(left_ty)?; - let right_kind = self.ty_to_primval_kind(right_ty)?; - let left_val = self.eval_operand_to_primval(left)?; - let right_val = self.eval_operand_to_primval(right)?; - operator::binary_op(op, left_val, left_kind, right_val, right_kind) - } - - /// Applies the binary operation `op` to the two operands and writes a tuple of the result - /// and a boolean signifying the potential overflow to the destination. - pub(super) fn intrinsic_with_overflow( - &mut self, - op: mir::BinOp, - left: &mir::Operand<'tcx>, - right: &mir::Operand<'tcx>, - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { - let (val, overflowed) = self.binop_with_overflow(op, left, right)?; - let val = Value::ByValPair(val, PrimVal::from_bool(overflowed)); - self.write_value(val, dest, dest_ty) - } - - /// Applies the binary operation `op` to the arguments and writes the result to the - /// destination. Returns `true` if the operation overflowed. - pub(super) fn intrinsic_overflowing( - &mut self, - op: mir::BinOp, - left: &mir::Operand<'tcx>, - right: &mir::Operand<'tcx>, - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, bool> { - let (val, overflowed) = self.binop_with_overflow(op, left, right)?; - self.write_primval(dest, val, dest_ty)?; - Ok(overflowed) - } - fn assign_fields>( &mut self, dest: Lvalue<'tcx>, diff --git a/src/operator.rs b/src/operator.rs index 5a6182d0be768..6e656d69f6558 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -1,10 +1,14 @@ use rustc::mir; +use rustc::ty::Ty; use error::{EvalError, EvalResult}; +use eval_context::EvalContext; +use lvalue::Lvalue; use memory::Pointer; use value::{ PrimVal, PrimValKind, + Value, bits_to_f32, bits_to_f64, f32_to_bits, @@ -12,6 +16,53 @@ use value::{ bits_to_bool, }; +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + fn binop_with_overflow( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + ) -> EvalResult<'tcx, (PrimVal, bool)> { + let left_ty = self.operand_ty(left); + let right_ty = self.operand_ty(right); + let left_kind = self.ty_to_primval_kind(left_ty)?; + let right_kind = self.ty_to_primval_kind(right_ty)?; + let left_val = self.eval_operand_to_primval(left)?; + let right_val = self.eval_operand_to_primval(right)?; + binary_op(op, left_val, left_kind, right_val, right_kind) + } + + /// Applies the binary operation `op` to the two operands and writes a tuple of the result + /// and a boolean signifying the potential overflow to the destination. + pub(super) fn intrinsic_with_overflow( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, ()> { + let (val, overflowed) = self.binop_with_overflow(op, left, right)?; + let val = Value::ByValPair(val, PrimVal::from_bool(overflowed)); + self.write_value(val, dest, dest_ty) + } + + /// Applies the binary operation `op` to the arguments and writes the result to the + /// destination. Returns `true` if the operation overflowed. + pub(super) fn intrinsic_overflowing( + &mut self, + op: mir::BinOp, + left: &mir::Operand<'tcx>, + right: &mir::Operand<'tcx>, + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, bool> { + let (val, overflowed) = self.binop_with_overflow(op, left, right)?; + self.write_primval(dest, val, dest_ty)?; + Ok(overflowed) + } +} + macro_rules! overflow { ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); From 69fa3ebff6c7be2f40bafc776832179f74ae0d1b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 14 Dec 2016 17:06:23 +0100 Subject: [PATCH 0692/1096] rustup to rustc 1.15.0-dev (ace092f56 2016-12-13) (always_encode_mir) --- src/error.rs | 3 ++ src/memory.rs | 16 +++++++++++ src/terminator/intrinsic.rs | 1 + src/terminator/mod.rs | 55 ++++++++++++++++++++++++++++++------- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/error.rs b/src/error.rs index afc1855e8e755..9b532e0137592 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,7 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), + UnterminatedCString(Pointer), DanglingPointerDeref, InvalidMemoryAccess, InvalidFunctionPointer, @@ -119,6 +120,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to deallocate frozen memory", EvalError::Layout(_) => "rustc layout computation failed", + EvalError::UnterminatedCString(_) => + "attempted to get length of a null terminated string, but no null found before end of allocation", } } diff --git a/src/memory.rs b/src/memory.rs index 671174fd75690..580372b3da2df 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -530,6 +530,22 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } + pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { + let alloc = self.get(ptr.alloc_id)?; + assert_eq!(ptr.offset as usize as u64, ptr.offset); + let offset = ptr.offset as usize; + match alloc.bytes[offset..].iter().position(|&c| c == 0) { + Some(size) => { + if self.relocations(ptr, size as u64)?.count() != 0 { + return Err(EvalError::ReadPointerAsBytes); + } + self.check_defined(ptr, size as u64)?; + Ok(&alloc.bytes[offset..offset + size]) + }, + None => Err(EvalError::UnterminatedCString(ptr)), + } + } + pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { self.get_bytes(ptr, size, 1) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 9be094d9bd977..443696edb1228 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_load" | + "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index fdc7c1db6faa7..95649f4a8b379 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -90,7 +90,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - if abi != bare_fn_ty.abi || sig != bare_fn_ty.sig.skip_binder() { + let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); + let bare_sig = self.tcx.erase_regions(&bare_sig); + // transmuting function pointers in miri is fine as long as the number of + // arguments and the abi don't change. + // FIXME: also check the size of the arguments' type and the return type + // Didn't get it to work, since that triggers an assertion in rustc which + // checks whether the type has escaping regions + if abi != bare_fn_ty.abi || + sig.variadic != bare_sig.variadic || + sig.inputs().len() != bare_sig.inputs().len() { return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); } self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, @@ -189,7 +198,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { - let ty = fn_ty.sig.0.output; + let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; let (ret, target) = destination.unwrap(); self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; @@ -197,7 +206,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Abi::C => { - let ty = fn_ty.sig.0.output; + let ty = fn_ty.sig.0.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.goto_block(target); @@ -320,11 +329,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect(); let args = args_res?; - if link_name.starts_with("pthread_") { - warn!("ignoring C ABI call: {}", link_name); - return Ok(()); - } - let usize = self.tcx.types.usize; match &link_name[..] { @@ -371,6 +375,37 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::new(result as u64), dest_ty)?; } + "memchr" => { + let ptr = args[0].read_ptr(&self.memory)?; + let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64(); + if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { + let new_ptr = ptr.offset(idx as u64); + self.write_value(Value::ByVal(PrimVal::from_ptr(new_ptr)), dest, dest_ty)?; + } else { + self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + } + } + + "getenv" => { + { + let name = args[0].read_ptr(&self.memory)?; + let name = self.memory.read_c_str(name)?; + info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); + } + self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + } + + // unix panic code inside libstd will read the return value of this function + "pthread_rwlock_rdlock" => { + self.write_primval(dest, PrimVal::new(0), dest_ty)?; + } + + link_name if link_name.starts_with("pthread_") => { + warn!("ignoring C ABI call: {}", link_name); + return Ok(()); + }, + _ => { return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); } @@ -520,7 +555,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - *first_ty = sig.inputs[0]; + *first_ty = sig.inputs()[0]; Ok((def_id, substs, Vec::new())) } else { Err(EvalError::VtableForArgumentlessMethod) @@ -664,7 +699,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; - let real_ty = sig.inputs[0]; + let real_ty = sig.inputs()[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); } else { From 8b8c7430f127bab7871011ca183f93d433375b6c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 14 Dec 2016 17:02:45 +0100 Subject: [PATCH 0693/1096] re-use `mir-opt` compiletest instead of rolling our own --- src/bin/miri.rs | 1 - tests/compiletest.rs | 70 ++++++++------------------------------------ 2 files changed, 12 insertions(+), 59 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6c0161ef0754a..c5422e48b5e13 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -21,7 +21,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut control = CompileController::basic(); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); - control.after_analysis.stop = Compilation::Stop; control } } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 1ee86a07b22f5..f7bf16926ba7c 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,6 +27,15 @@ fn run_pass() { compiletest::run_tests(&config); } +fn miri_pass(path: &str, target: &str) { + let mut config = compiletest::default_config(); + config.mode = "mir-opt".parse().expect("Invalid mode"); + config.src_base = PathBuf::from(path); + config.target = target.to_owned(); + config.rustc_path = PathBuf::from("target/debug/miri"); + compiletest::run_tests(&config); +} + fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { let target = target.unwrap(); @@ -57,65 +66,10 @@ fn compile_test() { }; run_pass(); for_all_targets(&sysroot, |target| { - let files = std::fs::read_dir("tests/run-pass").unwrap(); - let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - Box::new(files.chain(std::fs::read_dir(path).unwrap())) - } else { - Box::new(files) - }; - let mut mir_not_found = 0; - let mut crate_not_found = 0; - let mut success = 0; - let mut failed = 0; - for file in files { - let file = file.unwrap(); - let path = file.path(); - - if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { - continue; - } - - let stderr = std::io::stderr(); - write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); - let mut cmd = std::process::Command::new("target/debug/miri"); - cmd.arg(path); - cmd.arg(format!("--target={}", target)); - let libs = Path::new(&sysroot).join("lib"); - let sysroot = libs.join("rustlib").join(&target).join("lib"); - let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); - cmd.env(compiletest::procsrv::dylib_env_var(), paths); - - match cmd.output() { - Ok(ref output) if output.status.success() => { - success += 1; - writeln!(stderr.lock(), "ok").unwrap() - }, - Ok(output) => { - let output_err = std::str::from_utf8(&output.stderr).unwrap(); - if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { - mir_not_found += 1; - let end = text.find('`').unwrap(); - writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); - } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { - crate_not_found += 1; - let end = text.find('`').unwrap(); - writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); - } else { - failed += 1; - writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); - writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); - writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); - } - } - Err(e) => { - writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); - panic!("failed to execute miri"); - }, - } + miri_pass("tests/run-pass", &target); + if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + miri_pass(&path, &target); } - let stderr = std::io::stderr(); - writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); - assert_eq!(failed, 0, "some tests failed"); }); compile_fail(&sysroot); } From 9ec97bac716a1484fef1c0f995154da193f305eb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 14 Dec 2016 17:02:59 +0100 Subject: [PATCH 0694/1096] enable auxiliary builds --- src/bin/miri.rs | 19 +++++++++++-------- tests/run-pass/aux_test.rs | 7 +++++++ tests/run-pass/auxiliary/dep.rs | 1 + 3 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 tests/run-pass/aux_test.rs create mode 100644 tests/run-pass/auxiliary/dep.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index c5422e48b5e13..d46572606a7fd 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -34,14 +34,16 @@ fn after_analysis(state: &mut CompileState) { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - let (entry_node_id, _) = state.session.entry_fn.borrow() - .expect("no main or start function found"); - let entry_def_id = tcx.map.local_def_id(entry_node_id); - let limits = resource_limits_from_attributes(state); - miri::run_mir_passes(tcx); - miri::eval_main(tcx, entry_def_id, limits); - - state.session.abort_if_errors(); + if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { + let entry_def_id = tcx.map.local_def_id(entry_node_id); + let limits = resource_limits_from_attributes(state); + miri::run_mir_passes(tcx); + miri::eval_main(tcx, entry_def_id, limits); + + state.session.abort_if_errors(); + } else { + println!("no main function found, assuming auxiliary build"); + } } fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits { @@ -133,6 +135,7 @@ fn main() { args.push(sysroot_flag); args.push(find_sysroot()); } + args.push("-Zalways-encode-mir".to_owned()); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); } diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs new file mode 100644 index 0000000000000..aa471f6cf8fdf --- /dev/null +++ b/tests/run-pass/aux_test.rs @@ -0,0 +1,7 @@ +// aux-build:dep.rs + +extern crate dep; + +fn main() { + dep::foo(); +} diff --git a/tests/run-pass/auxiliary/dep.rs b/tests/run-pass/auxiliary/dep.rs new file mode 100644 index 0000000000000..b76b4321d62aa --- /dev/null +++ b/tests/run-pass/auxiliary/dep.rs @@ -0,0 +1 @@ +pub fn foo() {} From 24203602e11e50994f45f2f30efcdfd05e96a7e1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Dec 2016 09:56:40 +0100 Subject: [PATCH 0695/1096] remove unused import --- src/bin/miri.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index d46572606a7fd..9e1d6235d4c18 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -10,7 +10,7 @@ extern crate syntax; #[macro_use] extern crate log; use rustc::session::Session; -use rustc_driver::{CompilerCalls, Compilation}; +use rustc_driver::CompilerCalls; use rustc_driver::driver::{CompileState, CompileController}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; From fd0c21eeee5570ba0cc357db90a8337e4d5690ea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Dec 2016 09:58:41 +0100 Subject: [PATCH 0696/1096] check that the null terminator is defined and not part of a pointer --- src/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 580372b3da2df..babd9bcb783f9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -536,10 +536,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = ptr.offset as usize; match alloc.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - if self.relocations(ptr, size as u64)?.count() != 0 { + if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } - self.check_defined(ptr, size as u64)?; + self.check_defined(ptr, (size + 1) as u64)?; Ok(&alloc.bytes[offset..offset + size]) }, None => Err(EvalError::UnterminatedCString(ptr)), From 0a79304fcbcfea7780556bcda4e6f0cea32b8be2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Dec 2016 09:58:57 +0100 Subject: [PATCH 0697/1096] improve variable name --- src/terminator/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 95649f4a8b379..23d59e3fc3e60 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -389,8 +389,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "getenv" => { { - let name = args[0].read_ptr(&self.memory)?; - let name = self.memory.read_c_str(name)?; + let name_ptr = args[0].read_ptr(&self.memory)?; + let name = self.memory.read_c_str(name_ptr)?; info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); } self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; From 0deabf9c00edb1943ef1e02ec802b5dc6dfa1c83 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 15 Dec 2016 01:16:06 -0800 Subject: [PATCH 0698/1096] Revert "rustup to rustc 1.15.0-dev (ace092f56 2016-12-13)" --- src/bin/miri.rs | 22 +++++------ src/error.rs | 3 -- src/memory.rs | 16 -------- src/terminator/intrinsic.rs | 1 - src/terminator/mod.rs | 55 +++++--------------------- tests/compiletest.rs | 70 +++++++++++++++++++++++++++------ tests/run-pass/aux_test.rs | 7 ---- tests/run-pass/auxiliary/dep.rs | 1 - 8 files changed, 78 insertions(+), 97 deletions(-) delete mode 100644 tests/run-pass/aux_test.rs delete mode 100644 tests/run-pass/auxiliary/dep.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 9e1d6235d4c18..6c0161ef0754a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -10,7 +10,7 @@ extern crate syntax; #[macro_use] extern crate log; use rustc::session::Session; -use rustc_driver::CompilerCalls; +use rustc_driver::{CompilerCalls, Compilation}; use rustc_driver::driver::{CompileState, CompileController}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; @@ -21,6 +21,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut control = CompileController::basic(); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); + control.after_analysis.stop = Compilation::Stop; control } } @@ -34,16 +35,14 @@ fn after_analysis(state: &mut CompileState) { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { - let entry_def_id = tcx.map.local_def_id(entry_node_id); - let limits = resource_limits_from_attributes(state); - miri::run_mir_passes(tcx); - miri::eval_main(tcx, entry_def_id, limits); - - state.session.abort_if_errors(); - } else { - println!("no main function found, assuming auxiliary build"); - } + let (entry_node_id, _) = state.session.entry_fn.borrow() + .expect("no main or start function found"); + let entry_def_id = tcx.map.local_def_id(entry_node_id); + let limits = resource_limits_from_attributes(state); + miri::run_mir_passes(tcx); + miri::eval_main(tcx, entry_def_id, limits); + + state.session.abort_if_errors(); } fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits { @@ -135,7 +134,6 @@ fn main() { args.push(sysroot_flag); args.push(find_sysroot()); } - args.push("-Zalways-encode-mir".to_owned()); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); } diff --git a/src/error.rs b/src/error.rs index 9b532e0137592..afc1855e8e755 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,7 +11,6 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), - UnterminatedCString(Pointer), DanglingPointerDeref, InvalidMemoryAccess, InvalidFunctionPointer, @@ -120,8 +119,6 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to deallocate frozen memory", EvalError::Layout(_) => "rustc layout computation failed", - EvalError::UnterminatedCString(_) => - "attempted to get length of a null terminated string, but no null found before end of allocation", } } diff --git a/src/memory.rs b/src/memory.rs index babd9bcb783f9..671174fd75690 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -530,22 +530,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { - let alloc = self.get(ptr.alloc_id)?; - assert_eq!(ptr.offset as usize as u64, ptr.offset); - let offset = ptr.offset as usize; - match alloc.bytes[offset..].iter().position(|&c| c == 0) { - Some(size) => { - if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { - return Err(EvalError::ReadPointerAsBytes); - } - self.check_defined(ptr, (size + 1) as u64)?; - Ok(&alloc.bytes[offset..offset + size]) - }, - None => Err(EvalError::UnterminatedCString(ptr)), - } - } - pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { self.get_bytes(ptr, size, 1) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 443696edb1228..9be094d9bd977 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -57,7 +57,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_load" | - "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 23d59e3fc3e60..fdc7c1db6faa7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -90,16 +90,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); - let bare_sig = self.tcx.erase_regions(&bare_sig); - // transmuting function pointers in miri is fine as long as the number of - // arguments and the abi don't change. - // FIXME: also check the size of the arguments' type and the return type - // Didn't get it to work, since that triggers an assertion in rustc which - // checks whether the type has escaping regions - if abi != bare_fn_ty.abi || - sig.variadic != bare_sig.variadic || - sig.inputs().len() != bare_sig.inputs().len() { + if abi != bare_fn_ty.abi || sig != bare_fn_ty.sig.skip_binder() { return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); } self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, @@ -198,7 +189,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { - let ty = fn_ty.sig.0.output(); + let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty)?; let (ret, target) = destination.unwrap(); self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; @@ -206,7 +197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Abi::C => { - let ty = fn_ty.sig.0.output(); + let ty = fn_ty.sig.0.output; let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.goto_block(target); @@ -329,6 +320,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect(); let args = args_res?; + if link_name.starts_with("pthread_") { + warn!("ignoring C ABI call: {}", link_name); + return Ok(()); + } + let usize = self.tcx.types.usize; match &link_name[..] { @@ -375,37 +371,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::new(result as u64), dest_ty)?; } - "memchr" => { - let ptr = args[0].read_ptr(&self.memory)?; - let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; - let num = self.value_to_primval(args[2], usize)?.to_u64(); - if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { - let new_ptr = ptr.offset(idx as u64); - self.write_value(Value::ByVal(PrimVal::from_ptr(new_ptr)), dest, dest_ty)?; - } else { - self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; - } - } - - "getenv" => { - { - let name_ptr = args[0].read_ptr(&self.memory)?; - let name = self.memory.read_c_str(name_ptr)?; - info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); - } - self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; - } - - // unix panic code inside libstd will read the return value of this function - "pthread_rwlock_rdlock" => { - self.write_primval(dest, PrimVal::new(0), dest_ty)?; - } - - link_name if link_name.starts_with("pthread_") => { - warn!("ignoring C ABI call: {}", link_name); - return Ok(()); - }, - _ => { return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); } @@ -555,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - *first_ty = sig.inputs()[0]; + *first_ty = sig.inputs[0]; Ok((def_id, substs, Vec::new())) } else { Err(EvalError::VtableForArgumentlessMethod) @@ -699,7 +664,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; - let real_ty = sig.inputs()[0]; + let real_ty = sig.inputs[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); } else { diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f7bf16926ba7c..1ee86a07b22f5 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,15 +27,6 @@ fn run_pass() { compiletest::run_tests(&config); } -fn miri_pass(path: &str, target: &str) { - let mut config = compiletest::default_config(); - config.mode = "mir-opt".parse().expect("Invalid mode"); - config.src_base = PathBuf::from(path); - config.target = target.to_owned(); - config.rustc_path = PathBuf::from("target/debug/miri"); - compiletest::run_tests(&config); -} - fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { let target = target.unwrap(); @@ -66,10 +57,65 @@ fn compile_test() { }; run_pass(); for_all_targets(&sysroot, |target| { - miri_pass("tests/run-pass", &target); - if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - miri_pass(&path, &target); + let files = std::fs::read_dir("tests/run-pass").unwrap(); + let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + Box::new(files.chain(std::fs::read_dir(path).unwrap())) + } else { + Box::new(files) + }; + let mut mir_not_found = 0; + let mut crate_not_found = 0; + let mut success = 0; + let mut failed = 0; + for file in files { + let file = file.unwrap(); + let path = file.path(); + + if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { + continue; + } + + let stderr = std::io::stderr(); + write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); + let mut cmd = std::process::Command::new("target/debug/miri"); + cmd.arg(path); + cmd.arg(format!("--target={}", target)); + let libs = Path::new(&sysroot).join("lib"); + let sysroot = libs.join("rustlib").join(&target).join("lib"); + let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); + cmd.env(compiletest::procsrv::dylib_env_var(), paths); + + match cmd.output() { + Ok(ref output) if output.status.success() => { + success += 1; + writeln!(stderr.lock(), "ok").unwrap() + }, + Ok(output) => { + let output_err = std::str::from_utf8(&output.stderr).unwrap(); + if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { + mir_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); + } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { + crate_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); + } else { + failed += 1; + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); + writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + } + } + Err(e) => { + writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); + panic!("failed to execute miri"); + }, + } } + let stderr = std::io::stderr(); + writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); + assert_eq!(failed, 0, "some tests failed"); }); compile_fail(&sysroot); } diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs deleted file mode 100644 index aa471f6cf8fdf..0000000000000 --- a/tests/run-pass/aux_test.rs +++ /dev/null @@ -1,7 +0,0 @@ -// aux-build:dep.rs - -extern crate dep; - -fn main() { - dep::foo(); -} diff --git a/tests/run-pass/auxiliary/dep.rs b/tests/run-pass/auxiliary/dep.rs deleted file mode 100644 index b76b4321d62aa..0000000000000 --- a/tests/run-pass/auxiliary/dep.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn foo() {} From 6ec3d65068cbcbb606b62da8a53744d6844b6cfc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 17:10:16 -0800 Subject: [PATCH 0699/1096] Revert "Revert "rustup to rustc 1.15.0-dev (ace092f56 2016-12-13)"" --- src/bin/miri.rs | 22 ++++++----- src/error.rs | 3 ++ src/memory.rs | 16 ++++++++ src/terminator/intrinsic.rs | 1 + src/terminator/mod.rs | 55 +++++++++++++++++++++----- tests/compiletest.rs | 70 ++++++--------------------------- tests/run-pass/aux_test.rs | 7 ++++ tests/run-pass/auxiliary/dep.rs | 1 + 8 files changed, 97 insertions(+), 78 deletions(-) create mode 100644 tests/run-pass/aux_test.rs create mode 100644 tests/run-pass/auxiliary/dep.rs diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6c0161ef0754a..9e1d6235d4c18 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -10,7 +10,7 @@ extern crate syntax; #[macro_use] extern crate log; use rustc::session::Session; -use rustc_driver::{CompilerCalls, Compilation}; +use rustc_driver::CompilerCalls; use rustc_driver::driver::{CompileState, CompileController}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; @@ -21,7 +21,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut control = CompileController::basic(); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); - control.after_analysis.stop = Compilation::Stop; control } } @@ -35,14 +34,16 @@ fn after_analysis(state: &mut CompileState) { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - let (entry_node_id, _) = state.session.entry_fn.borrow() - .expect("no main or start function found"); - let entry_def_id = tcx.map.local_def_id(entry_node_id); - let limits = resource_limits_from_attributes(state); - miri::run_mir_passes(tcx); - miri::eval_main(tcx, entry_def_id, limits); - - state.session.abort_if_errors(); + if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { + let entry_def_id = tcx.map.local_def_id(entry_node_id); + let limits = resource_limits_from_attributes(state); + miri::run_mir_passes(tcx); + miri::eval_main(tcx, entry_def_id, limits); + + state.session.abort_if_errors(); + } else { + println!("no main function found, assuming auxiliary build"); + } } fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits { @@ -134,6 +135,7 @@ fn main() { args.push(sysroot_flag); args.push(find_sysroot()); } + args.push("-Zalways-encode-mir".to_owned()); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); } diff --git a/src/error.rs b/src/error.rs index afc1855e8e755..9b532e0137592 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,6 +11,7 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), NoMirFor(String), + UnterminatedCString(Pointer), DanglingPointerDeref, InvalidMemoryAccess, InvalidFunctionPointer, @@ -119,6 +120,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to deallocate frozen memory", EvalError::Layout(_) => "rustc layout computation failed", + EvalError::UnterminatedCString(_) => + "attempted to get length of a null terminated string, but no null found before end of allocation", } } diff --git a/src/memory.rs b/src/memory.rs index 671174fd75690..babd9bcb783f9 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -530,6 +530,22 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } + pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { + let alloc = self.get(ptr.alloc_id)?; + assert_eq!(ptr.offset as usize as u64, ptr.offset); + let offset = ptr.offset as usize; + match alloc.bytes[offset..].iter().position(|&c| c == 0) { + Some(size) => { + if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { + return Err(EvalError::ReadPointerAsBytes); + } + self.check_defined(ptr, (size + 1) as u64)?; + Ok(&alloc.bytes[offset..offset + size]) + }, + None => Err(EvalError::UnterminatedCString(ptr)), + } + } + pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { self.get_bytes(ptr, size, 1) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 9be094d9bd977..443696edb1228 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_load" | + "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index fdc7c1db6faa7..23d59e3fc3e60 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -90,7 +90,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - if abi != bare_fn_ty.abi || sig != bare_fn_ty.sig.skip_binder() { + let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); + let bare_sig = self.tcx.erase_regions(&bare_sig); + // transmuting function pointers in miri is fine as long as the number of + // arguments and the abi don't change. + // FIXME: also check the size of the arguments' type and the return type + // Didn't get it to work, since that triggers an assertion in rustc which + // checks whether the type has escaping regions + if abi != bare_fn_ty.abi || + sig.variadic != bare_sig.variadic || + sig.inputs().len() != bare_sig.inputs().len() { return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); } self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, @@ -189,7 +198,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { - let ty = fn_ty.sig.0.output; + let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; let (ret, target) = destination.unwrap(); self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; @@ -197,7 +206,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Abi::C => { - let ty = fn_ty.sig.0.output; + let ty = fn_ty.sig.0.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.goto_block(target); @@ -320,11 +329,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect(); let args = args_res?; - if link_name.starts_with("pthread_") { - warn!("ignoring C ABI call: {}", link_name); - return Ok(()); - } - let usize = self.tcx.types.usize; match &link_name[..] { @@ -371,6 +375,37 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::new(result as u64), dest_ty)?; } + "memchr" => { + let ptr = args[0].read_ptr(&self.memory)?; + let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64(); + if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { + let new_ptr = ptr.offset(idx as u64); + self.write_value(Value::ByVal(PrimVal::from_ptr(new_ptr)), dest, dest_ty)?; + } else { + self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + } + } + + "getenv" => { + { + let name_ptr = args[0].read_ptr(&self.memory)?; + let name = self.memory.read_c_str(name_ptr)?; + info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); + } + self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + } + + // unix panic code inside libstd will read the return value of this function + "pthread_rwlock_rdlock" => { + self.write_primval(dest, PrimVal::new(0), dest_ty)?; + } + + link_name if link_name.starts_with("pthread_") => { + warn!("ignoring C ABI call: {}", link_name); + return Ok(()); + }, + _ => { return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))); } @@ -520,7 +555,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - *first_ty = sig.inputs[0]; + *first_ty = sig.inputs()[0]; Ok((def_id, substs, Vec::new())) } else { Err(EvalError::VtableForArgumentlessMethod) @@ -664,7 +699,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; - let real_ty = sig.inputs[0]; + let real_ty = sig.inputs()[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); } else { diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 1ee86a07b22f5..f7bf16926ba7c 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,6 +27,15 @@ fn run_pass() { compiletest::run_tests(&config); } +fn miri_pass(path: &str, target: &str) { + let mut config = compiletest::default_config(); + config.mode = "mir-opt".parse().expect("Invalid mode"); + config.src_base = PathBuf::from(path); + config.target = target.to_owned(); + config.rustc_path = PathBuf::from("target/debug/miri"); + compiletest::run_tests(&config); +} + fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { let target = target.unwrap(); @@ -57,65 +66,10 @@ fn compile_test() { }; run_pass(); for_all_targets(&sysroot, |target| { - let files = std::fs::read_dir("tests/run-pass").unwrap(); - let files: Box> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - Box::new(files.chain(std::fs::read_dir(path).unwrap())) - } else { - Box::new(files) - }; - let mut mir_not_found = 0; - let mut crate_not_found = 0; - let mut success = 0; - let mut failed = 0; - for file in files { - let file = file.unwrap(); - let path = file.path(); - - if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { - continue; - } - - let stderr = std::io::stderr(); - write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); - let mut cmd = std::process::Command::new("target/debug/miri"); - cmd.arg(path); - cmd.arg(format!("--target={}", target)); - let libs = Path::new(&sysroot).join("lib"); - let sysroot = libs.join("rustlib").join(&target).join("lib"); - let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); - cmd.env(compiletest::procsrv::dylib_env_var(), paths); - - match cmd.output() { - Ok(ref output) if output.status.success() => { - success += 1; - writeln!(stderr.lock(), "ok").unwrap() - }, - Ok(output) => { - let output_err = std::str::from_utf8(&output.stderr).unwrap(); - if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { - mir_not_found += 1; - let end = text.find('`').unwrap(); - writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); - } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { - crate_not_found += 1; - let end = text.find('`').unwrap(); - writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); - } else { - failed += 1; - writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); - writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); - writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); - } - } - Err(e) => { - writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); - panic!("failed to execute miri"); - }, - } + miri_pass("tests/run-pass", &target); + if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + miri_pass(&path, &target); } - let stderr = std::io::stderr(); - writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); - assert_eq!(failed, 0, "some tests failed"); }); compile_fail(&sysroot); } diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs new file mode 100644 index 0000000000000..aa471f6cf8fdf --- /dev/null +++ b/tests/run-pass/aux_test.rs @@ -0,0 +1,7 @@ +// aux-build:dep.rs + +extern crate dep; + +fn main() { + dep::foo(); +} diff --git a/tests/run-pass/auxiliary/dep.rs b/tests/run-pass/auxiliary/dep.rs new file mode 100644 index 0000000000000..b76b4321d62aa --- /dev/null +++ b/tests/run-pass/auxiliary/dep.rs @@ -0,0 +1 @@ +pub fn foo() {} From 539e7e0ae1e7f473d2245769c7da946615170d77 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 17:40:24 -0800 Subject: [PATCH 0700/1096] Update compiletest_rs to 0.2.5. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0c7b1a011ee1..b63b557f07161 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "compiletest_rs" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -137,7 +137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" -"checksum compiletest_rs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "28d60af0dbee4912f00dda79ac3b06d1ca44b641d69359e6f1d4df7c985521d2" +"checksum compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f344389765ad7bec166f64c1b39ed6dd2b54d81c4c5dd8af789169351d380c" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" diff --git a/Cargo.toml b/Cargo.toml index a770bea069499..1d5de26f00730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,4 @@ log = "0.3.6" log_settings = "0.1.1" [dev-dependencies] -compiletest_rs = "0.2.3" +compiletest_rs = "0.2.5" From b36a83171bf0de5a1b8d139363b1b469e273c994 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 15 Dec 2016 23:40:45 -0800 Subject: [PATCH 0701/1096] Turn PrimVal into an enum including Undefined. This is step 1 of a refactoring to fix #95. The `Undefined` variant is so far unused and the old `bits` and `relocation` fields are emulated with two new temporary methods. There should be no functional change due to this commit. --- src/cast.rs | 26 +++++++------- src/eval_context.rs | 50 +++++++++++++------------- src/memory.rs | 14 ++++---- src/operator.rs | 46 ++++++++++++------------ src/terminator/intrinsic.rs | 32 ++++++++--------- src/terminator/mod.rs | 14 ++++---- src/value.rs | 70 ++++++++++++++++++++++--------------- 7 files changed, 132 insertions(+), 120 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index a49b3b1248079..4412bd3d41884 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -20,9 +20,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { F32 => self.cast_float(val.to_f32() as f64, dest_ty), F64 => self.cast_float(val.to_f64(), dest_ty), - I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits as i64, dest_ty), + I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits() as i64, dest_ty), - Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, dest_ty, false), + Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits(), dest_ty, false), FnPtr | Ptr => self.cast_ptr(val.to_ptr(), dest_ty), } @@ -39,15 +39,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyBool if v == 1 => Ok(PrimVal::from_bool(true)), TyBool => Err(EvalError::InvalidBool), - TyInt(IntTy::I8) => Ok(PrimVal::new(v as i64 as i8 as u64)), - TyInt(IntTy::I16) => Ok(PrimVal::new(v as i64 as i16 as u64)), - TyInt(IntTy::I32) => Ok(PrimVal::new(v as i64 as i32 as u64)), - TyInt(IntTy::I64) => Ok(PrimVal::new(v as i64 as i64 as u64)), + TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i64 as i8 as u64)), + TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i64 as i16 as u64)), + TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i64 as i32 as u64)), + TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i64 as i64 as u64)), - TyUint(UintTy::U8) => Ok(PrimVal::new(v as u8 as u64)), - TyUint(UintTy::U16) => Ok(PrimVal::new(v as u16 as u64)), - TyUint(UintTy::U32) => Ok(PrimVal::new(v as u32 as u64)), - TyUint(UintTy::U64) => Ok(PrimVal::new(v)), + TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u64)), + TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u64)), + TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u64)), + TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v)), TyInt(IntTy::Is) => { let int_ty = self.tcx.sess.target.int_type; @@ -66,10 +66,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i64 as f32)), TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), - TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v)), + TyChar if v as u8 as u64 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), - TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v))), + TyRawPtr(_) => Ok(PrimVal::Pointer(Pointer::from_int(v))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } @@ -94,7 +94,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::TypeVariants::*; match ty.sty { TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => - Ok(PrimVal::from_ptr(ptr)), + Ok(PrimVal::Pointer(ptr)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 713c01602c786..7ed1a17e44f56 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::from_ptr(ptr), PrimVal::from_uint(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::Pointer(ptr), PrimVal::from_uint(s.len() as u64))) } pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -180,7 +180,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::ConstFloat; let primval = match *const_val { - Integral(const_int) => PrimVal::new(const_int.to_u64_unchecked()), + Integral(const_int) => PrimVal::Bytes(const_int.to_u64_unchecked()), Float(ConstFloat::F32(f)) => PrimVal::from_f32(f), Float(ConstFloat::F64(f)) => PrimVal::from_f64(f), @@ -196,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(bs.len() as u64, 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - PrimVal::from_ptr(ptr) + PrimVal::Pointer(ptr) } Struct(_) => unimplemented!(), @@ -315,14 +315,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("global should have been cached (freeze)"); match global_value.data.expect("global should have been initialized") { Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?, - Value::ByVal(val) => if let Some(alloc_id) = val.relocation { + Value::ByVal(val) => if let Some(alloc_id) = val.relocation() { self.memory.freeze(alloc_id)?; }, Value::ByValPair(a, b) => { - if let Some(alloc_id) = a.relocation { + if let Some(alloc_id) = a.relocation() { self.memory.freeze(alloc_id)?; } - if let Some(alloc_id) = b.relocation { + if let Some(alloc_id) = b.relocation() { self.memory.freeze(alloc_id)?; } }, @@ -500,7 +500,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let n = adt_def.variants[variant].disr_val.to_u64_unchecked(); - self.write_primval(dest, PrimVal::new(n), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); } @@ -563,12 +563,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); - let ptr = PrimVal::from_ptr(raw_ptr); + let ptr = PrimVal::Pointer(raw_ptr); let val = match extra { LvalueExtra::None => Value::ByVal(ptr), LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_uint(len)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::from_ptr(vtable)), + LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Pointer(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -578,7 +578,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let ptr = self.alloc_ptr(ty)?; - self.write_primval(dest, PrimVal::from_ptr(ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Pointer(ptr), dest_ty)?; } Cast(kind, ref operand, cast_ty) => { @@ -617,7 +617,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(def_id, substs, fn_ty) => { let fn_ty = self.tcx.erase_regions(&fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); - self.write_value(Value::ByVal(PrimVal::from_ptr(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Pointer(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), }, @@ -629,7 +629,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); - self.write_value(Value::ByVal(PrimVal::from_ptr(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Pointer(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), }, @@ -1093,10 +1093,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { match ty.sty { - ty::TyBool if val.bits > 1 => Err(EvalError::InvalidBool), + ty::TyBool if val.bits() > 1 => Err(EvalError::InvalidBool), - ty::TyChar if ::std::char::from_u32(val.bits as u32).is_none() - => Err(EvalError::InvalidChar(val.bits as u32 as u64)), + ty::TyChar if ::std::char::from_u32(val.bits() as u32).is_none() + => Err(EvalError::InvalidChar(val.bits() as u32 as u64)), _ => Ok(()), } @@ -1150,23 +1150,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_ptr)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Pointer)?, ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { - PrimVal::from_ptr(p) + PrimVal::Pointer(p) } else { trace!("reading fat pointer extra of type {}", ty); let extra = ptr.offset(self.memory.pointer_size()); let extra = match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?), + ty::TyDynamic(..) => PrimVal::Pointer(self.memory.read_ptr(extra)?), ty::TySlice(..) | ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; - return Ok(Some(Value::ByValPair(PrimVal::from_ptr(p), extra))); + return Ok(Some(Value::ByValPair(PrimVal::Pointer(p), extra))); } } @@ -1225,7 +1225,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; let len = PrimVal::from_uint(length as u64); - let ptr = PrimVal::from_ptr(ptr); + let ptr = PrimVal::Pointer(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { @@ -1239,8 +1239,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - let ptr = PrimVal::from_ptr(ptr); - let extra = PrimVal::from_ptr(vtable); + let ptr = PrimVal::Pointer(ptr); + let extra = PrimVal::Pointer(vtable); self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty)?; }, @@ -1301,12 +1301,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Value::ByVal(val) => { trace!("frame[{}] {:?}: {:?}", frame, local, val); - if let Some(alloc_id) = val.relocation { allocs.push(alloc_id); } + if let Some(alloc_id) = val.relocation() { allocs.push(alloc_id); } } Value::ByValPair(val1, val2) => { trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); - if let Some(alloc_id) = val1.relocation { allocs.push(alloc_id); } - if let Some(alloc_id) = val2.relocation { allocs.push(alloc_id); } + if let Some(alloc_id) = val1.relocation() { allocs.push(alloc_id); } + if let Some(alloc_id) = val2.relocation() { allocs.push(alloc_id); } } } } diff --git a/src/memory.rs b/src/memory.rs index babd9bcb783f9..ac457966b31ab 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -587,18 +587,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { val: PrimVal, kind: PrimValKind, ) -> EvalResult<'tcx, ()> { - if let Some(alloc_id) = val.relocation { - return self.write_ptr(dest, Pointer::new(alloc_id, val.bits)); + if let Some(alloc_id) = val.relocation() { + return self.write_ptr(dest, Pointer::new(alloc_id, val.bits())); } use value::PrimValKind::*; let (size, bits) = match kind { - I8 | U8 | Bool => (1, val.bits as u8 as u64), - I16 | U16 => (2, val.bits as u16 as u64), - I32 | U32 | F32 | Char => (4, val.bits as u32 as u64), - I64 | U64 | F64 => (8, val.bits), + I8 | U8 | Bool => (1, val.bits() as u8 as u64), + I16 | U16 => (2, val.bits() as u16 as u64), + I32 | U32 | F32 | Char => (4, val.bits() as u32 as u64), + I64 | U64 | F64 => (8, val.bits()), // int -> ptr transmutes are handled here - FnPtr | Ptr => return self.write_usize(dest, val.bits), + FnPtr | Ptr => return self.write_usize(dest, val.bits()), }; self.write_uint(dest, bits, size) diff --git a/src/operator.rs b/src/operator.rs index 6e656d69f6558..9e879eaf98077 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { macro_rules! overflow { ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); - let primval = PrimVal::new(val as u64); + let primval = PrimVal::Bytes(val as u64); Ok((primval, overflowed)) }) } @@ -112,7 +112,7 @@ macro_rules! float_arithmetic { let l = $from_bits($l); let r = $from_bits($r); let bits = $to_bits(l $float_op r); - PrimVal::new(bits) + PrimVal::Bytes(bits) }) } @@ -148,7 +148,7 @@ pub fn binary_op<'tcx>( return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } - let (l, r) = (left.bits, right.bits); + let (l, r) = (left.bits(), right.bits()); // These ops can have an RHS with a different numeric type. if bin_op == Shl || bin_op == Shr { @@ -165,7 +165,7 @@ pub fn binary_op<'tcx>( // Cast to `u32` because `overflowing_sh{l,r}` only take `u32`, then apply the bitmask // to ensure it's within the valid shift value range. - let r = (right.bits as u32) & (type_bits - 1); + let r = (right.bits() as u32) & (type_bits - 1); return match bin_op { Shl => int_shift!(left_kind, overflowing_shl, l, r), @@ -213,9 +213,9 @@ pub fn binary_op<'tcx>( (Gt, _) => PrimVal::from_bool(l > r), (Ge, _) => PrimVal::from_bool(l >= r), - (BitOr, _) => PrimVal::new(l | r), - (BitAnd, _) => PrimVal::new(l & r), - (BitXor, _) => PrimVal::new(l ^ r), + (BitOr, _) => PrimVal::Bytes(l | r), + (BitAnd, _) => PrimVal::Bytes(l & r), + (BitXor, _) => PrimVal::Bytes(l ^ r), (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), @@ -254,25 +254,25 @@ pub fn unary_op<'tcx>( use value::PrimValKind::*; let bits = match (un_op, val_kind) { - (Not, Bool) => !bits_to_bool(val.bits) as u64, + (Not, Bool) => !bits_to_bool(val.bits()) as u64, - (Not, U8) => !(val.bits as u8) as u64, - (Not, U16) => !(val.bits as u16) as u64, - (Not, U32) => !(val.bits as u32) as u64, - (Not, U64) => !val.bits, + (Not, U8) => !(val.bits() as u8) as u64, + (Not, U16) => !(val.bits() as u16) as u64, + (Not, U32) => !(val.bits() as u32) as u64, + (Not, U64) => !val.bits(), - (Not, I8) => !(val.bits as i8) as u64, - (Not, I16) => !(val.bits as i16) as u64, - (Not, I32) => !(val.bits as i32) as u64, - (Not, I64) => !(val.bits as i64) as u64, + (Not, I8) => !(val.bits() as i8) as u64, + (Not, I16) => !(val.bits() as i16) as u64, + (Not, I32) => !(val.bits() as i32) as u64, + (Not, I64) => !(val.bits() as i64) as u64, - (Neg, I8) => -(val.bits as i8) as u64, - (Neg, I16) => -(val.bits as i16) as u64, - (Neg, I32) => -(val.bits as i32) as u64, - (Neg, I64) => -(val.bits as i64) as u64, + (Neg, I8) => -(val.bits() as i8) as u64, + (Neg, I16) => -(val.bits() as i16) as u64, + (Neg, I32) => -(val.bits() as i32) as u64, + (Neg, I64) => -(val.bits() as i64) as u64, - (Neg, F32) => f32_to_bits(-bits_to_f32(val.bits)), - (Neg, F64) => f64_to_bits(-bits_to_f64(val.bits)), + (Neg, F32) => f32_to_bits(-bits_to_f32(val.bits())), + (Neg, F64) => f64_to_bits(-bits_to_f64(val.bits())), _ => { let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); @@ -280,5 +280,5 @@ pub fn unary_op<'tcx>( } }; - Ok(PrimVal::new(bits)) + Ok(PrimVal::Bytes(bits)) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 443696edb1228..de9c972ffd41b 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); let new_ptr = ptr.signed_offset(offset); - self.write_primval(dest, PrimVal::from_ptr(new_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Pointer(new_ptr), dest_ty)?; } "assume" => { @@ -171,7 +171,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - self.write_primval(dest, PrimVal::new(discr_val), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } "drop_in_place" => { @@ -235,16 +235,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) }, None => match this.ty_to_primval_kind(dest_ty) { - Ok(_) => Value::ByVal(PrimVal::new(0)), + Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) } }, - Some(Value::ByVal(_)) => Value::ByVal(PrimVal::new(0)), + Some(Value::ByVal(_)) => Value::ByVal(PrimVal::Bytes(0)), Some(Value::ByValPair(..)) => - Value::ByValPair(PrimVal::new(0), PrimVal::new(0)), + Value::ByValPair(PrimVal::Bytes(0), PrimVal::Bytes(0)), }; Ok(Some(zero_val)) }; @@ -292,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.signed_offset(offset * pointee_size); - self.write_primval(dest, PrimVal::from_ptr(result_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Pointer(result_ptr), dest_ty)?; } "overflowing_sub" => { @@ -361,7 +361,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); - self.write_primval(dest, PrimVal::new(n), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; } "transmute" => { @@ -507,18 +507,18 @@ macro_rules! integer_intrinsic { use value::PrimValKind::*; let bits = match $kind { - I8 => (val.bits as i8).$method() as u64, - U8 => (val.bits as u8).$method() as u64, - I16 => (val.bits as i16).$method() as u64, - U16 => (val.bits as u16).$method() as u64, - I32 => (val.bits as i32).$method() as u64, - U32 => (val.bits as u32).$method() as u64, - I64 => (val.bits as i64).$method() as u64, - U64 => (val.bits as u64).$method() as u64, + I8 => (val.bits() as i8).$method() as u64, + U8 => (val.bits() as u8).$method() as u64, + I16 => (val.bits() as i16).$method() as u64, + U16 => (val.bits() as u16).$method() as u64, + I32 => (val.bits() as i32).$method() as u64, + U32 => (val.bits() as u32).$method() as u64, + I64 => (val.bits() as i64).$method() as u64, + U64 => (val.bits() as u64).$method() as u64, _ => bug!("invalid `{}` argument: {:?}", $name, val), }; - PrimVal::new(bits) + PrimVal::Bytes(bits) }); } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 23d59e3fc3e60..c48a8a72c3d5c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -336,7 +336,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[0], usize)?.to_u64(); let align = self.value_to_primval(args[1], usize)?.to_u64(); let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::from_ptr(ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Pointer(ptr), dest_ty)?; } "__rust_deallocate" => { @@ -352,7 +352,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[2], usize)?.to_u64(); let align = self.value_to_primval(args[3], usize)?.to_u64(); let new_ptr = self.memory.reallocate(ptr, size, align)?; - self.write_primval(dest, PrimVal::from_ptr(new_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Pointer(new_ptr), dest_ty)?; } "memcmp" => { @@ -372,7 +372,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - self.write_primval(dest, PrimVal::new(result as u64), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(result as u64), dest_ty)?; } "memchr" => { @@ -527,7 +527,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ptr }, }; - args[0].0 = Value::ByVal(PrimVal::from_ptr(ptr)); + args[0].0 = Value::ByVal(PrimVal::Pointer(ptr)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; - *first_arg = Value::ByVal(PrimVal::from_ptr(self_ptr)); + *first_arg = Value::ByVal(PrimVal::Pointer(self_ptr)); let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; @@ -633,7 +633,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // run drop impl before the fields' drop impls if let Some(drop_def_id) = adt_def.destructor() { - drop.push((drop_def_id, Value::ByVal(PrimVal::from_ptr(adt_ptr)), substs)); + drop.push((drop_def_id, Value::ByVal(PrimVal::Pointer(adt_ptr)), substs)); } let layout = self.type_layout(ty)?; let fields = match *layout { @@ -701,7 +701,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; let real_ty = sig.inputs()[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - drop.push((def_id, Value::ByVal(PrimVal::from_ptr(ptr)), substs)); + drop.push((def_id, Value::ByVal(PrimVal::Pointer(ptr)), substs)); } else { // just a sanity check assert_eq!(drop_fn.offset, 0); diff --git a/src/value.rs b/src/value.rs index 4642018c12465..7055548095d07 100644 --- a/src/value.rs +++ b/src/value.rs @@ -46,13 +46,17 @@ pub enum Value { /// A `PrimVal` represents an immediate, primitive value existing outside of an allocation. It is /// considered to be like a #[derive(Clone, Copy, Debug, PartialEq)] -pub struct PrimVal { - pub bits: u64, +pub enum PrimVal { + Bytes(u64), + // FIXME(solson): Rename this variant to Ptr. + // FIXME(solson): Outdated comment, pulled from `relocations` field I deleted. /// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only /// large enough to contain one, hence the `Option`. - pub relocation: Option, + Pointer(Pointer), + + Undefined, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -110,56 +114,64 @@ impl<'a, 'tcx: 'a> Value { } impl PrimVal { - pub fn new(bits: u64) -> Self { - PrimVal { bits: bits, relocation: None } - } - - pub fn new_with_relocation(bits: u64, alloc_id: AllocId) -> Self { - PrimVal { bits: bits, relocation: Some(alloc_id) } + // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't + // stick around with this name. + pub fn bits(&self) -> u64 { + match *self { + PrimVal::Bytes(b) => b, + PrimVal::Pointer(p) => p.offset, + PrimVal::Undefined => panic!(".bits()() on PrimVal::Undefined"), + } } - pub fn from_ptr(ptr: Pointer) -> Self { - PrimVal::new_with_relocation(ptr.offset as u64, ptr.alloc_id) + // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't + // stick around with this name. + pub fn relocation(&self) -> Option { + if let PrimVal::Pointer(ref p) = *self { + Some(p.alloc_id) + } else { + None + } } pub fn from_bool(b: bool) -> Self { - PrimVal::new(b as u64) + PrimVal::Bytes(b as u64) } pub fn from_char(c: char) -> Self { - PrimVal::new(c as u64) + PrimVal::Bytes(c as u64) } pub fn from_f32(f: f32) -> Self { - PrimVal::new(f32_to_bits(f)) + PrimVal::Bytes(f32_to_bits(f)) } pub fn from_f64(f: f64) -> Self { - PrimVal::new(f64_to_bits(f)) + PrimVal::Bytes(f64_to_bits(f)) } pub fn from_uint(n: u64) -> Self { - PrimVal::new(n) + PrimVal::Bytes(n) } pub fn from_int(n: i64) -> Self { - PrimVal::new(n as u64) + PrimVal::Bytes(n as u64) } pub fn to_f32(self) -> f32 { - assert!(self.relocation.is_none()); - bits_to_f32(self.bits) + assert!(self.relocation().is_none()); + bits_to_f32(self.bits()) } pub fn to_f64(self) -> f64 { - assert!(self.relocation.is_none()); - bits_to_f64(self.bits) + assert!(self.relocation().is_none()); + bits_to_f64(self.bits()) } pub fn to_ptr(self) -> Pointer { - self.relocation.map(|alloc_id| { - Pointer::new(alloc_id, self.bits) - }).unwrap_or_else(|| Pointer::from_int(self.bits)) + self.relocation().map(|alloc_id| { + Pointer::new(alloc_id, self.bits()) + }).unwrap_or_else(|| Pointer::from_int(self.bits())) } pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { @@ -170,24 +182,24 @@ impl PrimVal { if let Some(ptr) = self.try_as_ptr() { return ptr.to_int().expect("non abstract ptr") as u64; } - self.bits + self.bits() } pub fn to_i64(self) -> i64 { if let Some(ptr) = self.try_as_ptr() { return ptr.to_int().expect("non abstract ptr") as i64; } - self.bits as i64 + self.bits() as i64 } pub fn try_as_ptr(self) -> Option { - self.relocation.map(|alloc_id| { - Pointer::new(alloc_id, self.bits) + self.relocation().map(|alloc_id| { + Pointer::new(alloc_id, self.bits()) }) } pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { - match self.bits { + match self.bits() { 0 => Ok(false), 1 => Ok(true), _ => Err(EvalError::InvalidBool), From 67e1627a5543bb504b5783cfd4d214d183e4be28 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 15 Dec 2016 23:55:00 -0800 Subject: [PATCH 0702/1096] Rename PrimVal::Pointer to PrimVal::Ptr. Also fill out the PrimVal doc comments. --- src/cast.rs | 4 ++-- src/eval_context.rs | 28 ++++++++++++++-------------- src/terminator/intrinsic.rs | 4 ++-- src/terminator/mod.rs | 12 ++++++------ src/value.rs | 23 +++++++++++++---------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index 4412bd3d41884..f00dbcec499d9 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u64 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), - TyRawPtr(_) => Ok(PrimVal::Pointer(Pointer::from_int(v))), + TyRawPtr(_) => Ok(PrimVal::Ptr(Pointer::from_int(v))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } @@ -94,7 +94,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::TypeVariants::*; match ty.sty { TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => - Ok(PrimVal::Pointer(ptr)), + Ok(PrimVal::Ptr(ptr)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 7ed1a17e44f56..eca0684dcb95a 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::Pointer(ptr), PrimVal::from_uint(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_uint(s.len() as u64))) } pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -196,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(bs.len() as u64, 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - PrimVal::Pointer(ptr) + PrimVal::Ptr(ptr) } Struct(_) => unimplemented!(), @@ -563,12 +563,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); - let ptr = PrimVal::Pointer(raw_ptr); + let ptr = PrimVal::Ptr(raw_ptr); let val = match extra { LvalueExtra::None => Value::ByVal(ptr), LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_uint(len)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Pointer(vtable)), + LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -578,7 +578,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let ptr = self.alloc_ptr(ty)?; - self.write_primval(dest, PrimVal::Pointer(ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } Cast(kind, ref operand, cast_ty) => { @@ -617,7 +617,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(def_id, substs, fn_ty) => { let fn_ty = self.tcx.erase_regions(&fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); - self.write_value(Value::ByVal(PrimVal::Pointer(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), }, @@ -629,7 +629,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); - self.write_value(Value::ByVal(PrimVal::Pointer(fn_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), }, @@ -1150,23 +1150,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Pointer)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Ptr)?, ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut { ty, .. }) | ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { - PrimVal::Pointer(p) + PrimVal::Ptr(p) } else { trace!("reading fat pointer extra of type {}", ty); let extra = ptr.offset(self.memory.pointer_size()); let extra = match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => PrimVal::Pointer(self.memory.read_ptr(extra)?), + ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; - return Ok(Some(Value::ByValPair(PrimVal::Pointer(p), extra))); + return Ok(Some(Value::ByValPair(PrimVal::Ptr(p), extra))); } } @@ -1225,7 +1225,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; let len = PrimVal::from_uint(length as u64); - let ptr = PrimVal::Pointer(ptr); + let ptr = PrimVal::Ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { @@ -1239,8 +1239,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - let ptr = PrimVal::Pointer(ptr); - let extra = PrimVal::Pointer(vtable); + let ptr = PrimVal::Ptr(ptr); + let extra = PrimVal::Ptr(vtable); self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty)?; }, diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index de9c972ffd41b..732e4f555a9a5 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); let new_ptr = ptr.signed_offset(offset); - self.write_primval(dest, PrimVal::Pointer(new_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } "assume" => { @@ -292,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.signed_offset(offset * pointee_size); - self.write_primval(dest, PrimVal::Pointer(result_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; } "overflowing_sub" => { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c48a8a72c3d5c..06c006baeed72 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -336,7 +336,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[0], usize)?.to_u64(); let align = self.value_to_primval(args[1], usize)?.to_u64(); let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::Pointer(ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "__rust_deallocate" => { @@ -352,7 +352,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.value_to_primval(args[2], usize)?.to_u64(); let align = self.value_to_primval(args[3], usize)?.to_u64(); let new_ptr = self.memory.reallocate(ptr, size, align)?; - self.write_primval(dest, PrimVal::Pointer(new_ptr), dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } "memcmp" => { @@ -527,7 +527,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ptr }, }; - args[0].0 = Value::ByVal(PrimVal::Pointer(ptr)); + args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; - *first_arg = Value::ByVal(PrimVal::Pointer(self_ptr)); + *first_arg = Value::ByVal(PrimVal::Ptr(self_ptr)); let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; @@ -633,7 +633,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // run drop impl before the fields' drop impls if let Some(drop_def_id) = adt_def.destructor() { - drop.push((drop_def_id, Value::ByVal(PrimVal::Pointer(adt_ptr)), substs)); + drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs)); } let layout = self.type_layout(ty)?; let fields = match *layout { @@ -701,7 +701,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; let real_ty = sig.inputs()[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - drop.push((def_id, Value::ByVal(PrimVal::Pointer(ptr)), substs)); + drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs)); } else { // just a sanity check assert_eq!(drop_fn.offset, 0); diff --git a/src/value.rs b/src/value.rs index 7055548095d07..943f096c7ed82 100644 --- a/src/value.rs +++ b/src/value.rs @@ -43,19 +43,22 @@ pub enum Value { ByValPair(PrimVal, PrimVal), } -/// A `PrimVal` represents an immediate, primitive value existing outside of an allocation. It is -/// considered to be like a +/// A `PrimVal` represents an immediate, primitive value existing outside of a +/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in +/// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes +/// of a simple value, a pointer into another `Allocation`, or be undefined. #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimVal { + /// The raw bytes of a simple value. Bytes(u64), - // FIXME(solson): Rename this variant to Ptr. - // FIXME(solson): Outdated comment, pulled from `relocations` field I deleted. - /// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An - /// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only - /// large enough to contain one, hence the `Option`. - Pointer(Pointer), + /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of + /// relocations, but a `PrimVal` is only large enough to contain one, so we just represent the + /// relocation and its associated offset together as a `Pointer` here. + Ptr(Pointer), + /// An undefined `PrimVal`, for representing values that aren't safe to examine, but are safe + /// to copy around, just like undefined bytes in an `Allocation`. Undefined, } @@ -119,7 +122,7 @@ impl PrimVal { pub fn bits(&self) -> u64 { match *self { PrimVal::Bytes(b) => b, - PrimVal::Pointer(p) => p.offset, + PrimVal::Ptr(p) => p.offset, PrimVal::Undefined => panic!(".bits()() on PrimVal::Undefined"), } } @@ -127,7 +130,7 @@ impl PrimVal { // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't // stick around with this name. pub fn relocation(&self) -> Option { - if let PrimVal::Pointer(ref p) = *self { + if let PrimVal::Ptr(ref p) = *self { Some(p.alloc_id) } else { None From e615f671cefc4d2b934ddb0d151b55f311d793ec Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 00:06:03 -0800 Subject: [PATCH 0703/1096] Remove potentially wrong PartialEq from PrimVal. --- src/terminator/mod.rs | 2 +- src/value.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 06c006baeed72..72e64c6a6fd95 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (index, const_val) in values.iter().enumerate() { let val = self.const_to_value(const_val)?; let prim = self.value_to_primval(val, discr_ty)?; - if discr_prim == prim { + if discr_prim.bits() == prim.bits() { target_block = targets[index]; break; } diff --git a/src/value.rs b/src/value.rs index 943f096c7ed82..ae42cb9bfd320 100644 --- a/src/value.rs +++ b/src/value.rs @@ -47,7 +47,7 @@ pub enum Value { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u64), From 33f97feafbac2a3dc85f210d13a93af42f905f3f Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 00:07:02 -0800 Subject: [PATCH 0704/1096] Shorten PrimVal::Undefined to PrimVal::Undef. --- src/value.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/value.rs b/src/value.rs index ae42cb9bfd320..2a3c22353f7b4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -59,7 +59,7 @@ pub enum PrimVal { /// An undefined `PrimVal`, for representing values that aren't safe to examine, but are safe /// to copy around, just like undefined bytes in an `Allocation`. - Undefined, + Undef, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -123,7 +123,7 @@ impl PrimVal { match *self { PrimVal::Bytes(b) => b, PrimVal::Ptr(p) => p.offset, - PrimVal::Undefined => panic!(".bits()() on PrimVal::Undefined"), + PrimVal::Undef => panic!(".bits()() on PrimVal::Undef"), } } From f83c45e3671a47100275aa3aff65e6436a716000 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 22:01:01 -0800 Subject: [PATCH 0705/1096] Turn invalid panics into Results and rename fns. --- src/cast.rs | 6 +-- src/lvalue.rs | 2 +- src/operator.rs | 4 +- src/terminator/intrinsic.rs | 32 +++++++------- src/terminator/mod.rs | 44 +++++++++---------- src/value.rs | 84 +++++++++++++++++-------------------- 6 files changed, 82 insertions(+), 90 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index f00dbcec499d9..b50684283c840 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -17,14 +17,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use value::PrimValKind::*; match kind { - F32 => self.cast_float(val.to_f32() as f64, dest_ty), - F64 => self.cast_float(val.to_f64(), dest_ty), + F32 => self.cast_float(val.to_f32()? as f64, dest_ty), + F64 => self.cast_float(val.to_f64()?, dest_ty), I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits() as i64, dest_ty), Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits(), dest_ty, false), - FnPtr | Ptr => self.cast_ptr(val.to_ptr(), dest_ty), + FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty), } } diff --git a/src/lvalue.rs b/src/lvalue.rs index e88b65bbe47cb..1ff526654cafd 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -279,7 +279,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; - let n = self.value_to_primval(n_ptr, usize)?.to_u64(); + let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len); let ptr = base_ptr.offset(n * elem_size); (ptr, LvalueExtra::None) diff --git a/src/operator.rs b/src/operator.rs index 9e879eaf98077..4ef1ee86ce0ec 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -142,8 +142,8 @@ pub fn binary_op<'tcx>( // If the pointers are into the same allocation, fall through to the more general match // later, which will do comparisons on the `bits` fields, which are the pointer offsets // in this case. - let left_ptr = left.to_ptr(); - let right_ptr = right.to_ptr(); + let left_ptr = left.to_ptr()?; + let right_ptr = right.to_ptr()?; if left_ptr.alloc_id != right_ptr.alloc_id { return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 732e4f555a9a5..1ae6ade000972 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -45,14 +45,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let ptr = arg_vals[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64()?; let new_ptr = ptr.signed_offset(offset); self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } "assume" => { let bool = self.tcx.types.bool; - let cond = self.value_to_primval(arg_vals[0], bool)?.try_as_bool()?; + let cond = self.value_to_primval(arg_vals[0], bool)?.to_bool()?; if !cond { return Err(EvalError::AssumptionNotHeld); } } @@ -152,7 +152,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; - let count = self.value_to_primval(arg_vals[2], usize)?.to_u64(); + let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; self.memory.copy(src, dest, count * elem_size, elem_align)?; } @@ -180,12 +180,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_ty = self.tcx.mk_mut_ptr(ty); let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()), + Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()?), Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.to_ptr(), + ptr: ptr.to_ptr()?, extra: match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), _ => bug!("invalid fat pointer type: {}", ptr_ty), }, }, @@ -204,12 +204,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fabsf32" => { - let f = self.value_to_primval(arg_vals[2], f32)?.to_f32(); + let f = self.value_to_primval(arg_vals[2], f32)?.to_f32()?; self.write_primval(dest, PrimVal::from_f32(f.abs()), dest_ty)?; } "fabsf64" => { - let f = self.value_to_primval(arg_vals[2], f64)?.to_f64(); + let f = self.value_to_primval(arg_vals[2], f64)?.to_f64()?; self.write_primval(dest, PrimVal::from_f64(f.abs()), dest_ty)?; } @@ -288,7 +288,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_ty = substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64(); + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64()?; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.signed_offset(offset * pointee_size); @@ -308,24 +308,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "powif32" => { - let f = self.value_to_primval(arg_vals[0], f32)?.to_f32(); - let i = self.value_to_primval(arg_vals[1], i32)?.to_i64(); + let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i64()?; self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?; } "powif64" => { - let f = self.value_to_primval(arg_vals[0], f64)?.to_f64(); - let i = self.value_to_primval(arg_vals[1], i32)?.to_i64(); + let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i64()?; self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?; } "sqrtf32" => { - let f = self.value_to_primval(arg_vals[0], f32)?.to_f32(); + let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; self.write_primval(dest, PrimVal::from_f32(f.sqrt()), dest_ty)?; } "sqrtf64" => { - let f = self.value_to_primval(arg_vals[0], f64)?.to_f64(); + let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; self.write_primval(dest, PrimVal::from_f64(f.sqrt()), dest_ty)?; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 72e64c6a6fd95..7a67f56debf53 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -38,7 +38,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { - let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?; + let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; self.goto_block(if cond_val { then_target } else { else_target }); } @@ -88,7 +88,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr(); + let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); let bare_sig = self.tcx.erase_regions(&bare_sig); @@ -132,7 +132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Assert { ref cond, expected, ref msg, target, .. } => { - let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?; + let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; if expected == cond_val { self.goto_block(target); } else { @@ -141,10 +141,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let span = terminator.source_info.span; let len = self.eval_operand_to_primval(len) .expect("can't eval len") - .to_u64(); + .to_u64()?; let index = self.eval_operand_to_primval(index) .expect("can't eval index") - .to_u64(); + .to_u64()?; Err(EvalError::ArrayIndexOutOfBounds(span, len, index)) }, mir::AssertMessage::Math(ref err) => @@ -333,8 +333,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { - let size = self.value_to_primval(args[0], usize)?.to_u64(); - let align = self.value_to_primval(args[1], usize)?.to_u64(); + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; let ptr = self.memory.allocate(size, align)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } @@ -342,15 +342,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_deallocate" => { let ptr = args[0].read_ptr(&self.memory)?; // FIXME: insert sanity check for size and align? - let _old_size = self.value_to_primval(args[1], usize)?.to_u64(); - let _align = self.value_to_primval(args[2], usize)?.to_u64(); + let _old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let _align = self.value_to_primval(args[2], usize)?.to_u64()?; self.memory.deallocate(ptr)?; }, "__rust_reallocate" => { let ptr = args[0].read_ptr(&self.memory)?; - let size = self.value_to_primval(args[2], usize)?.to_u64(); - let align = self.value_to_primval(args[3], usize)?.to_u64(); + let size = self.value_to_primval(args[2], usize)?.to_u64()?; + let align = self.value_to_primval(args[3], usize)?.to_u64()?; let new_ptr = self.memory.reallocate(ptr, size, align)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } @@ -358,7 +358,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "memcmp" => { let left = args[0].read_ptr(&self.memory)?; let right = args[1].read_ptr(&self.memory)?; - let n = self.value_to_primval(args[2], usize)?.to_u64(); + let n = self.value_to_primval(args[2], usize)?.to_u64()?; let result = { let left_bytes = self.memory.read_bytes(left, n)?; @@ -377,13 +377,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "memchr" => { let ptr = args[0].read_ptr(&self.memory)?; - let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; - let num = self.value_to_primval(args[2], usize)?.to_u64(); + let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64); - self.write_value(Value::ByVal(PrimVal::from_ptr(new_ptr)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; } else { - self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; } } @@ -393,12 +393,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.memory.read_c_str(name_ptr)?; info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); } - self.write_value(Value::ByVal(PrimVal::new(0)), dest, dest_ty)?; + self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; } // unix panic code inside libstd will read the return value of this function "pthread_rwlock_rdlock" => { - self.write_primval(dest, PrimVal::new(0), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } link_name if link_name.starts_with("pthread_") => { @@ -594,14 +594,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), Value::ByVal(ptr) => { assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.to_ptr(); + let contents_ptr = ptr.to_ptr()?; self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; }, Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.to_ptr(); + let ptr = prim_ptr.to_ptr()?; let extra = match self.tcx.struct_tail(contents_ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.try_as_uint()?), + ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), _ => bug!("invalid fat pointer type: {}", ty), }; self.drop( diff --git a/src/value.rs b/src/value.rs index 2a3c22353f7b4..797723ecca65a 100644 --- a/src/value.rs +++ b/src/value.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.to_ptr()), + ByVal(ptr) | ByValPair(ptr, _) => ptr.to_ptr(), } } @@ -94,7 +94,7 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, vtable)) } - ByValPair(ptr, vtable) => Ok((ptr.to_ptr(), vtable.to_ptr())), + ByValPair(ptr, vtable) => Ok((ptr.to_ptr()?, vtable.to_ptr()?)), _ => bug!("expected ptr and vtable, got {:?}", self), } @@ -109,18 +109,18 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, len)) }, ByValPair(ptr, val) => { - Ok((ptr.to_ptr(), val.try_as_uint()?)) + Ok((ptr.to_ptr()?, val.to_u64()?)) }, _ => unimplemented!(), } } } -impl PrimVal { +impl<'tcx> PrimVal { // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't // stick around with this name. - pub fn bits(&self) -> u64 { - match *self { + pub fn bits(self) -> u64 { + match self { PrimVal::Bytes(b) => b, PrimVal::Ptr(p) => p.offset, PrimVal::Undef => panic!(".bits()() on PrimVal::Undef"), @@ -129,20 +129,20 @@ impl PrimVal { // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't // stick around with this name. - pub fn relocation(&self) -> Option { - if let PrimVal::Ptr(ref p) = *self { + pub fn relocation(self) -> Option { + if let PrimVal::Ptr(ref p) = self { Some(p.alloc_id) } else { None } } - pub fn from_bool(b: bool) -> Self { - PrimVal::Bytes(b as u64) + pub fn from_uint(n: u64) -> Self { + PrimVal::Bytes(n) } - pub fn from_char(c: char) -> Self { - PrimVal::Bytes(c as u64) + pub fn from_int(n: i64) -> Self { + PrimVal::Bytes(n as u64) } pub fn from_f32(f: f32) -> Self { @@ -153,56 +153,48 @@ impl PrimVal { PrimVal::Bytes(f64_to_bits(f)) } - pub fn from_uint(n: u64) -> Self { - PrimVal::Bytes(n) - } - - pub fn from_int(n: i64) -> Self { - PrimVal::Bytes(n as u64) + pub fn from_bool(b: bool) -> Self { + PrimVal::Bytes(b as u64) } - pub fn to_f32(self) -> f32 { - assert!(self.relocation().is_none()); - bits_to_f32(self.bits()) + pub fn from_char(c: char) -> Self { + PrimVal::Bytes(c as u64) } - pub fn to_f64(self) -> f64 { - assert!(self.relocation().is_none()); - bits_to_f64(self.bits()) + fn to_bytes(self) -> EvalResult<'tcx, u64> { + match self { + PrimVal::Bytes(b) => Ok(b), + PrimVal::Ptr(p) => p.to_int(), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } } - pub fn to_ptr(self) -> Pointer { - self.relocation().map(|alloc_id| { - Pointer::new(alloc_id, self.bits()) - }).unwrap_or_else(|| Pointer::from_int(self.bits())) + pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + match self { + PrimVal::Bytes(b) => Ok(Pointer::from_int(b)), + PrimVal::Ptr(p) => Ok(p), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } } - pub fn try_as_uint<'tcx>(self) -> EvalResult<'tcx, u64> { - self.to_ptr().to_int() + pub fn to_u64(self) -> EvalResult<'tcx, u64> { + self.to_bytes() } - pub fn to_u64(self) -> u64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as u64; - } - self.bits() + pub fn to_i64(self) -> EvalResult<'tcx, i64> { + self.to_bytes().map(|b| b as i64) } - pub fn to_i64(self) -> i64 { - if let Some(ptr) = self.try_as_ptr() { - return ptr.to_int().expect("non abstract ptr") as i64; - } - self.bits() as i64 + pub fn to_f32(self) -> EvalResult<'tcx, f32> { + self.to_bytes().map(bits_to_f32) } - pub fn try_as_ptr(self) -> Option { - self.relocation().map(|alloc_id| { - Pointer::new(alloc_id, self.bits()) - }) + pub fn to_f64(self) -> EvalResult<'tcx, f64> { + self.to_bytes().map(bits_to_f64) } - pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> { - match self.bits() { + pub fn to_bool(self) -> EvalResult<'tcx, bool> { + match self.to_bytes()? { 0 => Ok(false), 1 => Ok(true), _ => Err(EvalError::InvalidBool), From 3bad50e11472339d3477c605adb90034c476c8b8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 22:11:44 -0800 Subject: [PATCH 0706/1096] Rename PrimVal::from_{u,}int to from_{u,i}64. --- src/eval_context.rs | 20 ++++++++++---------- src/terminator/intrinsic.rs | 10 +++++----- src/value.rs | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index eca0684dcb95a..7108286193fe1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_uint(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u64(s.len() as u64))) } pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -465,7 +465,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - self.write_primval(dest, PrimVal::from_int(0), dest_ty)?; + self.write_primval(dest, PrimVal::from_i64(0), dest_ty)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -557,7 +557,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); - self.write_primval(dest, PrimVal::from_uint(len), dest_ty)?; + self.write_primval(dest, PrimVal::from_u64(len), dest_ty)?; } Ref(_, _, ref lvalue) => { @@ -567,7 +567,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = match extra { LvalueExtra::None => Value::ByVal(ptr), - LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_uint(len)), + LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u64(len)), LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -1132,7 +1132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I64 => 8, Is => self.memory.pointer_size(), }; - PrimVal::from_int(self.memory.read_int(ptr, size)?) + PrimVal::from_i64(self.memory.read_int(ptr, size)?) } ty::TyUint(uint_ty) => { @@ -1144,7 +1144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64 => 8, Us => self.memory.pointer_size(), }; - PrimVal::from_uint(self.memory.read_uint(ptr, size)?) + PrimVal::from_u64(self.memory.read_uint(ptr, size)?) } ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), @@ -1163,7 +1163,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let extra = match self.tcx.struct_tail(ty).sty { ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => PrimVal::from_uint(self.memory.read_usize(extra)?), + ty::TyStr => PrimVal::from_u64(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), }; return Ok(Some(Value::ByValPair(PrimVal::Ptr(p), extra))); @@ -1175,9 +1175,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes(); if signed { - PrimVal::from_int(self.memory.read_int(ptr, size)?) + PrimVal::from_i64(self.memory.read_int(ptr, size)?) } else { - PrimVal::from_uint(self.memory.read_uint(ptr, size)?) + PrimVal::from_u64(self.memory.read_uint(ptr, size)?) } } else { return Ok(None); @@ -1224,7 +1224,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - let len = PrimVal::from_uint(length as u64); + let len = PrimVal::from_u64(length as u64); let ptr = PrimVal::Ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 1ae6ade000972..af99d47dda775 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -259,7 +259,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "min_align_of" => { let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty)?; - let align_val = PrimVal::from_uint(elem_align as u64); + let align_val = PrimVal::from_u64(elem_align as u64); self.write_primval(dest, align_val, dest_ty)?; } @@ -267,7 +267,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); - let align_val = PrimVal::from_uint(align); + let align_val = PrimVal::from_u64(align); self.write_primval(dest, align_val, dest_ty)?; } @@ -336,20 +336,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // .expect("size_of intrinsic called on unsized value") // see https://github.com/rust-lang/rust/pull/37708 let size = self.type_size(ty)?.unwrap_or(!0) as u64; - self.write_primval(dest, PrimVal::from_uint(size), dest_ty)?; + self.write_primval(dest, PrimVal::from_u64(size), dest_ty)?; } "size_of_val" => { let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_uint(size), dest_ty)?; + self.write_primval(dest, PrimVal::from_u64(size), dest_ty)?; } "min_align_of_val" | "align_of_val" => { let ty = substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_uint(align), dest_ty)?; + self.write_primval(dest, PrimVal::from_u64(align), dest_ty)?; } "type_name" => { diff --git a/src/value.rs b/src/value.rs index 797723ecca65a..c7c81e5c947d6 100644 --- a/src/value.rs +++ b/src/value.rs @@ -137,11 +137,11 @@ impl<'tcx> PrimVal { } } - pub fn from_uint(n: u64) -> Self { + pub fn from_u64(n: u64) -> Self { PrimVal::Bytes(n) } - pub fn from_int(n: i64) -> Self { + pub fn from_i64(n: i64) -> Self { PrimVal::Bytes(n as u64) } From 33223fdd76e462456d6364da493b48b968036e6e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 23:43:58 -0800 Subject: [PATCH 0707/1096] Allow compiletest to see symlinked targets. --- tests/compiletest.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f7bf16926ba7c..ff96cf3c6801d 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -38,15 +38,8 @@ fn miri_pass(path: &str, target: &str) { fn for_all_targets(sysroot: &str, mut f: F) { for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { - let target = target.unwrap(); - if !target.metadata().unwrap().is_dir() { - continue; - } - let target = target.file_name().into_string().unwrap(); - match &*target { - "etc" | "src" => continue, - _ => {}, - } + let target = target.unwrap().file_name().into_string().unwrap(); + if !target.contains("-") { continue; } let stderr = std::io::stderr(); writeln!(stderr.lock(), "running tests for target {}", target).unwrap(); f(target); From 0591683b737b01d28e80840095a052897d33c2d2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 23:47:43 -0800 Subject: [PATCH 0708/1096] Stop before trans so I can test non-x86_64 targets. I had problems when it tried to link outputs for targets other than my host. This re-breaks tests with auxiliary builds. I'm not sure what to do about those right now. --- src/bin/miri.rs | 3 ++- tests/run-pass/aux_test.rs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 9e1d6235d4c18..24a0b7ba00b0a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -10,7 +10,7 @@ extern crate syntax; #[macro_use] extern crate log; use rustc::session::Session; -use rustc_driver::CompilerCalls; +use rustc_driver::{Compilation, CompilerCalls}; use rustc_driver::driver::{CompileState, CompileController}; use syntax::ast::{MetaItemKind, NestedMetaItemKind}; @@ -21,6 +21,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut control = CompileController::basic(); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); + control.after_analysis.stop = Compilation::Stop; control } } diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index aa471f6cf8fdf..5510582e87a3f 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,7 +1,9 @@ // aux-build:dep.rs -extern crate dep; +// FIXME: Auxiliary builds are currently broken. +// extern crate dep; fn main() { - dep::foo(); + // FIXME: Auxiliary builds are currently broken. + // dep::foo(); } From 42239e69bf43ee7dbda05cf757f86f10a0f16272 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 16 Dec 2016 23:57:46 -0800 Subject: [PATCH 0709/1096] Make layout SizeOverflow test trigger on i686. --- tests/compile-fail/repeat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-fail/repeat.rs b/tests/compile-fail/repeat.rs index 70d26a6859220..abe89e233e7cd 100644 --- a/tests/compile-fail/repeat.rs +++ b/tests/compile-fail/repeat.rs @@ -1,5 +1,5 @@ fn main() { - let data: [u8; std::isize::MAX as usize] = [42; std::isize::MAX as usize]; + let data: [u8; std::usize::MAX] = [42; std::usize::MAX]; //~^ ERROR: rustc layout computation failed: SizeOverflow([u8; assert_eq!(data.len(), 1024); } From 96b83ebb7cc2ac8d7734f2dfaeb4e93e8666bea2 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 00:54:37 -0800 Subject: [PATCH 0710/1096] Improve compiletest target detection. --- tests/compiletest.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ff96cf3c6801d..50970086ee541 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -36,10 +36,17 @@ fn miri_pass(path: &str, target: &str) { compiletest::run_tests(&config); } +fn is_target_dir>(path: P) -> bool { + let mut path = path.into(); + path.push("lib"); + path.metadata().map(|m| m.is_dir()).unwrap_or(false) +} + fn for_all_targets(sysroot: &str, mut f: F) { - for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { - let target = target.unwrap().file_name().into_string().unwrap(); - if !target.contains("-") { continue; } + for entry in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { + let entry = entry.unwrap(); + if !is_target_dir(entry.path()) { continue; } + let target = entry.file_name().into_string().unwrap(); let stderr = std::io::stderr(); writeln!(stderr.lock(), "running tests for target {}", target).unwrap(); f(target); From 142d971c821717f37e2e45abbc556d1059745145 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 01:34:19 -0800 Subject: [PATCH 0711/1096] Add regression test for write_primval bug. --- .../too-large-primval-write-problem.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/run-pass/too-large-primval-write-problem.rs diff --git a/tests/run-pass/too-large-primval-write-problem.rs b/tests/run-pass/too-large-primval-write-problem.rs new file mode 100644 index 0000000000000..1bbe45277c43f --- /dev/null +++ b/tests/run-pass/too-large-primval-write-problem.rs @@ -0,0 +1,23 @@ +// PrimVals in Miri are represented with 8 bytes (u64) and at the time of writing, the `-x` +// will sign extend into the entire 8 bytes. Then, if you tried to write the `-x` into +// something smaller than 8 bytes, like a 4 byte pointer, it would crash in byteorder crate +// code that assumed only the low 4 bytes would be set. Actually, we were masking properly for +// everything except pointers before I fixed it, so this was probably impossible to reproduce on +// 64-bit. +// +// This is just intended as a regression test to make sure we don't reintroduce this problem. + +#[cfg(target_pointer_width = "32")] +fn main() { + use std::mem::transmute; + + // Make the weird PrimVal. + let x = 1i32; + let bad = unsafe { transmute::(-x) }; + + // Force it through the Memory::write_primval code. + Box::new(bad); +} + +#[cfg(not(target_pointer_width = "32"))] +fn main() {} From 29e690fe14bf6354c07c793c3af53d5242ec202d Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 01:36:02 -0800 Subject: [PATCH 0712/1096] Handle writing undefined PrimVals and mask properly. --- src/memory.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index ac457966b31ab..90fbf04c73d41 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -587,21 +587,36 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { val: PrimVal, kind: PrimValKind, ) -> EvalResult<'tcx, ()> { - if let Some(alloc_id) = val.relocation() { - return self.write_ptr(dest, Pointer::new(alloc_id, val.bits())); - } - use value::PrimValKind::*; - let (size, bits) = match kind { - I8 | U8 | Bool => (1, val.bits() as u8 as u64), - I16 | U16 => (2, val.bits() as u16 as u64), - I32 | U32 | F32 | Char => (4, val.bits() as u32 as u64), - I64 | U64 | F64 => (8, val.bits()), - // int -> ptr transmutes are handled here - FnPtr | Ptr => return self.write_usize(dest, val.bits()), + let size = match kind { + I8 | U8 | Bool => 1, + I16 | U16 => 2, + I32 | U32 | F32 | Char => 4, + I64 | U64 | F64 => 8, + Ptr | FnPtr => self.pointer_size(), }; - self.write_uint(dest, bits, size) + match val { + PrimVal::Ptr(ptr) => { + assert_eq!(size, self.pointer_size()); + self.write_ptr(dest, ptr) + } + + PrimVal::Bytes(bytes) => { + // We need to mask here, or the byteorder crate can die when given a u64 larger + // than fits in an integer of the requested size. + let mask = match size { + 1 => 0xff, + 2 => 0xffff, + 4 => 0xffffffff, + 8 => 0xffffffffffffffff, + _ => bug!("unexpected PrimVal size"), + }; + self.write_uint(dest, bytes & mask, size) + } + + PrimVal::Undef => self.mark_definedness(dest, size, false), + } } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { From d63ab5f8c313a5868d245f9cf1969fc991a301f6 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 01:47:24 -0800 Subject: [PATCH 0713/1096] Refactor PrimVal::relocation out of existence. --- src/eval_context.rs | 20 ++++++++++---------- src/value.rs | 12 +----------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7108286193fe1..c1774099b8c51 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -315,15 +315,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("global should have been cached (freeze)"); match global_value.data.expect("global should have been initialized") { Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?, - Value::ByVal(val) => if let Some(alloc_id) = val.relocation() { - self.memory.freeze(alloc_id)?; + Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { + self.memory.freeze(ptr.alloc_id)?; }, - Value::ByValPair(a, b) => { - if let Some(alloc_id) = a.relocation() { - self.memory.freeze(alloc_id)?; + Value::ByValPair(val1, val2) => { + if let PrimVal::Ptr(ptr) = val1 { + self.memory.freeze(ptr.alloc_id)?; } - if let Some(alloc_id) = b.relocation() { - self.memory.freeze(alloc_id)?; + if let PrimVal::Ptr(ptr) = val2 { + self.memory.freeze(ptr.alloc_id)?; } }, } @@ -1301,12 +1301,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Value::ByVal(val) => { trace!("frame[{}] {:?}: {:?}", frame, local, val); - if let Some(alloc_id) = val.relocation() { allocs.push(alloc_id); } + if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } } Value::ByValPair(val1, val2) => { trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); - if let Some(alloc_id) = val1.relocation() { allocs.push(alloc_id); } - if let Some(alloc_id) = val2.relocation() { allocs.push(alloc_id); } + if let PrimVal::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } + if let PrimVal::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } } } } diff --git a/src/value.rs b/src/value.rs index c7c81e5c947d6..a4b4f18ee2faa 100644 --- a/src/value.rs +++ b/src/value.rs @@ -4,7 +4,7 @@ use std::mem::transmute; use error::{EvalError, EvalResult}; -use memory::{AllocId, Memory, Pointer}; +use memory::{Memory, Pointer}; pub(super) fn bits_to_f32(bits: u64) -> f32 { unsafe { transmute::(bits as u32) } @@ -127,16 +127,6 @@ impl<'tcx> PrimVal { } } - // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't - // stick around with this name. - pub fn relocation(self) -> Option { - if let PrimVal::Ptr(ref p) = self { - Some(p.alloc_id) - } else { - None - } - } - pub fn from_u64(n: u64) -> Self { PrimVal::Bytes(n) } From 0cc4535a587c47d824e9ce0b905eb2ae9e548f90 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 01:52:26 -0800 Subject: [PATCH 0714/1096] This test appears to work on 32-bit now. --- tests/run-pass/sums.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/run-pass/sums.rs b/tests/run-pass/sums.rs index f067b29220ce8..a8dfd5ed66ae7 100644 --- a/tests/run-pass/sums.rs +++ b/tests/run-pass/sums.rs @@ -47,8 +47,6 @@ fn two_nones() -> (Option, Option) { (None, None) } -// FIXME(solson): Casts inside PartialEq fails on 32-bit. -#[cfg(target_pointer_width = "64")] fn main() { assert_eq!(two_nones(), (None, None)); assert_eq!(match_opt_some(), 13); @@ -59,6 +57,3 @@ fn main() { assert_eq!(return_true(), MyBool::True(())); assert_eq!(return_unit(), Unit::Unit(())); } - -#[cfg(not(target_pointer_width = "64"))] -fn main() {} From 9e244251a0c05ba5c06fd94970b01d11cfbb73f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 01:58:03 -0800 Subject: [PATCH 0715/1096] Enable an old test that works now! --- tests/run-pass/std.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/run-pass/std.rs b/tests/run-pass/std.rs index 2e65550b07bf8..e0e23812d275e 100644 --- a/tests/run-pass/std.rs +++ b/tests/run-pass/std.rs @@ -1,4 +1,4 @@ -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::sync::Arc; @@ -9,14 +9,12 @@ fn rc_cell() -> Rc> { r } -// TODO(solson): also requires destructors to run for the second borrow to work -// TODO(solson): needs StructWrappedNullablePointer support -// fn rc_refcell() -> i32 { -// let r = Rc::new(RefCell::new(42)); -// *r.borrow_mut() += 10; -// let x = *r.borrow(); -// x -// } +fn rc_refcell() -> i32 { + let r = Rc::new(RefCell::new(42)); + *r.borrow_mut() += 10; + let x = *r.borrow(); + x +} fn arc() -> Arc { let a = Arc::new(42); @@ -30,5 +28,6 @@ fn true_assert() { fn main() { assert_eq!(*arc(), 42); assert_eq!(rc_cell().get(), 84); + assert_eq!(rc_refcell(), 52); true_assert(); } From 4fe41ad8d52b4add5a185c1a309b03186dc013fb Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 03:09:57 -0800 Subject: [PATCH 0716/1096] Refactor PrimVal::bits() out of existence. --- src/cast.rs | 4 +- src/eval_context.rs | 6 +-- src/memory.rs | 3 +- src/operator.rs | 80 ++++++++++++++++++++++++------------- src/terminator/intrinsic.rs | 55 ++++++++++++++----------- src/terminator/mod.rs | 2 +- src/value.rs | 12 +----- 7 files changed, 91 insertions(+), 71 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index b50684283c840..2b3194a59940c 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -20,9 +20,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { F32 => self.cast_float(val.to_f32()? as f64, dest_ty), F64 => self.cast_float(val.to_f64()?, dest_ty), - I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits() as i64, dest_ty), + I8 | I16 | I32 | I64 => self.cast_signed_int(val.to_i64()?, dest_ty), - Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits(), dest_ty, false), + Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.to_u64()?, dest_ty, false), FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty), } diff --git a/src/eval_context.rs b/src/eval_context.rs index c1774099b8c51..7696a70f379a1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1093,10 +1093,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { match ty.sty { - ty::TyBool if val.bits() > 1 => Err(EvalError::InvalidBool), + ty::TyBool if val.to_bytes()? > 1 => Err(EvalError::InvalidBool), - ty::TyChar if ::std::char::from_u32(val.bits() as u32).is_none() - => Err(EvalError::InvalidChar(val.bits() as u32 as u64)), + ty::TyChar if ::std::char::from_u32(val.to_bytes()? as u32).is_none() + => Err(EvalError::InvalidChar(val.to_bytes()? as u32 as u64)), _ => Ok(()), } diff --git a/src/memory.rs b/src/memory.rs index 90fbf04c73d41..d65bf7f145007 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -77,8 +77,7 @@ impl Pointer { pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, u64> { match self.alloc_id { - NEVER_ALLOC_ID | - ZST_ALLOC_ID => Ok(self.offset), + NEVER_ALLOC_ID => Ok(self.offset), _ => Err(EvalError::ReadPointerAsBytes), } } diff --git a/src/operator.rs b/src/operator.rs index 4ef1ee86ce0ec..1cf929b886684 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -139,16 +139,38 @@ pub fn binary_op<'tcx>( use rustc::mir::BinOp::*; use value::PrimValKind::*; - // If the pointers are into the same allocation, fall through to the more general match - // later, which will do comparisons on the `bits` fields, which are the pointer offsets - // in this case. - let left_ptr = left.to_ptr()?; - let right_ptr = right.to_ptr()?; - if left_ptr.alloc_id != right_ptr.alloc_id { - return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); + // FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store + // plain bytes, and leave that to PrimVal::Bytes. + fn normalize(val: PrimVal) -> PrimVal { + if let PrimVal::Ptr(ptr) = val { + if let Ok(bytes) = ptr.to_int() { + return PrimVal::Bytes(bytes); + } + } + val } + let (left, right) = (normalize(left), normalize(right)); + + let (l, r) = match (left, right) { + (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), + + (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { + if left_ptr.alloc_id == right_ptr.alloc_id { + // If the pointers are into the same allocation, fall through to the more general + // match later, which will do comparisons on the pointer offsets. + (left_ptr.offset, right_ptr.offset) + } else { + return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); + } + } + + (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) | + (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { + return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes))?, false)); + } - let (l, r) = (left.bits(), right.bits()); + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), + }; // These ops can have an RHS with a different numeric type. if bin_op == Shl || bin_op == Shr { @@ -165,11 +187,11 @@ pub fn binary_op<'tcx>( // Cast to `u32` because `overflowing_sh{l,r}` only take `u32`, then apply the bitmask // to ensure it's within the valid shift value range. - let r = (right.bits() as u32) & (type_bits - 1); + let masked_shift_width = (r as u32) & (type_bits - 1); return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r), - Shr => int_shift!(left_kind, overflowing_shr, l, r), + Shl => int_shift!(left_kind, overflowing_shl, l, masked_shift_width), + Shr => int_shift!(left_kind, overflowing_shr, l, masked_shift_width), _ => bug!("it has already been checked that this is a shift op"), }; } @@ -253,26 +275,28 @@ pub fn unary_op<'tcx>( use rustc::mir::UnOp::*; use value::PrimValKind::*; - let bits = match (un_op, val_kind) { - (Not, Bool) => !bits_to_bool(val.bits()) as u64, + let bytes = val.to_bytes()?; + + let result_bytes = match (un_op, val_kind) { + (Not, Bool) => !bits_to_bool(bytes) as u64, - (Not, U8) => !(val.bits() as u8) as u64, - (Not, U16) => !(val.bits() as u16) as u64, - (Not, U32) => !(val.bits() as u32) as u64, - (Not, U64) => !val.bits(), + (Not, U8) => !(bytes as u8) as u64, + (Not, U16) => !(bytes as u16) as u64, + (Not, U32) => !(bytes as u32) as u64, + (Not, U64) => !bytes, - (Not, I8) => !(val.bits() as i8) as u64, - (Not, I16) => !(val.bits() as i16) as u64, - (Not, I32) => !(val.bits() as i32) as u64, - (Not, I64) => !(val.bits() as i64) as u64, + (Not, I8) => !(bytes as i8) as u64, + (Not, I16) => !(bytes as i16) as u64, + (Not, I32) => !(bytes as i32) as u64, + (Not, I64) => !(bytes as i64) as u64, - (Neg, I8) => -(val.bits() as i8) as u64, - (Neg, I16) => -(val.bits() as i16) as u64, - (Neg, I32) => -(val.bits() as i32) as u64, - (Neg, I64) => -(val.bits() as i64) as u64, + (Neg, I8) => -(bytes as i8) as u64, + (Neg, I16) => -(bytes as i16) as u64, + (Neg, I32) => -(bytes as i32) as u64, + (Neg, I64) => -(bytes as i64) as u64, - (Neg, F32) => f32_to_bits(-bits_to_f32(val.bits())), - (Neg, F64) => f64_to_bits(-bits_to_f64(val.bits())), + (Neg, F32) => f32_to_bits(-bits_to_f32(bytes)), + (Neg, F64) => f64_to_bits(-bits_to_f64(bytes)), _ => { let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); @@ -280,5 +304,5 @@ pub fn unary_op<'tcx>( } }; - Ok(PrimVal::Bytes(bits)) + Ok(PrimVal::Bytes(result_bytes)) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index af99d47dda775..a25d10d05aeae 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -163,7 +163,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let num = self.value_to_primval(arg_vals[0], ty)?; let kind = self.ty_to_primval_kind(ty)?; - let num = numeric_intrinsic(intrinsic_name, num, kind); + let num = numeric_intrinsic(intrinsic_name, num, kind)?; self.write_primval(dest, num, ty)?; } @@ -501,33 +501,40 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } -macro_rules! integer_intrinsic { - ($name:expr, $val:expr, $kind:expr, $method:ident) => ({ - let val = $val; - - use value::PrimValKind::*; - let bits = match $kind { - I8 => (val.bits() as i8).$method() as u64, - U8 => (val.bits() as u8).$method() as u64, - I16 => (val.bits() as i16).$method() as u64, - U16 => (val.bits() as u16).$method() as u64, - I32 => (val.bits() as i32).$method() as u64, - U32 => (val.bits() as u32).$method() as u64, - I64 => (val.bits() as i64).$method() as u64, - U64 => (val.bits() as u64).$method() as u64, - _ => bug!("invalid `{}` argument: {:?}", $name, val), - }; - - PrimVal::Bytes(bits) - }); -} +fn numeric_intrinsic<'tcx>( + name: &str, + val: PrimVal, + kind: PrimValKind +) -> EvalResult<'tcx, PrimVal> { + macro_rules! integer_intrinsic { + ($name:expr, $val:expr, $kind:expr, $method:ident) => ({ + let val = $val; + let bytes = val.to_bytes()?; + + use value::PrimValKind::*; + let result_bytes = match $kind { + I8 => (bytes as i8).$method() as u64, + U8 => (bytes as u8).$method() as u64, + I16 => (bytes as i16).$method() as u64, + U16 => (bytes as u16).$method() as u64, + I32 => (bytes as i32).$method() as u64, + U32 => (bytes as u32).$method() as u64, + I64 => (bytes as i64).$method() as u64, + U64 => bytes.$method() as u64, + _ => bug!("invalid `{}` argument: {:?}", $name, val), + }; + + PrimVal::Bytes(result_bytes) + }); + } -fn numeric_intrinsic(name: &str, val: PrimVal, kind: PrimValKind) -> PrimVal { - match name { + let result_val = match name { "bswap" => integer_intrinsic!("bswap", val, kind, swap_bytes), "ctlz" => integer_intrinsic!("ctlz", val, kind, leading_zeros), "ctpop" => integer_intrinsic!("ctpop", val, kind, count_ones), "cttz" => integer_intrinsic!("cttz", val, kind, trailing_zeros), _ => bug!("not a numeric intrinsic: {}", name), - } + }; + + Ok(result_val) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 7a67f56debf53..b4cad215b77aa 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for (index, const_val) in values.iter().enumerate() { let val = self.const_to_value(const_val)?; let prim = self.value_to_primval(val, discr_ty)?; - if discr_prim.bits() == prim.bits() { + if discr_prim.to_bytes()? == prim.to_bytes()? { target_block = targets[index]; break; } diff --git a/src/value.rs b/src/value.rs index a4b4f18ee2faa..05acb68436dc0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -117,16 +117,6 @@ impl<'a, 'tcx: 'a> Value { } impl<'tcx> PrimVal { - // FIXME(solson): Remove this. It's a temporary function to aid refactoring, but it shouldn't - // stick around with this name. - pub fn bits(self) -> u64 { - match self { - PrimVal::Bytes(b) => b, - PrimVal::Ptr(p) => p.offset, - PrimVal::Undef => panic!(".bits()() on PrimVal::Undef"), - } - } - pub fn from_u64(n: u64) -> Self { PrimVal::Bytes(n) } @@ -151,7 +141,7 @@ impl<'tcx> PrimVal { PrimVal::Bytes(c as u64) } - fn to_bytes(self) -> EvalResult<'tcx, u64> { + pub fn to_bytes(self) -> EvalResult<'tcx, u64> { match self { PrimVal::Bytes(b) => Ok(b), PrimVal::Ptr(p) => p.to_int(), From 6b7c68bec27bd4b50882ce32ea27d9f1901b452b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 03:19:24 -0800 Subject: [PATCH 0717/1096] Rename bits to bytes for consistency. --- src/operator.rs | 56 ++++++++++++++++++++++++------------------------- src/value.rs | 24 ++++++++++----------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index 1cf929b886684..2e1ac075294b4 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -9,11 +9,11 @@ use value::{ PrimVal, PrimValKind, Value, - bits_to_f32, - bits_to_f64, - f32_to_bits, - f64_to_bits, - bits_to_bool, + bytes_to_f32, + bytes_to_f64, + f32_to_bytes, + f64_to_bytes, + bytes_to_bool, }; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -108,23 +108,23 @@ macro_rules! int_shift { } macro_rules! float_arithmetic { - ($from_bits:ident, $to_bits:ident, $float_op:tt, $l:expr, $r:expr) => ({ - let l = $from_bits($l); - let r = $from_bits($r); - let bits = $to_bits(l $float_op r); - PrimVal::Bytes(bits) + ($from_bytes:ident, $to_bytes:ident, $float_op:tt, $l:expr, $r:expr) => ({ + let l = $from_bytes($l); + let r = $from_bytes($r); + let bytes = $to_bytes(l $float_op r); + PrimVal::Bytes(bytes) }) } macro_rules! f32_arithmetic { ($float_op:tt, $l:expr, $r:expr) => ( - float_arithmetic!(bits_to_f32, f32_to_bits, $float_op, $l, $r) + float_arithmetic!(bytes_to_f32, f32_to_bytes, $float_op, $l, $r) ) } macro_rules! f64_arithmetic { ($float_op:tt, $l:expr, $r:expr) => ( - float_arithmetic!(bits_to_f64, f64_to_bits, $float_op, $l, $r) + float_arithmetic!(bytes_to_f64, f64_to_bytes, $float_op, $l, $r) ) } @@ -202,19 +202,19 @@ pub fn binary_op<'tcx>( } let val = match (bin_op, left_kind) { - (Eq, F32) => PrimVal::from_bool(bits_to_f32(l) == bits_to_f32(r)), - (Ne, F32) => PrimVal::from_bool(bits_to_f32(l) != bits_to_f32(r)), - (Lt, F32) => PrimVal::from_bool(bits_to_f32(l) < bits_to_f32(r)), - (Le, F32) => PrimVal::from_bool(bits_to_f32(l) <= bits_to_f32(r)), - (Gt, F32) => PrimVal::from_bool(bits_to_f32(l) > bits_to_f32(r)), - (Ge, F32) => PrimVal::from_bool(bits_to_f32(l) >= bits_to_f32(r)), - - (Eq, F64) => PrimVal::from_bool(bits_to_f64(l) == bits_to_f64(r)), - (Ne, F64) => PrimVal::from_bool(bits_to_f64(l) != bits_to_f64(r)), - (Lt, F64) => PrimVal::from_bool(bits_to_f64(l) < bits_to_f64(r)), - (Le, F64) => PrimVal::from_bool(bits_to_f64(l) <= bits_to_f64(r)), - (Gt, F64) => PrimVal::from_bool(bits_to_f64(l) > bits_to_f64(r)), - (Ge, F64) => PrimVal::from_bool(bits_to_f64(l) >= bits_to_f64(r)), + (Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)), + (Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)), + (Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)), + (Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)), + (Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)), + (Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)), + + (Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)), + (Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)), + (Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)), + (Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)), + (Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)), + (Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)), (Add, F32) => f32_arithmetic!(+, l, r), (Sub, F32) => f32_arithmetic!(-, l, r), @@ -278,7 +278,7 @@ pub fn unary_op<'tcx>( let bytes = val.to_bytes()?; let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !bits_to_bool(bytes) as u64, + (Not, Bool) => !bytes_to_bool(bytes) as u64, (Not, U8) => !(bytes as u8) as u64, (Not, U16) => !(bytes as u16) as u64, @@ -295,8 +295,8 @@ pub fn unary_op<'tcx>( (Neg, I32) => -(bytes as i32) as u64, (Neg, I64) => -(bytes as i64) as u64, - (Neg, F32) => f32_to_bits(-bits_to_f32(bytes)), - (Neg, F64) => f64_to_bits(-bits_to_f64(bytes)), + (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)), + (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)), _ => { let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); diff --git a/src/value.rs b/src/value.rs index 05acb68436dc0..ddffdd01ee8cd 100644 --- a/src/value.rs +++ b/src/value.rs @@ -6,25 +6,25 @@ use std::mem::transmute; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; -pub(super) fn bits_to_f32(bits: u64) -> f32 { - unsafe { transmute::(bits as u32) } +pub(super) fn bytes_to_f32(bytes: u64) -> f32 { + unsafe { transmute::(bytes as u32) } } -pub(super) fn bits_to_f64(bits: u64) -> f64 { - unsafe { transmute::(bits) } +pub(super) fn bytes_to_f64(bytes: u64) -> f64 { + unsafe { transmute::(bytes) } } -pub(super) fn f32_to_bits(f: f32) -> u64 { +pub(super) fn f32_to_bytes(f: f32) -> u64 { unsafe { transmute::(f) as u64 } } -pub(super) fn f64_to_bits(f: f64) -> u64 { +pub(super) fn f64_to_bytes(f: f64) -> u64 { unsafe { transmute::(f) } } -pub(super) fn bits_to_bool(n: u64) -> bool { +pub(super) fn bytes_to_bool(n: u64) -> bool { // FIXME(solson): Can we reach here due to user error? - debug_assert!(n == 0 || n == 1, "bits interpreted as bool were {}", n); + debug_assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n); n & 1 == 1 } @@ -126,11 +126,11 @@ impl<'tcx> PrimVal { } pub fn from_f32(f: f32) -> Self { - PrimVal::Bytes(f32_to_bits(f)) + PrimVal::Bytes(f32_to_bytes(f)) } pub fn from_f64(f: f64) -> Self { - PrimVal::Bytes(f64_to_bits(f)) + PrimVal::Bytes(f64_to_bytes(f)) } pub fn from_bool(b: bool) -> Self { @@ -166,11 +166,11 @@ impl<'tcx> PrimVal { } pub fn to_f32(self) -> EvalResult<'tcx, f32> { - self.to_bytes().map(bits_to_f32) + self.to_bytes().map(bytes_to_f32) } pub fn to_f64(self) -> EvalResult<'tcx, f64> { - self.to_bytes().map(bits_to_f64) + self.to_bytes().map(bytes_to_f64) } pub fn to_bool(self) -> EvalResult<'tcx, bool> { From 459a27d6bd80a2170492e14b19f0b3d3238c9038 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 17 Dec 2016 03:36:22 -0800 Subject: [PATCH 0718/1096] Reading undef local/globals gets PrimVal::Undef. This fixes #95. --- src/eval_context.rs | 4 ++-- src/lvalue.rs | 27 +++++++++++++++------------ src/terminator/mod.rs | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7696a70f379a1..0ae95d1b05aa1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -774,7 +774,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, promoted: None, }; - self.read_lvalue(Lvalue::Global(cid))? + self.read_lvalue(Lvalue::Global(cid)) } } @@ -784,7 +784,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), promoted: Some(index), }; - self.read_lvalue(Lvalue::Global(cid))? + self.read_lvalue(Lvalue::Global(cid)) } }; diff --git a/src/lvalue.rs b/src/lvalue.rs index 1ff526654cafd..f06cffeee89b5 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,12 +1,13 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::{self, Ty}; use rustc::ty::subst::Substs; +use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; -use error::{EvalError, EvalResult}; +use error::EvalResult; +use eval_context::{EvalContext}; use memory::Pointer; -use eval_context::{EvalContext, Value}; +use value::{PrimVal, Value}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Lvalue<'tcx> { @@ -117,23 +118,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } let lvalue = self.eval_lvalue(lvalue)?; - self.read_lvalue(lvalue) + Ok(self.read_lvalue(lvalue)) } - pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> Value { match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) + Value::ByRef(ptr) } Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local).ok_or(EvalError::ReadUndefBytes) + self.stack[frame].get_local(local).unwrap_or(Value::ByVal(PrimVal::Undef)) + } + Lvalue::Global(cid) => { + self.globals + .get(&cid) + .expect("global not cached") + .data + .unwrap_or(Value::ByVal(PrimVal::Undef)) } - Lvalue::Global(cid) => self.globals - .get(&cid) - .expect("global not cached") - .data - .ok_or(EvalError::ReadUndefBytes), } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index b4cad215b77aa..9672b0c3d920a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -585,7 +585,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { // special case `Box` to deallocate the inner allocation ty::TyBox(contents_ty) => { - let val = self.read_lvalue(lval)?; + let val = self.read_lvalue(lval); // we are going through the read_value path, because that already does all the // checks for the trait object types. We'd only be repeating ourselves here. let val = self.follow_by_ref_value(val, ty)?; From b233ada529fc6c7f5b99c8b2007a7e3c1ec75c8a Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 18 Dec 2016 20:59:01 -0800 Subject: [PATCH 0719/1096] Change Option to Value, using ByVal(Undef). This job isn't quite finished because it caused me to discover bugs related to reading `ByVal(Undef)` when a `ByValPair` is expected, e.g. for a fat pointer. This wasn't a problem with the `None` of `Option`, but I realized an equivalent bug existed even then, since you could transmute a `u64` like `ByVal(Bytes(42))` to a fat pointer type on 32-bit targets. Likewise, you could transmute a fat pointer to `u64` and get panics related to expecting `ByVal` but finding `ByValPair`, so the problem goes both ways. --- src/eval_context.rs | 154 ++++++++++++++++-------------------- src/lvalue.rs | 18 ++--- src/memory.rs | 15 +--- src/terminator/intrinsic.rs | 27 ++++--- src/terminator/mod.rs | 4 +- 5 files changed, 93 insertions(+), 125 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ae95d1b05aa1..a9d930e0fdf52 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -77,8 +77,8 @@ pub struct Frame<'tcx> { /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which /// can either directly contain `PrimVal` or refer to some part of an `Allocation`. /// - /// Before being initialized, a local is simply marked as None. - pub locals: Vec>, + /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`. + pub locals: Vec, /// Temporary allocations introduced to save stackframes /// This is pure interpreter magic and has nothing to do with how rustc does it @@ -283,7 +283,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local // `Value` for that. let num_locals = mir.local_decls.len() - 1; - let locals = vec![None; num_locals]; + let locals = vec![Value::ByVal(PrimVal::Undef); num_locals]; self.stack.push(Frame { mir: mir, @@ -310,10 +310,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { StackPopCleanup::Freeze => if let Lvalue::Global(id) = frame.return_lvalue { - let global_value = self.globals - .get_mut(&id) - .expect("global should have been cached (freeze)"); - match global_value.data.expect("global should have been initialized") { + let global_value = self.globals.get_mut(&id) + .expect("global should have been cached (freeze)"); + match global_value.value { Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { self.memory.freeze(ptr.alloc_id)?; @@ -337,7 +336,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // deallocate all locals that are backed by an allocation for local in frame.locals.into_iter() { - if let Some(Value::ByRef(ptr)) = local { + if let Value::ByRef(ptr) = local { trace!("deallocating local"); self.memory.dump_alloc(ptr.alloc_id); match self.memory.deallocate(ptr) { @@ -423,7 +422,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty)?.expect("array elements are sized") as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty)? + .expect("array elements are sized") as u64, _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; let offsets = (0..).map(|i| i * elem_size); @@ -488,7 +488,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); let dest = dest.offset(offset.bytes()); - let dest_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); + let dest_size = self.type_size(ty)? + .expect("bad StructWrappedNullablePointer discrfield"); self.memory.write_int(dest, 0, dest_size)?; } } else { @@ -541,7 +542,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; self.inc_step_counter_and_check_limit(length)?; - let elem_size = self.type_size(elem_ty)?.expect("repeat element type must be sized"); + let elem_size = self.type_size(elem_ty)? + .expect("repeat element type must be sized"); let value = self.eval_operand(operand)?; // FIXME(solson) @@ -811,16 +813,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let new_lvalue = match lvalue { Lvalue::Local { frame, local } => { match self.stack[frame].get_local(local) { - Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), - opt_val => { + Value::ByRef(ptr) => Lvalue::from_ptr(ptr), + val => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].substs); let substs = self.stack[frame].substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].set_local(local, Value::ByRef(ptr)); - if let Some(val) = opt_val { - self.write_value_to_ptr(val, ptr, ty)?; - } + self.write_value_to_ptr(val, ptr, ty)?; Lvalue::from_ptr(ptr) } } @@ -828,19 +828,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { .. } => lvalue, Lvalue::Global(cid) => { let global_val = *self.globals.get(&cid).expect("global not cached"); - match global_val.data { - Some(Value::ByRef(ptr)) => Lvalue::from_ptr(ptr), + match global_val.value { + Value::ByRef(ptr) => Lvalue::from_ptr(ptr), _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; - if let Some(val) = global_val.data { - self.write_value_to_ptr(val, ptr, global_val.ty)?; - } + self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; if !global_val.mutable { self.memory.freeze(ptr.alloc_id)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { - data: Some(Value::ByRef(ptr)), + value: Value::ByRef(ptr), .. global_val }; Lvalue::from_ptr(ptr) @@ -881,8 +879,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - let kind = self.ty_to_primval_kind(dest_ty)?; - self.memory.write_primval(ptr, val, kind) + let size = self.type_size(dest_ty)?.expect("dest type must be sized"); + self.memory.write_primval(ptr, val, size) } Lvalue::Local { frame, local } => { self.stack[frame].set_local(local, Value::ByVal(val)); @@ -891,7 +889,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Global(cid) => { let global_val = self.globals.get_mut(&cid).expect("global not cached"); if global_val.mutable { - global_val.data = Some(Value::ByVal(val)); + global_val.value = Value::ByVal(val); Ok(()) } else { Err(EvalError::ModifiedConstantMemory) @@ -912,12 +910,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !dest.mutable { return Err(EvalError::ModifiedConstantMemory); } - self.write_value_possibly_by_val( - src_val, - |this, val| *this.globals.get_mut(&cid).expect("already checked") = Global { data: Some(val), ..dest }, - dest.data, - dest_ty, - ) + let write_dest = |this: &mut Self, val| { + *this.globals.get_mut(&cid).expect("already checked") = Global { + value: val, + ..dest + } + }; + self.write_value_possibly_by_val(src_val, write_dest, dest.value, dest_ty) }, Lvalue::Ptr { ptr, extra } => { @@ -942,10 +941,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, src_val: Value, write_dest: F, - old_dest_val: Option, + old_dest_val: Value, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { - if let Some(Value::ByRef(dest_ptr)) = old_dest_val { + if let Value::ByRef(dest_ptr) = old_dest_val { // If the value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be // other pointers into the allocation. These other pointers are logically @@ -992,8 +991,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => { - let kind = self.ty_to_primval_kind(dest_ty)?; - self.memory.write_primval(dest, primval, kind) + let size = self.type_size(dest_ty)?.expect("dest type must be sized"); + self.memory.write_primval(dest, primval, size) } Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest, dest_ty), } @@ -1011,10 +1010,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1 = self.get_field_offset(ty, 1)?.bytes(); let field_0_ty = self.get_field_ty(ty, 0)?; let field_1_ty = self.get_field_ty(ty, 1)?; - let field_0_kind = self.ty_to_primval_kind(field_0_ty)?; - let field_1_kind = self.ty_to_primval_kind(field_1_ty)?; - self.memory.write_primval(ptr.offset(field_0), a, field_0_kind)?; - self.memory.write_primval(ptr.offset(field_1), b, field_1_kind)?; + let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); + let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); + self.memory.write_primval(ptr.offset(field_0), a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1), b, field_1_size)?; Ok(()) } @@ -1293,21 +1292,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut allocs = Vec::new(); if let Lvalue::Local { frame, local } = lvalue { - if let Some(val) = self.stack[frame].get_local(local) { - match val { - Value::ByRef(ptr) => { - trace!("frame[{}] {:?}:", frame, local); - allocs.push(ptr.alloc_id); - } - Value::ByVal(val) => { - trace!("frame[{}] {:?}: {:?}", frame, local, val); - if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } - } - Value::ByValPair(val1, val2) => { - trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); - if let PrimVal::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } - if let PrimVal::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } - } + match self.stack[frame].get_local(local) { + Value::ByRef(ptr) => { + trace!("frame[{}] {:?}:", frame, local); + allocs.push(ptr.alloc_id); + } + Value::ByVal(val) => { + trace!("frame[{}] {:?}: {:?}", frame, local, val); + if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } + } + Value::ByValPair(val1, val2) => { + trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); + if let PrimVal::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } + if let PrimVal::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } } } } @@ -1315,61 +1312,48 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.dump_allocs(allocs); } - /// convenience function to ensure correct usage of globals and code-sharing with locals - pub fn modify_global< - F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Option>, - >( - &mut self, - cid: GlobalId<'tcx>, - f: F, - ) -> EvalResult<'tcx, ()> { + /// Convenience function to ensure correct usage of globals and code-sharing with locals. + pub fn modify_global(&mut self, cid: GlobalId<'tcx>, f: F) -> EvalResult<'tcx, ()> + where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, + { let mut val = *self.globals.get(&cid).expect("global not cached"); if !val.mutable { return Err(EvalError::ModifiedConstantMemory); } - val.data = f(self, val.data)?; + val.value = f(self, val.value)?; *self.globals.get_mut(&cid).expect("already checked") = val; Ok(()) } - /// convenience function to ensure correct usage of locals and code-sharing with globals - pub fn modify_local< - F: FnOnce(&mut Self, Option) -> EvalResult<'tcx, Option>, - >( + /// Convenience function to ensure correct usage of locals and code-sharing with globals. + pub fn modify_local( &mut self, frame: usize, local: mir::Local, f: F, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx, ()> + where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, + { let val = self.stack[frame].get_local(local); - let val = f(self, val)?; - if let Some(val) = val { - self.stack[frame].set_local(local, val); - } else { - self.deallocate_local(frame, local)?; - } - Ok(()) - } - - pub fn deallocate_local(&mut self, frame: usize, local: mir::Local) -> EvalResult<'tcx, ()> { - if let Some(Value::ByRef(ptr)) = self.stack[frame].get_local(local) { - self.memory.deallocate(ptr)?; - } - // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.stack[frame].locals[local.index() - 1] = None; + let new_val = f(self, val)?; + self.stack[frame].set_local(local, new_val); + // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) + // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { + // self.memory.deallocate(ptr)?; + // } Ok(()) } } impl<'tcx> Frame<'tcx> { - pub fn get_local(&self, local: mir::Local) -> Option { + pub fn get_local(&self, local: mir::Local) -> Value { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1] } fn set_local(&mut self, local: mir::Local, value: Value) { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] = Some(value); + self.locals[local.index() - 1] = value; } } diff --git a/src/lvalue.rs b/src/lvalue.rs index f06cffeee89b5..8776d297495fb 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -55,7 +55,7 @@ pub struct GlobalId<'tcx> { #[derive(Copy, Clone, Debug)] pub struct Global<'tcx> { - pub(super) data: Option, + pub(super) value: Value, pub(super) mutable: bool, pub(super) ty: Ty<'tcx>, } @@ -98,7 +98,7 @@ impl<'tcx> Lvalue<'tcx> { impl<'tcx> Global<'tcx> { pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { Global { - data: None, + value: Value::ByVal(PrimVal::Undef), mutable: true, ty: ty, } @@ -109,7 +109,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Local(index) = proj.base { - if let Some(Value::ByValPair(a, b)) = self.frame().get_local(index) { + if let Value::ByValPair(a, b) = self.frame().get_local(index) { if let mir::ProjectionElem::Field(ref field, _) = proj.elem { let val = [a, b][field.index()]; return Ok(Value::ByVal(val)); @@ -127,16 +127,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(extra, LvalueExtra::None); Value::ByRef(ptr) } - Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local).unwrap_or(Value::ByVal(PrimVal::Undef)) - } - Lvalue::Global(cid) => { - self.globals - .get(&cid) - .expect("global not cached") - .data - .unwrap_or(Value::ByVal(PrimVal::Undef)) - } + Lvalue::Local { frame, local } => self.stack[frame].get_local(local), + Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value, } } diff --git a/src/memory.rs b/src/memory.rs index d65bf7f145007..2825f55eab4b5 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -11,7 +11,7 @@ use rustc::ty::layout::{self, TargetDataLayout}; use syntax::abi::Abi; use error::{EvalError, EvalResult}; -use value::{PrimVal, PrimValKind}; +use value::PrimVal; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -584,17 +584,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { &mut self, dest: Pointer, val: PrimVal, - kind: PrimValKind, + size: u64, ) -> EvalResult<'tcx, ()> { - use value::PrimValKind::*; - let size = match kind { - I8 | U8 | Bool => 1, - I16 | U16 => 2, - I32 | U32 | F32 | Char => 4, - I64 | U64 | F64 => 8, - Ptr | FnPtr => self.pointer_size(), - }; - match val { PrimVal::Ptr(ptr) => { assert_eq!(size, self.pointer_size()); @@ -609,7 +600,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 2 => 0xffff, 4 => 0xffffffff, 8 => 0xffffffffffffffff, - _ => bug!("unexpected PrimVal size"), + _ => bug!("unexpected PrimVal::Bytes size"), }; self.write_uint(dest, bytes & mask, size) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a25d10d05aeae..193abd2138906 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -227,14 +227,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "forget" => {} "init" => { - let size = self.type_size(dest_ty)?.expect("cannot zero unsized value");; - let init = |this: &mut Self, val: Option| { + let size = self.type_size(dest_ty)?.expect("cannot zero unsized value"); + let init = |this: &mut Self, val: Value| { let zero_val = match val { - Some(Value::ByRef(ptr)) => { + Value::ByRef(ptr) => { this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) }, - None => match this.ty_to_primval_kind(dest_ty) { + // TODO(solson): Revisit this, it's fishy to check for Undef here. + Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; @@ -242,11 +243,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) } }, - Some(Value::ByVal(_)) => Value::ByVal(PrimVal::Bytes(0)), - Some(Value::ByValPair(..)) => + Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)), + Value::ByValPair(..) => Value::ByValPair(PrimVal::Bytes(0), PrimVal::Bytes(0)), }; - Ok(Some(zero_val)) + Ok(zero_val) }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, @@ -371,19 +372,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "uninit" => { let size = dest_layout.size(&self.tcx.data_layout).bytes(); - let uninit = |this: &mut Self, val: Option| { + let uninit = |this: &mut Self, val: Value| { match val { - Some(Value::ByRef(ptr)) => { + Value::ByRef(ptr) => { this.memory.mark_definedness(ptr, size, false)?; - Ok(Some(Value::ByRef(ptr))) + Ok(Value::ByRef(ptr)) }, - None => Ok(None), - Some(_) => Ok(None), + _ => Ok(Value::ByVal(PrimVal::Undef)), } }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => + self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, uninit)?, } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9672b0c3d920a..d74458a87cda8 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -515,8 +515,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) => ptr, Value::ByVal(primval) => { let ptr = self.alloc_ptr(args[0].1)?; - let kind = self.ty_to_primval_kind(args[0].1)?; - self.memory.write_primval(ptr, primval, kind)?; + let size = self.type_size(args[0].1)?.expect("closures are sized"); + self.memory.write_primval(ptr, primval, size)?; temporaries.push(ptr); ptr }, From 2a5029ed6d161d3067f0579705596e1f2a75ef23 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 18 Dec 2016 23:31:23 -0800 Subject: [PATCH 0720/1096] Update MIR passes to match rustc. --- src/eval_context.rs | 12 +++++++++++- src/lvalue.rs | 10 +++++++++- src/step.rs | 30 +++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index a9d930e0fdf52..cc3d02d8ce297 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -465,7 +465,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - self.write_primval(dest, PrimVal::from_i64(0), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -1423,11 +1423,21 @@ pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("no-landing-pads"))); + // From here on out, regions are gone. passes.push_pass(Box::new(::rustc_mir::transform::erase_regions::EraseRegions)); + passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); passes.push_pass(Box::new(::rustc_borrowck::ElaborateDrops)); passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("elaborate-drops"))); + + // No lifetime analysis based on borrowing can be done from here on out. + passes.push_pass(Box::new(::rustc_mir::transform::instcombine::InstCombine::new())); + passes.push_pass(Box::new(::rustc_mir::transform::deaggregator::Deaggregator)); + passes.push_pass(Box::new(::rustc_mir::transform::copy_prop::CopyPropagation)); + + passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyLocals)); + passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); passes.run_passes(tcx); diff --git a/src/lvalue.rs b/src/lvalue.rs index 8776d297495fb..966fb65c3cd14 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,5 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; +use rustc::ty::layout::Size; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; @@ -196,7 +197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } RawNullablePointer { .. } => { - assert_eq!(field.index(), 0); + assert_eq!(field, 0); return Ok(base); } @@ -206,6 +207,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UntaggedUnion { .. } => return Ok(base), + Vector { element, count } => { + let field = field as u64; + assert!(field < count); + let elem_size = element.size(&self.tcx.data_layout).bytes(); + Size::from_bytes(field * elem_size) + } + _ => bug!("field access on non-product type: {:?}", base_layout), }; diff --git a/src/step.rs b/src/step.rs index ddd6c40e4c79b..527df96a18bfd 100644 --- a/src/step.rs +++ b/src/step.rs @@ -8,6 +8,7 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir; +use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; use error::{EvalResult, EvalError}; @@ -85,7 +86,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::StatementKind::*; match stmt.kind { Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, - SetDiscriminant { .. } => unimplemented!(), + + SetDiscriminant { ref lvalue, variant_index } => { + let dest = self.eval_lvalue(lvalue)?; + let dest_ty = self.lvalue_ty(lvalue); + let dest_layout = self.type_layout(dest_ty)?; + + match *dest_layout { + Layout::General { discr, ref variants, .. } => { + let discr_size = discr.size().bytes(); + let discr_offset = variants[variant_index].offsets[0].bytes(); + + // FIXME(solson) + let dest = self.force_allocation(dest)?; + let discr_dest = (dest.to_ptr()).offset(discr_offset); + + self.memory.write_uint(discr_dest, variant_index as u64, discr_size)?; + } + + Layout::RawNullablePointer { nndiscr, .. } => { + use value::PrimVal; + if variant_index as u64 != nndiscr { + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + } + + _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout), + } + } // Miri can safely ignore these. Only translation needs it. StorageLive(_) | From 9093188a5c15d9a60c1ab08d5fecd9fa88338b38 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 21 Dec 2016 17:15:03 -0800 Subject: [PATCH 0721/1096] Clean up useless `pub use`. --- src/eval_context.rs | 5 +---- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index cc3d02d8ce297..97b6203ec0686 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -16,10 +16,7 @@ use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; use memory::{Memory, Pointer}; use operator; -use value::{PrimVal, PrimValKind}; - -// FIXME(solson): Remove this. -pub use value::Value; +use value::{PrimVal, PrimValKind, Value}; pub type MirRef<'tcx> = Ref<'tcx, mir::Mir<'tcx>>; diff --git a/src/lib.rs b/src/lib.rs index 1400d637767dc..4617a35b35035 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,6 @@ pub use eval_context::{ Frame, ResourceLimits, StackPopCleanup, - Value, eval_main, run_mir_passes, }; @@ -62,4 +61,5 @@ pub use memory::{ pub use value::{ PrimVal, PrimValKind, + Value, }; From 6d1c47b6ef27f6fc913c619d731c147cb00f8824 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 21 Dec 2016 17:23:28 -0800 Subject: [PATCH 0722/1096] Update for changes in rustc. --- src/eval_context.rs | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 97b6203ec0686..adbb0db59149f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -469,7 +469,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield } => { + StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { if nndiscr == variant as u64 { let offsets = nonnull.offsets.iter().map(|s| s.bytes()); diff --git a/src/lib.rs b/src/lib.rs index 4617a35b35035..8965384e162e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![feature( btree_range, - cell_extras, collections, collections_bound, pub_restricted, From d6e35fe46fbb170c882277cff3532caec04e4940 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Wed, 21 Dec 2016 17:26:52 -0800 Subject: [PATCH 0723/1096] Add test for #95. --- tests/run-pass/move-undef-primval.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/run-pass/move-undef-primval.rs diff --git a/tests/run-pass/move-undef-primval.rs b/tests/run-pass/move-undef-primval.rs new file mode 100644 index 0000000000000..73c33943a63ac --- /dev/null +++ b/tests/run-pass/move-undef-primval.rs @@ -0,0 +1,12 @@ +struct Foo { + _inner: i32, +} + +fn main() { + unsafe { + let foo = Foo { + _inner: std::mem::uninitialized(), + }; + let _bar = foo; + } +} From 3a658e09e88ad999fe1f5dbc4ac7a14c0d38c90f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 08:28:42 +0100 Subject: [PATCH 0724/1096] rustup (i128) --- .gitignore | 1 + Cargo.lock | 8 ++--- Cargo.toml | 3 +- src/bin/miri.rs | 8 ++--- src/cast.rs | 40 +++++++++++---------- src/error.rs | 2 +- src/eval_context.rs | 33 +++++++++-------- src/lib.rs | 1 + src/memory.rs | 64 +++++++++++++++++---------------- src/operator.rs | 44 ++++++++++++----------- src/step.rs | 2 +- src/terminator/intrinsic.rs | 42 +++++++++++----------- src/terminator/mod.rs | 28 +++++++-------- src/value.rs | 71 +++++++++++++++++++++++++------------ 14 files changed, 196 insertions(+), 151 deletions(-) diff --git a/.gitignore b/.gitignore index d9940aa58b5ca..a51553a8c5e22 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ tex/*/out *.dot *.mir +*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index b63b557f07161..94611529c5d13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ name = "miri" version = "0.1.0" dependencies = [ - "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)", "compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -19,8 +19,8 @@ dependencies = [ [[package]] name = "byteorder" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "1.0.0" +source = "git+https://github.com/quininer/byteorder.git?branch=i128#ef51df297aa833d0b6639aae328a95597fc07d75" [[package]] name = "compiletest_rs" @@ -136,7 +136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" +"checksum byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)" = "" "checksum compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f344389765ad7bec166f64c1b39ed6dd2b54d81c4c5dd8af789169351d380c" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" diff --git a/Cargo.toml b/Cargo.toml index 1d5de26f00730..ada46a794cb59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ test = false test = false [dependencies] -byteorder = "0.4.2" +#byteorder = "0.4.2" +byteorder = { git = "https://github.com/quininer/byteorder.git", branch = "i128", features = ["i128"]} env_logger = "0.3.3" log = "0.3.6" log_settings = "0.1.1" diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 24a0b7ba00b0a..379f25ef7576a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,4 +1,4 @@ -#![feature(rustc_private)] +#![feature(rustc_private, i128_type)] extern crate getopts; extern crate miri; @@ -51,7 +51,7 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits let mut limits = miri::ResourceLimits::default(); let krate = state.hir_crate.as_ref().unwrap(); let err_msg = "miri attributes need to be in the form `miri(key = value)`"; - let extract_int = |lit: &syntax::ast::Lit| -> u64 { + let extract_int = |lit: &syntax::ast::Lit| -> u128 { match lit.node { syntax::ast::LitKind::Int(i, _) => i, _ => state.session.span_fatal(lit.span, "expected an integer literal"), @@ -64,8 +64,8 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits if let NestedMetaItemKind::MetaItem(ref inner) = item.node { if let MetaItemKind::NameValue(ref value) = inner.node { match &inner.name().as_str()[..] { - "memory_size" => limits.memory_size = extract_int(value), - "step_limit" => limits.step_limit = extract_int(value), + "memory_size" => limits.memory_size = extract_int(value) as u64, + "step_limit" => limits.step_limit = extract_int(value) as u64, "stack_limit" => limits.stack_limit = extract_int(value) as usize, _ => state.session.span_err(item.span, "unknown miri attribute"), } diff --git a/src/cast.rs b/src/cast.rs index 2b3194a59940c..413c9b6ba827b 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -20,34 +20,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { F32 => self.cast_float(val.to_f32()? as f64, dest_ty), F64 => self.cast_float(val.to_f64()?, dest_ty), - I8 | I16 | I32 | I64 => self.cast_signed_int(val.to_i64()?, dest_ty), + I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty), - Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.to_u64()?, dest_ty, false), + Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false), FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty), } } - fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_int(val as u64, ty, val < 0) + fn cast_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + self.cast_int(val as u128, ty, val < 0) } - fn cast_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { + fn cast_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { TyBool if v == 0 => Ok(PrimVal::from_bool(false)), TyBool if v == 1 => Ok(PrimVal::from_bool(true)), TyBool => Err(EvalError::InvalidBool), - TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i64 as i8 as u64)), - TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i64 as i16 as u64)), - TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i64 as i32 as u64)), - TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i64 as i64 as u64)), + TyInt(IntTy::I8) => Ok(PrimVal::Bytes(v as i128 as i8 as u128)), + TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i128 as i16 as u128)), + TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i128 as i32 as u128)), + TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i128 as i64 as u128)), + TyInt(IntTy::I128) => Ok(PrimVal::Bytes(v as u128)), - TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u64)), - TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u64)), - TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u64)), - TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v)), + TyUint(UintTy::U8) => Ok(PrimVal::Bytes(v as u8 as u128)), + TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u128)), + TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u128)), + TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v as u64 as u128)), + TyUint(UintTy::U128) => Ok(PrimVal::Bytes(v)), TyInt(IntTy::Is) => { let int_ty = self.tcx.sess.target.int_type; @@ -61,15 +63,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.cast_int(v, ty, negative) } - TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i64 as f64)), + TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)), TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)), - TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i64 as f32)), + TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i128 as f32)), TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)), - TyChar if v as u8 as u64 == v => Ok(PrimVal::Bytes(v)), + TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), - TyRawPtr(_) => Ok(PrimVal::Ptr(Pointer::from_int(v))), + TyRawPtr(_) => Ok(PrimVal::Ptr(Pointer::from_int(v as u64))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } @@ -80,9 +82,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { // Casting negative floats to unsigned integers yields zero. TyUint(_) if val < 0.0 => self.cast_int(0, ty, false), - TyInt(_) if val < 0.0 => self.cast_int(val as i64 as u64, ty, true), + TyInt(_) if val < 0.0 => self.cast_int(val as i128 as u128, ty, true), - TyInt(_) | ty::TyUint(_) => self.cast_int(val as u64, ty, false), + TyInt(_) | ty::TyUint(_) => self.cast_int(val as u128, ty, false), TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)), TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)), diff --git a/src/error.rs b/src/error.rs index 9b532e0137592..bf01ae27d2a28 100644 --- a/src/error.rs +++ b/src/error.rs @@ -31,7 +31,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), - InvalidChar(u64), + InvalidChar(u128), OutOfMemory { allocation_size: u64, memory_size: u64, diff --git a/src/eval_context.rs b/src/eval_context.rs index adbb0db59149f..7a0264665e523 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -169,7 +169,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u64(s.len() as u64))) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { @@ -177,7 +177,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::ConstFloat; let primval = match *const_val { - Integral(const_int) => PrimVal::Bytes(const_int.to_u64_unchecked()), + Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()), Float(ConstFloat::F32(f)) => PrimVal::from_f32(f), Float(ConstFloat::F64(f)) => PrimVal::from_f64(f), @@ -429,7 +429,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked(); let discr_size = discr.size().bytes(); let discr_offset = variants[variant].offsets[0].bytes(); @@ -497,7 +497,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let n = adt_def.variants[variant].disr_val.to_u64_unchecked(); + let n = adt_def.variants[variant].disr_val.to_u128_unchecked(); self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); @@ -556,7 +556,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); - self.write_primval(dest, PrimVal::from_u64(len), dest_ty)?; + self.write_primval(dest, PrimVal::from_u128(len as u128), dest_ty)?; } Ref(_, _, ref lvalue) => { @@ -566,7 +566,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = match extra { LvalueExtra::None => Value::ByVal(ptr), - LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u64(len)), + LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u128(len as u128)), LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -1028,6 +1028,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I16 => 2, I32 => 4, I64 => 8, + I128 => 16, Is => self.memory.pointer_size(), }; PrimValKind::from_int_size(size) @@ -1040,6 +1041,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U16 => 2, U32 => 4, U64 => 8, + U128 => 16, Us => self.memory.pointer_size(), }; PrimValKind::from_uint_size(size) @@ -1092,7 +1094,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyBool if val.to_bytes()? > 1 => Err(EvalError::InvalidBool), ty::TyChar if ::std::char::from_u32(val.to_bytes()? as u32).is_none() - => Err(EvalError::InvalidChar(val.to_bytes()? as u32 as u64)), + => Err(EvalError::InvalidChar(val.to_bytes()? as u32 as u128)), _ => Ok(()), } @@ -1115,7 +1117,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::from_char(ch), - None => return Err(EvalError::InvalidChar(c as u64)), + None => return Err(EvalError::InvalidChar(c as u128)), } } @@ -1126,9 +1128,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I16 => 2, I32 => 4, I64 => 8, + I128 => 16, Is => self.memory.pointer_size(), }; - PrimVal::from_i64(self.memory.read_int(ptr, size)?) + PrimVal::from_i128(self.memory.read_int(ptr, size)?) } ty::TyUint(uint_ty) => { @@ -1138,9 +1141,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U16 => 2, U32 => 4, U64 => 8, + U128 => 16, Us => self.memory.pointer_size(), }; - PrimVal::from_u64(self.memory.read_uint(ptr, size)?) + PrimVal::from_u128(self.memory.read_uint(ptr, size)?) } ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), @@ -1159,7 +1163,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let extra = match self.tcx.struct_tail(ty).sty { ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => PrimVal::from_u64(self.memory.read_usize(extra)?), + ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), _ => bug!("unsized primval ptr read from {:?}", ty), }; return Ok(Some(Value::ByValPair(PrimVal::Ptr(p), extra))); @@ -1171,9 +1175,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes(); if signed { - PrimVal::from_i64(self.memory.read_int(ptr, size)?) + PrimVal::from_i128(self.memory.read_int(ptr, size)?) } else { - PrimVal::from_u64(self.memory.read_uint(ptr, size)?) + PrimVal::from_u128(self.memory.read_uint(ptr, size)?) } } else { return Ok(None); @@ -1220,7 +1224,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - let len = PrimVal::from_u64(length as u64); + let len = PrimVal::from_u128(length as u128); let ptr = PrimVal::Ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; } @@ -1454,6 +1458,7 @@ impl IntegerExt for layout::Integer { I16 => Size::from_bits(16), I32 => Size::from_bits(32), I64 => Size::from_bits(64), + I128 => Size::from_bits(128), } } } diff --git a/src/lib.rs b/src/lib.rs index 8965384e162e4..418ccfa549fef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ collections_bound, pub_restricted, rustc_private, + i128_type, )] // From rustc. diff --git a/src/memory.rs b/src/memory.rs index 2825f55eab4b5..5be28f27712a4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ -use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian, self}; +use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; -use std::{fmt, iter, ptr, mem}; +use std::{fmt, iter, ptr, mem, io}; use rustc::hir::def_id::DefId; use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt}; @@ -567,6 +567,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size)?; let offset = read_target_uint(endianess, bytes).unwrap(); + assert_eq!(offset as u64 as u128, offset); + let offset = offset as u64; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)), @@ -596,10 +598,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // We need to mask here, or the byteorder crate can die when given a u64 larger // than fits in an integer of the requested size. let mask = match size { - 1 => 0xff, - 2 => 0xffff, - 4 => 0xffffffff, - 8 => 0xffffffffffffffff, + 1 => !0u8 as u128, + 2 => !0u16 as u128, + 4 => !0u32 as u128, + 8 => !0u64 as u128, + 16 => !0, _ => bug!("unexpected PrimVal::Bytes size"), }; self.write_uint(dest, bytes & mask, size) @@ -630,16 +633,17 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 2 => Ok(self.layout.i16_align.abi()), 4 => Ok(self.layout.i32_align.abi()), 8 => Ok(self.layout.i64_align.abi()), + 16 => Ok(self.layout.i128_align.abi()), _ => bug!("bad integer size: {}", size), } } - pub fn read_int(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, i64> { + pub fn read_int(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, i128> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } - pub fn write_int(&mut self, ptr: Pointer, n: i64, size: u64) -> EvalResult<'tcx, ()> { + pub fn write_int(&mut self, ptr: Pointer, n: i128, size: u64) -> EvalResult<'tcx, ()> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -647,12 +651,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_uint(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, u64> { + pub fn read_uint(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, u128> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } - pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: u64) -> EvalResult<'tcx, ()> { + pub fn write_uint(&mut self, ptr: Pointer, n: u128, size: u64) -> EvalResult<'tcx, ()> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -661,21 +665,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { - self.read_int(ptr, self.pointer_size()) + self.read_int(ptr, self.pointer_size()).map(|i| i as i64) } pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx, ()> { let size = self.pointer_size(); - self.write_int(ptr, n, size) + self.write_int(ptr, n as i128, size) } pub fn read_usize(&self, ptr: Pointer) -> EvalResult<'tcx, u64> { - self.read_uint(ptr, self.pointer_size()) + self.read_uint(ptr, self.pointer_size()).map(|i| i as u64) } pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { let size = self.pointer_size(); - self.write_uint(ptr, n, size) + self.write_uint(ptr, n as u128, size) } pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { @@ -801,31 +805,31 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // Methods to access integers in the target endianess //////////////////////////////////////////////////////////////////////////////// -fn write_target_uint(endianess: layout::Endian, mut target: &mut [u8], data: u64) -> Result<(), byteorder::Error> { +fn write_target_uint(endianess: layout::Endian, mut target: &mut [u8], data: u128) -> Result<(), io::Error> { let len = target.len(); match endianess { - layout::Endian::Little => target.write_uint::(data, len), - layout::Endian::Big => target.write_uint::(data, len), + layout::Endian::Little => target.write_uint128::(data, len), + layout::Endian::Big => target.write_uint128::(data, len), } } -fn write_target_int(endianess: layout::Endian, mut target: &mut [u8], data: i64) -> Result<(), byteorder::Error> { +fn write_target_int(endianess: layout::Endian, mut target: &mut [u8], data: i128) -> Result<(), io::Error> { let len = target.len(); match endianess { - layout::Endian::Little => target.write_int::(data, len), - layout::Endian::Big => target.write_int::(data, len), + layout::Endian::Little => target.write_int128::(data, len), + layout::Endian::Big => target.write_int128::(data, len), } } -fn read_target_uint(endianess: layout::Endian, mut source: &[u8]) -> Result { +fn read_target_uint(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { - layout::Endian::Little => source.read_uint::(source.len()), - layout::Endian::Big => source.read_uint::(source.len()), + layout::Endian::Little => source.read_uint128::(source.len()), + layout::Endian::Big => source.read_uint128::(source.len()), } } -fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result { +fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { - layout::Endian::Little => source.read_int::(source.len()), - layout::Endian::Big => source.read_int::(source.len()), + layout::Endian::Little => source.read_int128::(source.len()), + layout::Endian::Big => source.read_int128::(source.len()), } } @@ -833,26 +837,26 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result Result<(), byteorder::Error> { +fn write_target_f32(endianess: layout::Endian, mut target: &mut [u8], data: f32) -> Result<(), io::Error> { match endianess { layout::Endian::Little => target.write_f32::(data), layout::Endian::Big => target.write_f32::(data), } } -fn write_target_f64(endianess: layout::Endian, mut target: &mut [u8], data: f64) -> Result<(), byteorder::Error> { +fn write_target_f64(endianess: layout::Endian, mut target: &mut [u8], data: f64) -> Result<(), io::Error> { match endianess { layout::Endian::Little => target.write_f64::(data), layout::Endian::Big => target.write_f64::(data), } } -fn read_target_f32(endianess: layout::Endian, mut source: &[u8]) -> Result { +fn read_target_f32(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { layout::Endian::Little => source.read_f32::(), layout::Endian::Big => source.read_f32::(), } } -fn read_target_f64(endianess: layout::Endian, mut source: &[u8]) -> Result { +fn read_target_f64(endianess: layout::Endian, mut source: &[u8]) -> Result { match endianess { layout::Endian::Little => source.read_f64::(), layout::Endian::Big => source.read_f64::(), diff --git a/src/operator.rs b/src/operator.rs index 2e1ac075294b4..417e7047ec00e 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { macro_rules! overflow { ($op:ident, $l:expr, $r:expr) => ({ let (val, overflowed) = $l.$op($r); - let primval = PrimVal::Bytes(val as u64); + let primval = PrimVal::Bytes(val as u128); Ok((primval, overflowed)) }) } @@ -144,7 +144,7 @@ pub fn binary_op<'tcx>( fn normalize(val: PrimVal) -> PrimVal { if let PrimVal::Ptr(ptr) = val { if let Ok(bytes) = ptr.to_int() { - return PrimVal::Bytes(bytes); + return PrimVal::Bytes(bytes as u128); } } val @@ -158,7 +158,7 @@ pub fn binary_op<'tcx>( if left_ptr.alloc_id == right_ptr.alloc_id { // If the pointers are into the same allocation, fall through to the more general // match later, which will do comparisons on the pointer offsets. - (left_ptr.offset, right_ptr.offset) + (left_ptr.offset as u128, right_ptr.offset as u128) } else { return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); } @@ -166,7 +166,7 @@ pub fn binary_op<'tcx>( (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) | (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { - return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes))?, false)); + return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false)); } (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), @@ -182,6 +182,7 @@ pub fn binary_op<'tcx>( I16 | U16 => 16, I32 | U32 => 32, I64 | U64 => 64, + I128 | U128 => 128, _ => bug!("bad MIR: bitshift lhs is not integral"), }; @@ -278,22 +279,25 @@ pub fn unary_op<'tcx>( let bytes = val.to_bytes()?; let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !bytes_to_bool(bytes) as u64, - - (Not, U8) => !(bytes as u8) as u64, - (Not, U16) => !(bytes as u16) as u64, - (Not, U32) => !(bytes as u32) as u64, - (Not, U64) => !bytes, - - (Not, I8) => !(bytes as i8) as u64, - (Not, I16) => !(bytes as i16) as u64, - (Not, I32) => !(bytes as i32) as u64, - (Not, I64) => !(bytes as i64) as u64, - - (Neg, I8) => -(bytes as i8) as u64, - (Neg, I16) => -(bytes as i16) as u64, - (Neg, I32) => -(bytes as i32) as u64, - (Neg, I64) => -(bytes as i64) as u64, + (Not, Bool) => !bytes_to_bool(bytes) as u128, + + (Not, U8) => !(bytes as u8) as u128, + (Not, U16) => !(bytes as u16) as u128, + (Not, U32) => !(bytes as u32) as u128, + (Not, U64) => !(bytes as u64) as u128, + (Not, U128) => !bytes, + + (Not, I8) => !(bytes as i8) as u128, + (Not, I16) => !(bytes as i16) as u128, + (Not, I32) => !(bytes as i32) as u128, + (Not, I64) => !(bytes as i64) as u128, + (Not, I128) => !(bytes as i128) as u128, + + (Neg, I8) => -(bytes as i8) as u128, + (Neg, I16) => -(bytes as i16) as u128, + (Neg, I32) => -(bytes as i32) as u128, + (Neg, I64) => -(bytes as i64) as u128, + (Neg, I128) => -(bytes as i128) as u128, (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)), (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)), diff --git a/src/step.rs b/src/step.rs index 527df96a18bfd..109631327b16e 100644 --- a/src/step.rs +++ b/src/step.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?; let discr_dest = (dest.to_ptr()).offset(discr_offset); - self.memory.write_uint(discr_dest, variant_index as u64, discr_size)?; + self.memory.write_uint(discr_dest, variant_index as u128, discr_size)?; } Layout::RawNullablePointer { nndiscr, .. } => { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 193abd2138906..a91a75454dd93 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -45,8 +45,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let ptr = arg_vals[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64()?; - let new_ptr = ptr.signed_offset(offset); + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()?; + let new_ptr = ptr.signed_offset(offset as i64); self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } @@ -260,7 +260,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "min_align_of" => { let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty)?; - let align_val = PrimVal::from_u64(elem_align as u64); + let align_val = PrimVal::from_u128(elem_align as u128); self.write_primval(dest, align_val, dest_ty)?; } @@ -268,7 +268,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); - let align_val = PrimVal::from_u64(align); + let align_val = PrimVal::from_u128(align as u128); self.write_primval(dest, align_val, dest_ty)?; } @@ -289,7 +289,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_ty = substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - let offset = self.value_to_primval(arg_vals[1], isize)?.to_i64()?; + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = ptr.signed_offset(offset * pointee_size); @@ -310,13 +310,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "powif32" => { let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; - let i = self.value_to_primval(arg_vals[1], i32)?.to_i64()?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?; } "powif64" => { let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; - let i = self.value_to_primval(arg_vals[1], i32)?.to_i64()?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?; } @@ -336,21 +336,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") // see https://github.com/rust-lang/rust/pull/37708 - let size = self.type_size(ty)?.unwrap_or(!0) as u64; - self.write_primval(dest, PrimVal::from_u64(size), dest_ty)?; + let size = self.type_size(ty)?.unwrap_or(!0) as u128; + self.write_primval(dest, PrimVal::from_u128(size), dest_ty)?; } "size_of_val" => { let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_u64(size), dest_ty)?; + self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { let ty = substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; - self.write_primval(dest, PrimVal::from_u64(align), dest_ty)?; + self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; } "type_name" => { @@ -362,7 +362,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); - self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } "transmute" => { @@ -514,14 +514,16 @@ fn numeric_intrinsic<'tcx>( use value::PrimValKind::*; let result_bytes = match $kind { - I8 => (bytes as i8).$method() as u64, - U8 => (bytes as u8).$method() as u64, - I16 => (bytes as i16).$method() as u64, - U16 => (bytes as u16).$method() as u64, - I32 => (bytes as i32).$method() as u64, - U32 => (bytes as u32).$method() as u64, - I64 => (bytes as i64).$method() as u64, - U64 => bytes.$method() as u64, + I8 => (bytes as i8).$method() as u128, + U8 => (bytes as u8).$method() as u128, + I16 => (bytes as i16).$method() as u128, + U16 => (bytes as u16).$method() as u128, + I32 => (bytes as i32).$method() as u128, + U32 => (bytes as u32).$method() as u128, + I64 => (bytes as i64).$method() as u128, + U64 => (bytes as u64).$method() as u128, + I128 => (bytes as i128).$method() as u128, + U128 => bytes.$method() as u128, _ => bug!("invalid `{}` argument: {:?}", $name, val), }; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d74458a87cda8..6959da650b426 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let adt_ty = self.lvalue_ty(discr); let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u64_unchecked()); + .position(|v| discr_val == v.disr_val.to_u128_unchecked()); match matching { Some(i) => self.goto_block(targets[i]), @@ -262,7 +262,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { + fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; trace!("read_discriminant_value {:?}", adt_layout); @@ -275,13 +275,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { discr, signed: true, .. } => { let discr_size = discr.size().bytes(); - self.memory.read_int(adt_ptr, discr_size)? as u64 + self.memory.read_int(adt_ptr, discr_size)? as u128 } RawNullablePointer { nndiscr, value } => { let discr_size = value.size(&self.tcx.data_layout).bytes(); trace!("rawnullablepointer with size {}", discr_size); - self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? + self.read_nonnull_discriminant_value(adt_ptr, nndiscr as u128, discr_size)? } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { @@ -290,7 +290,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); - self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? + self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)? } // The discriminant_value intrinsic returns 0 for non-sum types. @@ -301,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: u64) -> EvalResult<'tcx, u64> { + fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> { let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, @@ -366,13 +366,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use std::cmp::Ordering::*; match left_bytes.cmp(right_bytes) { - Less => -1, + Less => -1i8, Equal => 0, Greater => 1, } }; - self.write_primval(dest, PrimVal::Bytes(result as u64), dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; } "memchr" => { @@ -641,8 +641,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { adt_def.struct_variant().fields.iter().zip(&variant.offsets) }, Layout::General { ref variants, .. } => { - let discr_val = self.read_discriminant_value(adt_ptr, ty)?; - match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u64_unchecked()) { + let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; + match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) { // start at offset 1, to skip over the discriminant Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]), None => return Err(EvalError::InvalidDiscriminant), @@ -650,8 +650,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr { - assert_eq!(discr as usize as u64, discr); + if discr == nndiscr as u128 { + assert_eq!(discr as usize as u128, discr); adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) } else { // FIXME: the zst variant might contain zst types that impl Drop @@ -660,8 +660,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Layout::RawNullablePointer { nndiscr, .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr { - assert_eq!(discr as usize as u64, discr); + if discr == nndiscr as u128 { + assert_eq!(discr as usize as u128, discr); assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); let field_ty = &adt_def.variants[discr as usize].fields[0]; let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); diff --git a/src/value.rs b/src/value.rs index ddffdd01ee8cd..8d153528d4f08 100644 --- a/src/value.rs +++ b/src/value.rs @@ -6,23 +6,23 @@ use std::mem::transmute; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; -pub(super) fn bytes_to_f32(bytes: u64) -> f32 { +pub(super) fn bytes_to_f32(bytes: u128) -> f32 { unsafe { transmute::(bytes as u32) } } -pub(super) fn bytes_to_f64(bytes: u64) -> f64 { - unsafe { transmute::(bytes) } +pub(super) fn bytes_to_f64(bytes: u128) -> f64 { + unsafe { transmute::(bytes as u64) } } -pub(super) fn f32_to_bytes(f: f32) -> u64 { - unsafe { transmute::(f) as u64 } +pub(super) fn f32_to_bytes(f: f32) -> u128 { + unsafe { transmute::(f) as u128 } } -pub(super) fn f64_to_bytes(f: f64) -> u64 { - unsafe { transmute::(f) } +pub(super) fn f64_to_bytes(f: f64) -> u128 { + unsafe { transmute::(f) as u128 } } -pub(super) fn bytes_to_bool(n: u64) -> bool { +pub(super) fn bytes_to_bool(n: u128) -> bool { // FIXME(solson): Can we reach here due to user error? debug_assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n); n & 1 == 1 @@ -50,7 +50,7 @@ pub enum Value { #[derive(Clone, Copy, Debug)] pub enum PrimVal { /// The raw bytes of a simple value. - Bytes(u64), + Bytes(u128), /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `PrimVal` is only large enough to contain one, so we just represent the @@ -64,8 +64,8 @@ pub enum PrimVal { #[derive(Clone, Copy, Debug, PartialEq)] pub enum PrimValKind { - I8, I16, I32, I64, - U8, U16, U32, U64, + I8, I16, I32, I64, I128, + U8, U16, U32, U64, U128, F32, F64, Bool, Char, @@ -109,7 +109,9 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, len)) }, ByValPair(ptr, val) => { - Ok((ptr.to_ptr()?, val.to_u64()?)) + let len = val.to_u128()?; + assert_eq!(len as u64 as u128, len); + Ok((ptr.to_ptr()?, len as u64)) }, _ => unimplemented!(), } @@ -117,12 +119,12 @@ impl<'a, 'tcx: 'a> Value { } impl<'tcx> PrimVal { - pub fn from_u64(n: u64) -> Self { + pub fn from_u128(n: u128) -> Self { PrimVal::Bytes(n) } - pub fn from_i64(n: i64) -> Self { - PrimVal::Bytes(n as u64) + pub fn from_i128(n: i128) -> Self { + PrimVal::Bytes(n as u128) } pub fn from_f32(f: f32) -> Self { @@ -134,35 +136,56 @@ impl<'tcx> PrimVal { } pub fn from_bool(b: bool) -> Self { - PrimVal::Bytes(b as u64) + PrimVal::Bytes(b as u128) } pub fn from_char(c: char) -> Self { - PrimVal::Bytes(c as u64) + PrimVal::Bytes(c as u128) } - pub fn to_bytes(self) -> EvalResult<'tcx, u64> { + pub fn to_bytes(self) -> EvalResult<'tcx, u128> { match self { PrimVal::Bytes(b) => Ok(b), - PrimVal::Ptr(p) => p.to_int(), + PrimVal::Ptr(p) => p.to_int().map(|b| b as u128), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { match self { - PrimVal::Bytes(b) => Ok(Pointer::from_int(b)), + PrimVal::Bytes(b) => Ok(Pointer::from_int(b as u64)), PrimVal::Ptr(p) => Ok(p), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } - pub fn to_u64(self) -> EvalResult<'tcx, u64> { + pub fn to_u128(self) -> EvalResult<'tcx, u128> { self.to_bytes() } + pub fn to_u64(self) -> EvalResult<'tcx, u64> { + self.to_bytes().map(|b| { + assert_eq!(b as u64 as u128, b); + b as u64 + }) + } + + pub fn to_i32(self) -> EvalResult<'tcx, i32> { + self.to_bytes().map(|b| { + assert_eq!(b as i32 as u128, b); + b as i32 + }) + } + + pub fn to_i128(self) -> EvalResult<'tcx, i128> { + self.to_bytes().map(|b| b as i128) + } + pub fn to_i64(self) -> EvalResult<'tcx, i64> { - self.to_bytes().map(|b| b as i64) + self.to_bytes().map(|b| { + assert_eq!(b as i64 as u128, b); + b as i64 + }) } pub fn to_f32(self) -> EvalResult<'tcx, f32> { @@ -186,7 +209,7 @@ impl PrimValKind { pub fn is_int(self) -> bool { use self::PrimValKind::*; match self { - I8 | I16 | I32 | I64 | U8 | U16 | U32 | U64 => true, + I8 | I16 | I32 | I64 | I128 | U8 | U16 | U32 | U64 | U128 => true, _ => false, } } @@ -197,6 +220,7 @@ impl PrimValKind { 2 => PrimValKind::U16, 4 => PrimValKind::U32, 8 => PrimValKind::U64, + 16 => PrimValKind::U128, _ => bug!("can't make uint with size {}", size), } } @@ -207,6 +231,7 @@ impl PrimValKind { 2 => PrimValKind::I16, 4 => PrimValKind::I32, 8 => PrimValKind::I64, + 16 => PrimValKind::I128, _ => bug!("can't make int with size {}", size), } } From 32cd8efb977b0edec1ddbd1efa2db49dad6faf89 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 15:46:03 +0100 Subject: [PATCH 0725/1096] re-enable auxiliary tests for the host only --- src/bin/miri.rs | 11 ++++++++++- tests/compiletest.rs | 14 +++++++++++--- tests/run-pass/aux_test.rs | 7 +++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 379f25ef7576a..56c2e433d0671 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -21,7 +21,10 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { let mut control = CompileController::basic(); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); - control.after_analysis.stop = Compilation::Stop; + if std::env::var("MIRI_HOST_TARGET") != Ok("yes".to_owned()) { + // only fully compile targets on the host + control.after_analysis.stop = Compilation::Stop; + } control } } @@ -136,6 +139,12 @@ fn main() { args.push(sysroot_flag); args.push(find_sysroot()); } + // we run the optimization passes inside miri + // if we ran them twice we'd get funny failures due to borrowck ElaborateDrops only working on + // unoptimized MIR + // FIXME: add an after-mir-passes hook to rustc driver + args.push("-Zmir-opt-level=0".to_owned()); + // for auxilary builds in unit tests args.push("-Zalways-encode-mir".to_owned()); rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 50970086ee541..90a40d8e39bdf 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,13 +27,20 @@ fn run_pass() { compiletest::run_tests(&config); } -fn miri_pass(path: &str, target: &str) { +fn miri_pass(path: &str, target: &str, host: &str) { let mut config = compiletest::default_config(); config.mode = "mir-opt".parse().expect("Invalid mode"); config.src_base = PathBuf::from(path); config.target = target.to_owned(); config.rustc_path = PathBuf::from("target/debug/miri"); + // don't actually execute the final binary, it might be for other targets and we only care + // about running miri, not the binary. + config.runtool = Some("echo \"\" || ".to_owned()); + if target == host { + std::env::set_var("MIRI_HOST_TARGET", "yes"); + } compiletest::run_tests(&config); + std::env::set_var("MIRI_HOST_TARGET", ""); } fn is_target_dir>(path: P) -> bool { @@ -65,10 +72,11 @@ fn compile_test() { .to_owned(), }; run_pass(); + let host = toolchain.unwrap().splitn(2, '-').skip(1).next().unwrap(); for_all_targets(&sysroot, |target| { - miri_pass("tests/run-pass", &target); + miri_pass("tests/run-pass", &target, host); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - miri_pass(&path, &target); + miri_pass(&path, &target, host); } }); compile_fail(&sysroot); diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index 5510582e87a3f..1b1dbaa68387e 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,9 +1,8 @@ // aux-build:dep.rs +// ignore-cross-compile -// FIXME: Auxiliary builds are currently broken. -// extern crate dep; +extern crate dep; fn main() { - // FIXME: Auxiliary builds are currently broken. - // dep::foo(); + dep::foo(); } From 5d7b92a6e3880326118b2c903529dac96e033573 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 15:59:32 +0100 Subject: [PATCH 0726/1096] fix travis --- tests/compiletest.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 90a40d8e39bdf..2d0e65c4c0073 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -72,7 +72,14 @@ fn compile_test() { .to_owned(), }; run_pass(); - let host = toolchain.unwrap().splitn(2, '-').skip(1).next().unwrap(); + let host = Path::new(&sysroot).file_name() + .unwrap() + .to_str() + .unwrap() + .splitn(2, '-') + .skip(1) + .next() + .unwrap(); for_all_targets(&sysroot, |target| { miri_pass("tests/run-pass", &target, host); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { From 1f40819315fa8df4d609a619e428f1ef75eb46c2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Dec 2016 09:05:46 +0100 Subject: [PATCH 0727/1096] try to pin down the travis failure --- tests/compiletest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 2d0e65c4c0073..09b31b71bbc71 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -73,13 +73,13 @@ fn compile_test() { }; run_pass(); let host = Path::new(&sysroot).file_name() - .unwrap() + .expect("sysroot has no last par") .to_str() - .unwrap() + .expect("sysroot contains non utf8") .splitn(2, '-') .skip(1) .next() - .unwrap(); + .expect("target dir not prefixed"); for_all_targets(&sysroot, |target| { miri_pass("tests/run-pass", &target, host); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { From 2f51310a80900364de3efe875d04cada346b85f5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Dec 2016 09:58:41 +0100 Subject: [PATCH 0728/1096] clamp down on hacks in compiletest --- tests/compiletest.rs | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 09b31b71bbc71..ae55c1adb5c5b 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,9 +3,9 @@ extern crate compiletest_rs as compiletest; use std::path::{PathBuf, Path}; use std::io::Write; -fn compile_fail(sysroot: &str) { - let flags = format!("--sysroot {} -Dwarnings", sysroot); - for_all_targets(sysroot, |target| { +fn compile_fail(sysroot: &Path) { + let flags = format!("--sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path")); + for_all_targets(&sysroot, |target| { let mut config = compiletest::default_config(); config.host_rustcflags = Some(flags.clone()); config.mode = "compile-fail".parse().expect("Invalid mode"); @@ -49,8 +49,10 @@ fn is_target_dir>(path: P) -> bool { path.metadata().map(|m| m.is_dir()).unwrap_or(false) } -fn for_all_targets(sysroot: &str, mut f: F) { - for entry in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() { +fn for_all_targets(sysroot: &Path, mut f: F) { + let target_dir = sysroot.join("lib").join("rustlib"); + println!("target dir: {}", target_dir.to_str().unwrap()); + for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") { let entry = entry.unwrap(); if !is_target_dir(entry.path()) { continue; } let target = entry.file_name().into_string().unwrap(); @@ -62,24 +64,23 @@ fn for_all_targets(sysroot: &str, mut f: F) { #[test] fn compile_test() { - // Taken from https://github.com/Manishearth/rust-clippy/pull/911. - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - let sysroot = match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => option_env!("RUST_SYSROOT") - .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") - .to_owned(), - }; + let sysroot = std::process::Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .expect("rustc not found") + .stdout; + let sysroot = std::str::from_utf8(&sysroot).expect("sysroot is not utf8").trim(); + let sysroot = &Path::new(&sysroot); + let host = std::process::Command::new("rustc") + .arg("-vV") + .output() + .expect("rustc not found for -vV") + .stdout; + let host = std::str::from_utf8(&host).expect("sysroot is not utf8"); + let host = host.split("\nhost: ").skip(1).next().expect("no host: part in rustc -vV"); + let host = host.split("\n").next().expect("no \n after host"); run_pass(); - let host = Path::new(&sysroot).file_name() - .expect("sysroot has no last par") - .to_str() - .expect("sysroot contains non utf8") - .splitn(2, '-') - .skip(1) - .next() - .expect("target dir not prefixed"); for_all_targets(&sysroot, |target| { miri_pass("tests/run-pass", &target, host); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { From 3084aa8052f469024848957809beee20890cb581 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 10 Jan 2017 10:04:53 +0100 Subject: [PATCH 0729/1096] test more targets on travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index e76a2f86cf93d..3aa1b51497bfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH +- rustup target add i686-unknown-linux-gnu +- rustup target add i686-pc-windows-gnu +- rustup target add i686-pc-windows-msvc script: - | env RUST_SYSROOT=$HOME/rust travis-cargo build && From f47aa03f6fce571e43d5a1606c41a1cea37dee50 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 10 Jan 2017 10:45:33 +0100 Subject: [PATCH 0730/1096] analyze travis --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3aa1b51497bfe..ed0363dcc9edf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,10 @@ before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH -- rustup target add i686-unknown-linux-gnu -- rustup target add i686-pc-windows-gnu -- rustup target add i686-pc-windows-msvc +- ls -lh ~/rust +- ~/rust/rustup target add i686-unknown-linux-gnu +- ~/rust/rustup target add i686-pc-windows-gnu +- ~/rust/rustup target add i686-pc-windows-msvc script: - | env RUST_SYSROOT=$HOME/rust travis-cargo build && From 421b537f80d3557d42e7be5f370b65484f08961d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 10 Jan 2017 12:50:27 +0100 Subject: [PATCH 0731/1096] travis fix --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed0363dcc9edf..fefa37b7fec46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,9 @@ before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH -- ls -lh ~/rust -- ~/rust/rustup target add i686-unknown-linux-gnu -- ~/rust/rustup target add i686-pc-windows-gnu -- ~/rust/rustup target add i686-pc-windows-msvc +- sh ~/rust-installer/rustup.sh --add-target=i686-unknown-linux-gnu --prefix=/home/travis/rust -y --disable-sudo +- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-gnu --prefix=/home/travis/rust -y --disable-sudo +- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust p-y --disable-sudo script: - | env RUST_SYSROOT=$HOME/rust travis-cargo build && From ae7d69a5bd5767620c0d48fd81e3fbab18d200de Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 15:47:52 +0100 Subject: [PATCH 0732/1096] msvc has different internals for mutexes and thus fails on a different function --- tests/compile-fail/send-is-not-static-par-for.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-fail/send-is-not-static-par-for.rs b/tests/compile-fail/send-is-not-static-par-for.rs index bee05ecd7fae3..afb401a919e91 100644 --- a/tests/compile-fail/send-is-not-static-par-for.rs +++ b/tests/compile-fail/send-is-not-static-par-for.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//error-pattern: no mir for `std::panicking::panicking` +//error-pattern: no mir for `std:: use std::sync::Mutex; From ccfcc12a580aa27fc8b99c6ec262e82d74629457 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 08:45:09 +0100 Subject: [PATCH 0733/1096] aux tests only run if the host is set --- tests/compiletest.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ae55c1adb5c5b..909538494e86a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -32,6 +32,7 @@ fn miri_pass(path: &str, target: &str, host: &str) { config.mode = "mir-opt".parse().expect("Invalid mode"); config.src_base = PathBuf::from(path); config.target = target.to_owned(); + config.host = host.to_owned(); config.rustc_path = PathBuf::from("target/debug/miri"); // don't actually execute the final binary, it might be for other targets and we only care // about running miri, not the binary. From 7c486416cb68efcefcf216037d1bcaab60e88133 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 08:52:22 +0100 Subject: [PATCH 0734/1096] allow the use of tuple struct constructors as functions --- src/terminator/mod.rs | 28 +++++++++++++++++++ .../run-pass/tuple_like_struct_constructor.rs | 5 ++++ 2 files changed, 33 insertions(+) create mode 100644 tests/run-pass/tuple_like_struct_constructor.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6959da650b426..1b5ff9da9d73f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -229,6 +229,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs, Vec::new()) }; + // FIXME(eddyb) Detect ADT constructors more efficiently. + if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() { + if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { + // technically they can diverge, but only if one of their arguments diverges, so it doesn't matter + let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); + let dest_ty = self.tcx.item_type(adt_def.did); + let dest_layout = self.type_layout(dest_ty)?; + match *dest_layout { + Layout::Univariant { ref variant, .. } => { + assert_eq!(v.disr_val.to_u128_unchecked(), 0); + let offsets = variant.offsets.iter().map(|s| s.bytes()); + + // FIXME: don't allocate for single or dual field structs + let dest = self.force_allocation(lvalue)?.to_ptr(); + + for (offset, (value, value_ty)) in offsets.into_iter().zip(args) { + let field_dest = dest.offset(offset); + self.write_value_to_ptr(value, field_dest, value_ty)?; + } + }, + // FIXME: enum variant constructors + _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), + } + self.goto_block(target); + return Ok(()); + } + } + let mir = self.load_mir(resolved_def_id)?; let (return_lvalue, return_to_block) = match destination { Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), diff --git a/tests/run-pass/tuple_like_struct_constructor.rs b/tests/run-pass/tuple_like_struct_constructor.rs new file mode 100644 index 0000000000000..05e8893de1787 --- /dev/null +++ b/tests/run-pass/tuple_like_struct_constructor.rs @@ -0,0 +1,5 @@ +fn main() { + #[derive(PartialEq, Eq, Debug)] + struct A(i32); + assert_eq!(Some(42).map(A), Some(A(42))); +} From 3ee34381b63a55837cb6c3834b969a281da6877c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 08:53:03 +0100 Subject: [PATCH 0735/1096] remove typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fefa37b7fec46..91e6255f33ccf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_script: export PATH=$HOME/.local/bin:$PATH - sh ~/rust-installer/rustup.sh --add-target=i686-unknown-linux-gnu --prefix=/home/travis/rust -y --disable-sudo - sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-gnu --prefix=/home/travis/rust -y --disable-sudo -- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust p-y --disable-sudo +- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust -y --disable-sudo script: - | env RUST_SYSROOT=$HOME/rust travis-cargo build && From 753dbcf158822199e8ae8c1aee99fd6cb036b623 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:41:36 +0100 Subject: [PATCH 0736/1096] add a test for dereferencing a pointer to a `!` --- src/error.rs | 3 +++ src/terminator/mod.rs | 2 +- tests/compile-fail/never_say_never.rs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/never_say_never.rs diff --git a/src/error.rs b/src/error.rs index bf01ae27d2a28..bf1c71f089dbd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,6 +52,7 @@ pub enum EvalError<'tcx> { ReallocatedFrozenMemory, DeallocatedFrozenMemory, Layout(layout::LayoutError<'tcx>), + Unreachable, } pub type EvalResult<'tcx, T> = Result>; @@ -122,6 +123,8 @@ impl<'tcx> Error for EvalError<'tcx> { "rustc layout computation failed", EvalError::UnterminatedCString(_) => "attempted to get length of a null terminated string, but no null found before end of allocation", + EvalError::Unreachable => + "entered unreachable code", } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1b5ff9da9d73f..1b302523ff2ae 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), - Unreachable => unimplemented!(), + Unreachable => return Err(EvalError::Unreachable), } Ok(()) diff --git a/tests/compile-fail/never_say_never.rs b/tests/compile-fail/never_say_never.rs new file mode 100644 index 0000000000000..5d7e9fec62c24 --- /dev/null +++ b/tests/compile-fail/never_say_never.rs @@ -0,0 +1,12 @@ +#![feature(never_type)] +#![allow(unreachable_code)] + +fn main() { + let y = &5; + let x: ! = unsafe { + *(y as *const _ as *const !) //~ ERROR entered unreachable code + }; + f(x) +} + +fn f(x: !) -> ! { x } From b5f824fd9c34f54961831fdef4107ed31ba45e04 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:59:00 +0100 Subject: [PATCH 0737/1096] fix ICE when transmuting inhabited types to uninhabited --- src/terminator/mod.rs | 5 ++++- tests/compile-fail/never_transmute_humans.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/never_transmute_humans.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1b302523ff2ae..4c02adbd07a9f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -200,7 +200,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Abi::RustIntrinsic => { let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; - let (ret, target) = destination.unwrap(); + let (ret, target) = match destination { + Some(dest) => dest, + None => return Err(EvalError::Unreachable), + }; self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) } diff --git a/tests/compile-fail/never_transmute_humans.rs b/tests/compile-fail/never_transmute_humans.rs new file mode 100644 index 0000000000000..38406eeb3fea6 --- /dev/null +++ b/tests/compile-fail/never_transmute_humans.rs @@ -0,0 +1,14 @@ +#![feature(never_type)] +#![allow(unreachable_code)] +#![allow(unused_variables)] + +struct Human; + +fn main() { + let x: ! = unsafe { + std::mem::transmute::(Human) //~ ERROR entered unreachable code + }; + f(x) +} + +fn f(x: !) -> ! { x } From a58170a4c6286f16805ee474b4814f264b5964a2 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 10:37:14 +0100 Subject: [PATCH 0738/1096] prevent intrinsics from creating uninhabited types --- src/eval_context.rs | 5 +++++ src/terminator/mod.rs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7a0264665e523..fe5beb2e5cfbf 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -10,6 +10,7 @@ use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::fx::FxHashSet; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -1468,3 +1469,7 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty:: let substituted = &f.ty(tcx, substs); tcx.normalize_associated_type(&substituted) } + +pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty() +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4c02adbd07a9f..5cc640142c66a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr}; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty}; +use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited}; use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; use value::PrimVal; @@ -201,8 +201,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { - Some(dest) => dest, - None => return Err(EvalError::Unreachable), + Some(dest) if is_inhabited(self.tcx, ty) => dest, + _ => return Err(EvalError::Unreachable), }; self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; Ok(()) From 548a6baec0312b53832e0b382d904bd1d26b4c1f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 11:23:44 +0100 Subject: [PATCH 0739/1096] also test transmutes to empty enums --- tests/compile-fail/never_transmute_void.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/compile-fail/never_transmute_void.rs diff --git a/tests/compile-fail/never_transmute_void.rs b/tests/compile-fail/never_transmute_void.rs new file mode 100644 index 0000000000000..3fffacc55ea47 --- /dev/null +++ b/tests/compile-fail/never_transmute_void.rs @@ -0,0 +1,16 @@ +#![feature(never_type)] +#![allow(unreachable_code)] +#![allow(unused_variables)] + +enum Void {} + +fn f(v: Void) -> ! { + match v {} +} + +fn main() { + let v: Void = unsafe { + std::mem::transmute::<(), Void>(()) //~ ERROR entered unreachable code + }; + f(v); +} From 0595f9546085ac53e57fe4776687794b0bf2bb7d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 11:27:04 +0100 Subject: [PATCH 0740/1096] remove old comment --- src/terminator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 5cc640142c66a..7107a99e8b5e7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -235,7 +235,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(eddyb) Detect ADT constructors more efficiently. if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() { if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { - // technically they can diverge, but only if one of their arguments diverges, so it doesn't matter let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); let dest_layout = self.type_layout(dest_ty)?; From 24870428a767758de49d20a603d368f68669e54f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 15 Dec 2016 18:27:47 +0100 Subject: [PATCH 0741/1096] more intrinsics --- src/terminator/intrinsic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a91a75454dd93..27d73fb66481d 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -66,6 +66,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "atomic_store" | + "atomic_store_relaxed" | + "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); let dest = arg_vals[0].read_ptr(&self.memory)?; @@ -90,6 +92,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } + "atomic_cxchg_relaxed" | "atomic_cxchg" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; @@ -108,6 +111,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } + "atomic_xadd" | "atomic_xadd_relaxed" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; From 374232c8324c4a5516f7f00d48558fb5775bfe96 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:26:22 +0100 Subject: [PATCH 0742/1096] add memrchr libc function --- src/terminator/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 7107a99e8b5e7..67cd75d4ce2ba 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -405,6 +405,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; } + "memrchr" => { + let ptr = args[0].read_ptr(&self.memory)?; + let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64(); + if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { + let new_ptr = ptr.offset(num - idx as u64 - 1); + self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; + } else { + self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + } + } + "memchr" => { let ptr = args[0].read_ptr(&self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; From 8084d60f5441890742b0ea7a28efc77639ee44c6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 18:08:53 +0100 Subject: [PATCH 0743/1096] add test for unions and remove needles forced alloc --- src/eval_context.rs | 4 -- tests/run-pass/union.rs | 88 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/run-pass/union.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index fe5beb2e5cfbf..3c541608ed1d1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -517,10 +517,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand = &operands[0]; let value = self.eval_operand(operand)?; let value_ty = self.operand_ty(operand); - - // FIXME(solson) - let dest = self.force_allocation(dest)?; - self.write_value(value, dest, value_ty)?; } diff --git a/tests/run-pass/union.rs b/tests/run-pass/union.rs new file mode 100644 index 0000000000000..9e05a89a4ea37 --- /dev/null +++ b/tests/run-pass/union.rs @@ -0,0 +1,88 @@ +#![feature(untagged_unions)] +#![allow(dead_code, unused_variables)] + +fn main() { + a(); + b(); + c(); + d(); +} + +fn a() { + union U { + f1: u32, + f2: f32, + } + let mut u = U { f1: 1 }; + unsafe { + let b1 = &mut u.f1; + *b1 = 5; + } + assert_eq!(unsafe { u.f1 }, 5); +} + +fn b() { + struct S { + x: u32, + y: u32, + } + + union U { + s: S, + both: u64, + } + let mut u = U { s: S { x: 1, y: 2 } }; + unsafe { + let bx = &mut u.s.x; + let by = &mut u.s.y; + *bx = 5; + *by = 10; + } + assert_eq!(unsafe { u.s.x }, 5); + assert_eq!(unsafe { u.s.y }, 10); +} + +fn c() { + #[repr(u32)] + enum Tag { I, F } + + #[repr(C)] + union U { + i: i32, + f: f32, + } + + #[repr(C)] + struct Value { + tag: Tag, + u: U, + } + + fn is_zero(v: Value) -> bool { + unsafe { + match v { + Value { tag: Tag::I, u: U { i: 0 } } => true, + Value { tag: Tag::F, u: U { f: 0.0 } } => true, + _ => false, + } + } + } + assert!(is_zero(Value { tag: Tag::I, u: U { i: 0 }})); + assert!(is_zero(Value { tag: Tag::F, u: U { f: 0.0 }})); + assert!(!is_zero(Value { tag: Tag::I, u: U { i: 1 }})); + assert!(!is_zero(Value { tag: Tag::F, u: U { f: 42.0 }})); +} + +fn d() { + union MyUnion { + f1: u32, + f2: f32, + } + let u = MyUnion { f1: 10 }; + unsafe { + match u { + MyUnion { f1: 10 } => { } + MyUnion { f2 } => { panic!("foo"); } + } + } +} From 927844ab241fe860dfc09733773bc2d35eba9ae4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 11 Jan 2017 10:04:17 +0100 Subject: [PATCH 0744/1096] priroda required functions --- src/eval_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 3c541608ed1d1..be745a805acc5 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -688,7 +688,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((offset, ty)) } - fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { + pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match ty.sty { ty::TyAdt(adt_def, substs) => { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) @@ -1011,7 +1011,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub(super) fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { + pub fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> { use syntax::ast::FloatTy; let kind = match ty.sty { From e7ef11813804a05f11325859145f24297326bd43 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 09:30:18 +0100 Subject: [PATCH 0745/1096] fix copy pasted code --- src/terminator/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 67cd75d4ce2ba..cbf9bbda2cf83 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -407,8 +407,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "memrchr" => { let ptr = args[0].read_ptr(&self.memory)?; - let val = self.value_to_primval(args[1], usize)?.to_u64() as u8; - let num = self.value_to_primval(args[2], usize)?.to_u64(); + let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; + let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1); self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; From 1838ef6bda07ba9f66eb83b1bf9503d1333b7442 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 13 Jan 2017 17:16:19 +0100 Subject: [PATCH 0746/1096] rustup to 2017-01-12 --- src/eval_context.rs | 5 ++--- src/terminator/mod.rs | 6 +++--- src/vtable.rs | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index be745a805acc5..e2ed5459d4590 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -200,9 +200,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), Function(_) => unimplemented!(), - Array(_, _) => unimplemented!(), + Array(_) => unimplemented!(), Repeat(_, _) => unimplemented!(), - Dummy => unimplemented!(), }; Ok(Value::ByVal(primval)) @@ -261,7 +260,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { ty.layout(&infcx).map_err(EvalError::Layout) }) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index cbf9bbda2cf83..c9530f636d2cc 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -462,7 +462,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( @@ -833,7 +833,7 @@ pub(super) fn get_impl_method<'a, 'tcx>( match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { Some(node_item) => { - let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); @@ -870,7 +870,7 @@ pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { Some(node_item) => { - let substs = tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); tcx.lift(&substs).unwrap_or_else(|| { diff --git a/src/vtable.rs b/src/vtable.rs index a2cd2d2729d16..1c589f43175e0 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -183,7 +183,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { debug!("normalize_and_test_predicates(predicates={:?})", predicates); - self.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::dummy(); From ac2bf50f9d853a56ff1a236d456619b2d3b65b64 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 16 Jan 2017 18:45:30 -0800 Subject: [PATCH 0747/1096] Use the new field init shorthand. --- src/eval_context.rs | 22 +++++++++------------- src/lib.rs | 3 ++- src/lvalue.rs | 21 +++++---------------- src/memory.rs | 30 ++++++++++++------------------ src/step.rs | 16 +++------------- src/terminator/mod.rs | 19 ++++--------------- 6 files changed, 35 insertions(+), 76 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e2ed5459d4590..d75f3fa7a6851 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -129,7 +129,7 @@ impl Default for ResourceLimits { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { EvalContext { - tcx: tcx, + tcx, memory: Memory::new(&tcx.data_layout, limits.memory_size), globals: HashMap::new(), stack: Vec::new(), @@ -283,15 +283,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let locals = vec![Value::ByVal(PrimVal::Undef); num_locals]; self.stack.push(Frame { - mir: mir, + mir, block: mir::START_BLOCK, - return_to_block: return_to_block, - return_lvalue: return_lvalue, - locals: locals, + return_to_block, + return_lvalue, + locals, interpreter_temporaries: temporaries, - span: span, - def_id: def_id, - substs: substs, + span, + def_id, + substs, stmt: 0, }); @@ -764,11 +764,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // function items are zero sized Value::ByRef(self.memory.allocate(0, 0)?) } else { - let cid = GlobalId { - def_id: def_id, - substs: substs, - promoted: None, - }; + let cid = GlobalId { def_id, substs, promoted: None }; self.read_lvalue(Lvalue::Global(cid)) } } diff --git a/src/lib.rs b/src/lib.rs index 418ccfa549fef..32925dffd7408 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,10 @@ btree_range, collections, collections_bound, + field_init_shorthand, + i128_type, pub_restricted, rustc_private, - i128_type, )] // From rustc. diff --git a/src/lvalue.rs b/src/lvalue.rs index 966fb65c3cd14..7bd6cc3d989e1 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -63,7 +63,7 @@ pub struct Global<'tcx> { impl<'tcx> Lvalue<'tcx> { pub fn from_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr: ptr, extra: LvalueExtra::None } + Lvalue::Ptr { ptr, extra: LvalueExtra::None } } pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { @@ -101,7 +101,7 @@ impl<'tcx> Global<'tcx> { Global { value: Value::ByVal(PrimVal::Undef), mutable: true, - ty: ty, + ty, } } } @@ -137,22 +137,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - - Local(local) => { - Lvalue::Local { - frame: self.stack.len() - 1, - local: local, - } - } + Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local }, Static(def_id) => { let substs = self.tcx.intern_substs(&[]); - let cid = GlobalId { - def_id: def_id, - substs: substs, - promoted: None, - }; - Lvalue::Global(cid) + Lvalue::Global(GlobalId { def_id, substs, promoted: None }) } Projection(ref proj) => return self.eval_lvalue_projection(proj), @@ -321,7 +310,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - Ok(Lvalue::Ptr { ptr: ptr, extra: extra }) + Ok(Lvalue::Ptr { ptr, extra }) } pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { diff --git a/src/memory.rs b/src/memory.rs index 5be28f27712a4..f3e0703f4c36d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -52,7 +52,7 @@ pub struct Pointer { impl Pointer { pub fn new(alloc_id: AllocId, offset: u64) -> Self { - Pointer { alloc_id: alloc_id, offset: offset } + Pointer { alloc_id, offset } } pub fn signed_offset(self, i: i64) -> Self { @@ -133,7 +133,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { functions: HashMap::new(), function_alloc_cache: HashMap::new(), next_id: AllocId(2), - layout: layout, + layout, memory_size: max_memory, memory_usage: 0, } @@ -151,7 +151,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { sig: fn_ty.sig, }); self.create_fn_alloc(FunctionDefinition { - def_id: def_id, + def_id, substs: substs.substs, abi: fn_ty.abi, // FIXME: why doesn't this compile? @@ -162,8 +162,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { self.create_fn_alloc(FunctionDefinition { - def_id: def_id, - substs: substs, + def_id, + substs, abi: fn_ty.abi, // FIXME: why doesn't this compile? //sig: tcx.erase_late_bound_regions(&fn_ty.sig), @@ -202,7 +202,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: vec![0; size as usize], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), - align: align, + align, immutable: false, }; let id = self.next_id; @@ -423,12 +423,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&[]); } let alloc = self.get(ptr.alloc_id)?; - if ptr.offset + size > alloc.bytes.len() as u64 { - return Err(EvalError::PointerOutOfBounds { - ptr: ptr, - size: size, - allocation_size: alloc.bytes.len() as u64, - }); + let allocation_size = alloc.bytes.len() as u64; + if ptr.offset + size > allocation_size { + return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size }); } assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -441,12 +438,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } let alloc = self.get_mut(ptr.alloc_id)?; - if ptr.offset + size > alloc.bytes.len() as u64 { - return Err(EvalError::PointerOutOfBounds { - ptr: ptr, - size: size, - allocation_size: alloc.bytes.len() as u64, - }); + let allocation_size = alloc.bytes.len() as u64; + if ptr.offset + size > allocation_size { + return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size }); } assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); diff --git a/src/step.rs b/src/step.rs index 109631327b16e..9b4914f6831b1 100644 --- a/src/step.rs +++ b/src/step.rs @@ -47,10 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, - }.visit_statement(block, stmt, mir::Location { - block: block, - statement_index: stmt_id, - }); + }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id }); if new? == 0 { self.statement(stmt)?; } @@ -68,10 +65,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, - }.visit_terminator(block, terminator, mir::Location { - block: block, - statement_index: stmt_id, - }); + }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id }); if new? == 0 { self.terminator(terminator)?; } @@ -153,11 +147,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) { - let cid = GlobalId { - def_id: def_id, - substs: substs, - promoted: None, - }; + let cid = GlobalId { def_id, substs, promoted: None }; if self.ecx.globals.contains_key(&cid) { return; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c9530f636d2cc..d824c16a72f54 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -646,14 +646,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), _ => bug!("invalid fat pointer type: {}", ty), }; - self.drop( - Lvalue::Ptr { - ptr: ptr, - extra: extra, - }, - contents_ty, - drop, - )?; + self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; }, } let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); @@ -771,7 +764,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: this creates a lot of stack frames if the element type has // a drop impl for i in 0..(len as u64) { - self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra: extra }, elem_ty, drop)?; + self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; } }, // FIXME: what about TyClosure and TyAnon? @@ -798,11 +791,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.type_is_sized(field_ty) { self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; } else { - let lvalue = Lvalue::Ptr { - ptr: ptr, - extra: extra, - }; - self.drop(lvalue, field_ty, drop)?; + self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?; break; // if it is not sized, then this is the last field anyway } } @@ -845,7 +834,7 @@ pub(super) fn get_impl_method<'a, 'tcx>( }); ImplMethod { method: node_item.item, - substs: substs, + substs, is_provided: node_item.node.is_from_trait(), } } From 53fa985fc4eac40dfef71d344b62bd243499dff1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 16 Jan 2017 19:37:53 -0800 Subject: [PATCH 0748/1096] Update for changes in rustc. --- src/memory.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index f3e0703f4c36d..79a1af50ad1f2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,4 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; -use std::collections::Bound::{Included, Excluded}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr, mem, io}; @@ -710,7 +709,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { { let start = ptr.offset.saturating_sub(self.pointer_size() - 1); let end = ptr.offset + size; - Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) + Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) } fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { From 684f75713963f0ca67fcce2b13746e982f203cee Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sun, 22 Jan 2017 00:19:35 -0800 Subject: [PATCH 0749/1096] Make clippy-suggested fixes. --- src/error.rs | 2 +- src/eval_context.rs | 4 ++-- src/lib.rs | 1 - src/terminator/mod.rs | 10 +++++----- src/vtable.rs | 22 +++++++++++----------- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index bf1c71f089dbd..c641fc24db52e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -153,7 +153,7 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", has, required), - EvalError::TypeNotPrimitive(ref ty) => + EvalError::TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty), EvalError::Layout(ref err) => write!(f, "rustc layout computation failed: {:?}", err), diff --git a/src/eval_context.rs b/src/eval_context.rs index d75f3fa7a6851..e02ea09be861f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::None => {}, } // deallocate all locals that are backed by an allocation - for local in frame.locals.into_iter() { + for local in frame.locals { if let Value::ByRef(ptr) = local { trace!("deallocating local"); self.memory.dump_alloc(ptr.alloc_id); @@ -1457,7 +1457,7 @@ impl IntegerExt for layout::Integer { pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty::FieldDef, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { - let substituted = &f.ty(tcx, substs); + let substituted = f.ty(tcx, substs); tcx.normalize_associated_type(&substituted) } diff --git a/src/lib.rs b/src/lib.rs index 32925dffd7408..47ed4fd0cebfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![feature( btree_range, collections, - collections_bound, field_init_shorthand, i128_type, pub_restricted, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d824c16a72f54..11082c633a755 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -579,7 +579,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } traits::VtableFnPointer(vtable_fn_ptr) => { - if let ty::TyFnDef(did, ref substs, _) = vtable_fn_ptr.fn_ty.sty { + if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty { args.remove(0); self.unpack_fn_args(args)?; Ok((did, substs, Vec::new())) @@ -775,14 +775,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn drop_fields< - I: Iterator, ty::layout::Size)>, - >( + fn drop_fields( &mut self, mut fields: I, lval: Lvalue<'tcx>, drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx, ()> + where I: Iterator, ty::layout::Size)>, + { // FIXME: some aggregates may be represented by Value::ByValPair let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); // manual iteration, because we need to be careful about the last field if it is unsized diff --git a/src/vtable.rs b/src/vtable.rs index 1c589f43175e0..8e2607562b411 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -27,11 +27,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableBuiltin(_) => { Vec::new().into_iter() } - traits::VtableImpl( - traits::VtableImplData { - impl_def_id: id, - substs, - nested: _ }) => { + + traits::VtableImpl(traits::VtableImplData { impl_def_id: id, substs, .. }) => { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { @@ -46,18 +43,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect::>() .into_iter() } + traits::VtableClosure( traits::VtableClosureData { closure_def_id, substs, - nested: _ }) => { + .. + } + ) => { let closure_type = self.tcx.closure_type(closure_def_id, substs); vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter() } - traits::VtableFnPointer( - traits::VtableFnPointerData { - fn_ty, - nested: _ }) => { + + traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { match fn_ty.sty { ty::TyFnDef(did, substs, bare_fn_ty) => { vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter() @@ -65,6 +63,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), } } + traits::VtableObject(ref data) => { // this would imply that the Self type being erased is // an object type; this cannot happen because we @@ -72,6 +71,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { bug!("cannot get vtable for an object type: {:?}", data); } + vtable @ traits::VtableParam(..) => { bug!("resolved vtable for {:?} to bad vtable {:?} in trans", trait_ref, @@ -100,7 +100,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } self.memory.write_usize(vtable.offset(ptr_size), size)?; - self.memory.write_usize(vtable.offset((ptr_size * 2)), align)?; + self.memory.write_usize(vtable.offset(ptr_size * 2), align)?; for (i, method) in methods.into_iter().enumerate() { if let Some(method) = method { From 04eadedb2888d96c5f5222454a6ca5470e4c0834 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 17:26:47 +0100 Subject: [PATCH 0750/1096] allow using tuple variant names as function handles --- src/eval_context.rs | 70 +++++++++++++++---- src/terminator/mod.rs | 27 ++++--- .../tuple_like_enum_variant_constructor.rs | 3 + 3 files changed, 75 insertions(+), 25 deletions(-) create mode 100644 tests/run-pass/tuple_like_enum_variant_constructor.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index e02ea09be861f..6eb9b3a1e56d9 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -355,18 +355,44 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn assign_fields>( + pub fn assign_discr_and_fields< + I: IntoIterator, + V: IntoValTyPair<'tcx>, + J: IntoIterator, + >( &mut self, dest: Lvalue<'tcx>, offsets: I, - operands: &[mir::Operand<'tcx>], + operands: J, + discr_val: u128, + discr_size: u64, + ) -> EvalResult<'tcx, ()> { + // FIXME(solson) + let dest_ptr = self.force_allocation(dest)?.to_ptr(); + + let mut offsets = offsets.into_iter(); + let discr_offset = offsets.next().unwrap(); + let discr_dest = dest_ptr.offset(discr_offset); + self.memory.write_uint(discr_dest, discr_val, discr_size)?; + + self.assign_fields(dest, offsets, operands) + } + + pub fn assign_fields< + I: IntoIterator, + V: IntoValTyPair<'tcx>, + J: IntoIterator, + >( + &mut self, + dest: Lvalue<'tcx>, + offsets: I, + operands: J, ) -> EvalResult<'tcx, ()> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); for (offset, operand) in offsets.into_iter().zip(operands) { - let value = self.eval_operand(operand)?; - let value_ty = self.operand_ty(operand); + let (value, value_ty) = operand.into_val_ty_pair(self)?; let field_dest = dest.offset(offset); self.write_value_to_ptr(value, field_dest, value_ty)?; } @@ -431,18 +457,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked(); let discr_size = discr.size().bytes(); - let discr_offset = variants[variant].offsets[0].bytes(); - - // FIXME(solson) - let dest = self.force_allocation(dest)?; - let discr_dest = (dest.to_ptr()).offset(discr_offset); - self.memory.write_uint(discr_dest, discr_val, discr_size)?; - - // Don't include the first offset; it's for the discriminant. - let field_offsets = variants[variant].offsets.iter().skip(1) - .map(|s| s.bytes()); - self.assign_fields(dest, field_offsets, operands)?; + self.assign_discr_and_fields( + dest, + variants[variant].offsets.iter().cloned().map(Size::bytes), + operands, + discr_val, + discr_size, + )?; } else { bug!("tried to assign {:?} to Layout::General", kind); } @@ -1464,3 +1486,21 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty:: pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty() } + +pub trait IntoValTyPair<'tcx> { + fn into_val_ty_pair<'a>(self, ecx: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a; +} + +impl<'tcx> IntoValTyPair<'tcx> for (Value, Ty<'tcx>) { + fn into_val_ty_pair<'a>(self, _: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a { + Ok(self) + } +} + +impl<'b, 'tcx: 'b> IntoValTyPair<'tcx> for &'b mir::Operand<'tcx> { + fn into_val_ty_pair<'a>(self, ecx: &mut EvalContext<'a, 'tcx>) -> EvalResult<'tcx, (Value, Ty<'tcx>)> where 'tcx: 'a { + let value = ecx.eval_operand(self)?; + let value_ty = ecx.operand_ty(self); + Ok((value, value_ty)) + } +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 11082c633a755..bd1c85accd588 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,7 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; -use rustc::ty::layout::Layout; +use rustc::ty::layout::{Layout, Size}; use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use syntax::codemap::{DUMMY_SP, Span}; @@ -238,20 +238,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); let dest_layout = self.type_layout(dest_ty)?; + let disr = v.disr_val.to_u128_unchecked(); match *dest_layout { Layout::Univariant { ref variant, .. } => { - assert_eq!(v.disr_val.to_u128_unchecked(), 0); + assert_eq!(disr, 0); let offsets = variant.offsets.iter().map(|s| s.bytes()); - // FIXME: don't allocate for single or dual field structs - let dest = self.force_allocation(lvalue)?.to_ptr(); - - for (offset, (value, value_ty)) in offsets.into_iter().zip(args) { - let field_dest = dest.offset(offset); - self.write_value_to_ptr(value, field_dest, value_ty)?; - } + self.assign_fields(lvalue, offsets, args)?; + }, + Layout::General { discr, ref variants, .. } => { + // FIXME: report a proper error for invalid discriminants + // right now we simply go into index out of bounds + let discr_size = discr.size().bytes(); + self.assign_discr_and_fields( + lvalue, + variants[disr as usize].offsets.iter().cloned().map(Size::bytes), + args, + disr, + discr_size, + )?; }, - // FIXME: enum variant constructors + // FIXME: raw nullable pointer constructors _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), } self.goto_block(target); diff --git a/tests/run-pass/tuple_like_enum_variant_constructor.rs b/tests/run-pass/tuple_like_enum_variant_constructor.rs new file mode 100644 index 0000000000000..5cf91b3f4d194 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor.rs @@ -0,0 +1,3 @@ +fn main() { + assert_eq!(Some(42).map(Some), Some(Some(42))); +} From e22cceaceb6210d65c618fdc747d40253eadee86 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Dec 2016 17:49:51 +0100 Subject: [PATCH 0751/1096] allow using tuple variant names as function handles in presence of NonZero optimizations --- src/terminator/mod.rs | 7 ++++++- ...tuple_like_enum_variant_constructor_pointer_opt.rs | 4 ++++ ...ike_enum_variant_constructor_struct_pointer_opt.rs | 11 +++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs create mode 100644 tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index bd1c85accd588..bd93bd53a497d 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -258,7 +258,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { discr_size, )?; }, - // FIXME: raw nullable pointer constructors + Layout::StructWrappedNullablePointer { .. } | + Layout::RawNullablePointer { .. } => { + assert_eq!(args.len(), 1); + let (val, ty) = args.pop().unwrap(); + self.write_value(val, lvalue, ty)?; + }, _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), } self.goto_block(target); diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs new file mode 100644 index 0000000000000..fb57d4f4c1652 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor_pointer_opt.rs @@ -0,0 +1,4 @@ +fn main() { + let x = 5; + assert_eq!(Some(&x).map(Some), Some(Some(&x))); +} diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs new file mode 100644 index 0000000000000..e61e4af5753a0 --- /dev/null +++ b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs @@ -0,0 +1,11 @@ +#[derive(Copy, Clone, PartialEq, Debug)] +struct A<'a> { + x: i32, + y: &'a i32, +} + +fn main() { + let x = 5; + let a = A { x: 99, y: &x }; + assert_eq!(Some(a).map(Some), Some(Some(a))); +} From 5adb84645fe1f734acadb9a26422a791a56edef0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 24 Jan 2017 13:28:36 +0100 Subject: [PATCH 0752/1096] add cargo-miri subcommand to directly interpret the main binary of a crate --- Cargo.lock | 99 +++++++++++++++++++++++ Cargo.toml | 6 ++ src/bin/cargo-miri.rs | 177 ++++++++++++++++++++++++++++++++++++++++++ src/bin/miri.rs | 46 +++++++++-- 4 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/bin/cargo-miri.rs diff --git a/Cargo.lock b/Cargo.lock index 94611529c5d13..afe24d684725b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)", + "cargo_metadata 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -22,6 +23,16 @@ name = "byteorder" version = "1.0.0" source = "git+https://github.com/quininer/byteorder.git?branch=i128#ef51df297aa833d0b6639aae328a95597fc07d75" +[[package]] +name = "cargo_metadata" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "compiletest_rs" version = "0.2.5" @@ -31,6 +42,11 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dtoa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "env_logger" version = "0.3.5" @@ -40,6 +56,11 @@ dependencies = [ "regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -80,6 +101,16 @@ dependencies = [ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-traits" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "regex" version = "0.1.77" @@ -102,6 +133,57 @@ name = "rustc-serialize" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "0.9.0-rc3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_codegen" +version = "0.9.0-rc3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_codegen_internals" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "0.9.0-rc3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_codegen 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "0.9.0-rc2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread-id" version = "2.0.0" @@ -119,6 +201,11 @@ dependencies = [ "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unicode-xid" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" version = "0.1.3" @@ -137,19 +224,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)" = "" +"checksum cargo_metadata 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb382367db7c8eb427e622e46b99eff500fb63d8cf22dc2df6bcc5587112a993" "checksum compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f344389765ad7bec166f64c1b39ed6dd2b54d81c4c5dd8af789169351d380c" +"checksum dtoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80e5dc7a4b2bbf348fb0afe68b3994daf1126223d2d9770221b8213c5e4565af" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" +"checksum itoa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8f9e7653c775f2ef8016f4181eb3ad62fe8a710e5dd73d4060a5903a58022f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" "checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +"checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" +"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" "checksum regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)" = "64b03446c466d35b42f2a8b203c8e03ed8b91c0f17b56e1f84f7210a257aa665" "checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd" "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" +"checksum serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfeedfddd5db4465d96959431d7f3d8d618a6052cdaf3fddb2e981e86a7ad04c" +"checksum serde_codegen 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "c89a070576ea7af4c609e72fcdd3d283e9c4c77946bd3fd7a07c43ee15b9c144" +"checksum serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806" +"checksum serde_derive 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "1651978181e36fc90e1faaf91ae21fe74ffba77bc4ce4baf18b20fbb00e24cd4" +"checksum serde_json 0.9.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)" = "6efad3dc934e5032a92ea163adb13c8414359da950a0f304c1897214f28d9444" +"checksum syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)" = "17134635792e6a2361f53efbee798701796d8b5842c1c21b7cdb875e2950c8fc" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" +"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index ada46a794cb59..ba7e38bed85b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,11 @@ doc = false name = "miri" test = false +[[bin]] +doc = false +name = "cargo-miri" +test = false + [lib] test = false @@ -20,6 +25,7 @@ byteorder = { git = "https://github.com/quininer/byteorder.git", branch = "i128" env_logger = "0.3.3" log = "0.3.6" log_settings = "0.1.1" +cargo_metadata = "0.1" [dev-dependencies] compiletest_rs = "0.2.5" diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs new file mode 100644 index 0000000000000..71026d828a1ac --- /dev/null +++ b/src/bin/cargo-miri.rs @@ -0,0 +1,177 @@ +#![feature(static_in_const)] + +extern crate cargo_metadata; + +use std::path::{PathBuf, Path}; +use std::io::Write; +use std::process::Command; + + +const CARGO_MIRI_HELP: &str = r#"Interprets bin crates + +Usage: + cargo miri [options] [--] [...] + +Common options: + -h, --help Print this message + --features Features to compile for the package + -V, --version Print version info and exit + +Other options are the same as `cargo rustc`. + +The feature `cargo-miri` is automatically defined for convenience. You can use +it to configure the resource limits + + #![cfg_attr(feature = "cargo-miri", memory_size = 42)] + +available resource limits are `memory_size`, `step_limit`, `stack_limit` +"#; + +fn show_help() { + println!("{}", CARGO_MIRI_HELP); +} + +fn show_version() { + println!("{}", env!("CARGO_PKG_VERSION")); +} + +fn main() { + // Check for version and help flags even when invoked as 'cargo-miri' + if std::env::args().any(|a| a == "--help" || a == "-h") { + show_help(); + return; + } + if std::env::args().any(|a| a == "--version" || a == "-V") { + show_version(); + return; + } + + let dep_path = std::env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps"); + + if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) { + // this arm is when `cargo miri` is called + + let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path=")); + + let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) { + metadata + } else { + let _ = std::io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata.")); + std::process::exit(101); + }; + + let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))); + + let current_dir = std::env::current_dir(); + + let package_index = metadata.packages + .iter() + .position(|package| { + let package_manifest_path = Path::new(&package.manifest_path); + if let Some(ref manifest_path) = manifest_path { + package_manifest_path == manifest_path + } else { + let current_dir = current_dir.as_ref().expect("could not read current directory"); + let package_manifest_directory = package_manifest_path.parent() + .expect("could not find parent directory of package manifest"); + package_manifest_directory == current_dir + } + }) + .expect("could not find matching package"); + let package = metadata.packages.remove(package_index); + for target in package.targets { + let args = std::env::args().skip(2); + if let Some("bin") = target.kind.get(0).map(AsRef::as_ref) { + if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), + &dep_path) { + std::process::exit(code); + } + } else { + panic!("badly formatted cargo metadata: target::kind is an empty array"); + } + } + } else { + // this arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself + + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) { + format!("{}/toolchains/{}", home, toolchain) + } else { + option_env!("SYSROOT") + .map(|s| s.to_owned()) + .or_else(|| { + Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .ok() + .and_then(|out| String::from_utf8(out.stdout).ok()) + .map(|s| s.trim().to_owned()) + }) + .expect("need to specify SYSROOT env var during miri compilation, or use rustup or multirust") + }; + + // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly + // without having to pass --sysroot or anything + let mut args: Vec = if std::env::args().any(|s| s == "--sysroot") { + std::env::args().skip(1).collect() + } else { + std::env::args().skip(1).chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect() + }; + + // this check ensures that dependencies are built but not interpreted and the final crate is + // interpreted but not built + let miri_enabled = std::env::args().any(|s| s == "-Zno-trans"); + + if miri_enabled { + args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]); + } + + let mut path = std::env::current_exe().expect("current executable path invalid"); + path.set_file_name("miri"); + + match Command::new(path).args(&args).status() { + Ok(exit) => if !exit.success() { + std::process::exit(exit.code().unwrap_or(42)); + }, + Err(e) => panic!("error during miri run: {:?}", e), + } + } +} + +fn process(old_args: I, dep_path: P) -> Result<(), i32> + where P: AsRef, + I: Iterator +{ + let mut args = vec!["rustc".to_owned()]; + + let mut found_dashes = false; + for arg in old_args { + found_dashes |= arg == "--"; + args.push(arg); + } + if !found_dashes { + args.push("--".to_owned()); + } + args.push("-L".to_owned()); + args.push(dep_path.as_ref().to_string_lossy().into_owned()); + args.push("-Zno-trans".to_owned()); + args.push("--cfg".to_owned()); + args.push(r#"feature="cargo-miri""#.to_owned()); + + let path = std::env::current_exe().expect("current executable path invalid"); + let exit_status = std::process::Command::new("cargo") + .args(&args) + .env("RUSTC", path) + .spawn() + .expect("could not run cargo") + .wait() + .expect("failed to wait for cargo?"); + + if exit_status.success() { + Ok(()) + } else { + Err(exit_status.code().unwrap_or(-1)) + } +} diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 56c2e433d0671..e36b9baa64bbf 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -4,21 +4,55 @@ extern crate getopts; extern crate miri; extern crate rustc; extern crate rustc_driver; +extern crate rustc_errors; extern crate env_logger; extern crate log_settings; extern crate syntax; #[macro_use] extern crate log; use rustc::session::Session; -use rustc_driver::{Compilation, CompilerCalls}; +use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls}; use rustc_driver::driver::{CompileState, CompileController}; -use syntax::ast::{MetaItemKind, NestedMetaItemKind}; +use rustc::session::config::{self, Input, ErrorOutputType}; +use syntax::ast::{MetaItemKind, NestedMetaItemKind, self}; +use std::path::PathBuf; -struct MiriCompilerCalls; +struct MiriCompilerCalls(RustcDefaultCalls); impl<'a> CompilerCalls<'a> for MiriCompilerCalls { - fn build_controller(&mut self, _: &Session, _: &getopts::Matches) -> CompileController<'a> { - let mut control = CompileController::basic(); + fn early_callback( + &mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + descriptions: &rustc_errors::registry::Registry, + output: ErrorOutputType + ) -> Compilation { + self.0.early_callback(matches, sopts, cfg, descriptions, output) + } + fn no_input( + &mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + odir: &Option, + ofile: &Option, + descriptions: &rustc_errors::registry::Registry + ) -> Option<(Input, Option)> { + self.0.no_input(matches, sopts, cfg, odir, ofile, descriptions) + } + fn late_callback( + &mut self, + matches: &getopts::Matches, + sess: &Session, + input: &Input, + odir: &Option, + ofile: &Option + ) -> Compilation { + self.0.late_callback(matches, sess, input, odir, ofile) + } + fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> CompileController<'a> { + let mut control = self.0.build_controller(sess, matches); control.after_hir_lowering.callback = Box::new(after_hir_lowering); control.after_analysis.callback = Box::new(after_analysis); if std::env::var("MIRI_HOST_TARGET") != Ok("yes".to_owned()) { @@ -147,5 +181,5 @@ fn main() { // for auxilary builds in unit tests args.push("-Zalways-encode-mir".to_owned()); - rustc_driver::run_compiler(&args, &mut MiriCompilerCalls, None, None); + rustc_driver::run_compiler(&args, &mut MiriCompilerCalls(RustcDefaultCalls), None, None); } From 2cca377bd485ef56ac85c09ebeb9aa5907bfe1f7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 24 Jan 2017 15:33:51 +0100 Subject: [PATCH 0753/1096] add `cargo miri test` sub sub command --- src/bin/cargo-miri.rs | 22 ++++++++++++++++------ src/bin/miri.rs | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs index 71026d828a1ac..5e059d6f2b0d5 100644 --- a/src/bin/cargo-miri.rs +++ b/src/bin/cargo-miri.rs @@ -51,7 +51,10 @@ fn main() { if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) { // this arm is when `cargo miri` is called - let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path=")); + let test = std::env::args().nth(2).map_or(false, |text| text == "test"); + let skip = if test { 3 } else { 2 }; + + let manifest_path_arg = std::env::args().skip(skip).find(|val| val.starts_with("--manifest-path=")); let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) { metadata @@ -80,14 +83,21 @@ fn main() { .expect("could not find matching package"); let package = metadata.packages.remove(package_index); for target in package.targets { - let args = std::env::args().skip(2); - if let Some("bin") = target.kind.get(0).map(AsRef::as_ref) { - if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), + let args = std::env::args().skip(skip); + if test && target.kind.get(0).map_or(false, |kind| kind == "test") { + if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args), &dep_path) { std::process::exit(code); } - } else { - panic!("badly formatted cargo metadata: target::kind is an empty array"); + } else if !test { + if target.kind.get(0).map_or(false, |kind| kind == "bin") { + if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), + &dep_path) { + std::process::exit(code); + } + } else { + panic!("badly formatted cargo metadata: target::kind is an empty array"); + } } } } else { diff --git a/src/bin/miri.rs b/src/bin/miri.rs index e36b9baa64bbf..0f8bb68549b56 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -14,6 +14,8 @@ use rustc::session::Session; use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls}; use rustc_driver::driver::{CompileState, CompileController}; use rustc::session::config::{self, Input, ErrorOutputType}; +use rustc::hir::{self, itemlikevisit}; +use rustc::ty::TyCtxt; use syntax::ast::{MetaItemKind, NestedMetaItemKind, self}; use std::path::PathBuf; @@ -68,19 +70,39 @@ fn after_hir_lowering(state: &mut CompileState) { state.session.plugin_attributes.borrow_mut().push(attr); } -fn after_analysis(state: &mut CompileState) { +fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { - let entry_def_id = tcx.map.local_def_id(entry_node_id); - let limits = resource_limits_from_attributes(state); - miri::run_mir_passes(tcx); - miri::eval_main(tcx, entry_def_id, limits); - - state.session.abort_if_errors(); + miri::run_mir_passes(tcx); + let limits = resource_limits_from_attributes(state); + + if std::env::args().any(|arg| arg == "--test") { + struct Visitor<'a, 'tcx: 'a>(miri::ResourceLimits, TyCtxt<'a, 'tcx, 'tcx>, &'a CompileState<'a, 'tcx>); + impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> { + fn visit_item(&mut self, i: &'hir hir::Item) { + if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { + if i.attrs.iter().any(|attr| attr.value.name == "test") { + let did = self.1.map.body_owner_def_id(body_id); + println!("running test: {}", self.1.map.def_path(did).to_string(self.1)); + miri::eval_main(self.1, did, self.0); + self.2.session.abort_if_errors(); + } + } + } + fn visit_trait_item(&mut self, _trait_item: &'hir hir::TraitItem) {} + fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {} + } + state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); } else { - println!("no main function found, assuming auxiliary build"); + if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { + let entry_def_id = tcx.map.local_def_id(entry_node_id); + miri::eval_main(tcx, entry_def_id, limits); + + state.session.abort_if_errors(); + } else { + println!("no main function found, assuming auxiliary build"); + } } } From 17090b85245039c663b79b17c477c8835880953c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 24 Jan 2017 17:55:42 +0100 Subject: [PATCH 0754/1096] add tests for cargo miri and run them on travis --- .gitignore | 2 +- .travis.yml | 7 ++++++- cargo-miri-test/Cargo.lock | 4 ++++ cargo-miri-test/Cargo.toml | 6 ++++++ cargo-miri-test/src/main.rs | 3 +++ cargo-miri-test/tests/foo.rs | 4 ++++ src/bin/cargo-miri.rs | 15 ++++++--------- 7 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 cargo-miri-test/Cargo.lock create mode 100644 cargo-miri-test/Cargo.toml create mode 100644 cargo-miri-test/src/main.rs create mode 100644 cargo-miri-test/tests/foo.rs diff --git a/.gitignore b/.gitignore index a51553a8c5e22..d32d9eb99afd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/target +target /doc tex/*/out *.dot diff --git a/.travis.yml b/.travis.yml index 91e6255f33ccf..52717dfc0dbfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,12 @@ before_script: script: - | env RUST_SYSROOT=$HOME/rust travis-cargo build && - env RUST_SYSROOT=$HOME/rust travis-cargo test + env RUST_SYSROOT=$HOME/rust travis-cargo test && + env RUST_SYSROOT=$HOME/rust travis-cargo install && + cd cargo-miri-test && + env RUST_SYSROOT=$HOME/rust travis-cargo miri && + env RUST_SYSROOT=$HOME/rust travis-cargo miri test && + cd .. notifications: email: on_success: never diff --git a/cargo-miri-test/Cargo.lock b/cargo-miri-test/Cargo.lock new file mode 100644 index 0000000000000..a62bb86226cdb --- /dev/null +++ b/cargo-miri-test/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "cargo-miri-test" +version = "0.1.0" + diff --git a/cargo-miri-test/Cargo.toml b/cargo-miri-test/Cargo.toml new file mode 100644 index 0000000000000..29886d99a394b --- /dev/null +++ b/cargo-miri-test/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cargo-miri-test" +version = "0.1.0" +authors = ["Oliver Schneider "] + +[dependencies] diff --git a/cargo-miri-test/src/main.rs b/cargo-miri-test/src/main.rs new file mode 100644 index 0000000000000..aa00ab84cb096 --- /dev/null +++ b/cargo-miri-test/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + assert_eq!(5, 5); +} diff --git a/cargo-miri-test/tests/foo.rs b/cargo-miri-test/tests/foo.rs new file mode 100644 index 0000000000000..fb7fad21c9db8 --- /dev/null +++ b/cargo-miri-test/tests/foo.rs @@ -0,0 +1,4 @@ +#[test] +fn bar() { + assert_eq!(4, 4); +} diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs index 5e059d6f2b0d5..58109ad59587d 100644 --- a/src/bin/cargo-miri.rs +++ b/src/bin/cargo-miri.rs @@ -84,19 +84,16 @@ fn main() { let package = metadata.packages.remove(package_index); for target in package.targets { let args = std::env::args().skip(skip); - if test && target.kind.get(0).map_or(false, |kind| kind == "test") { + let kind = target.kind.get(0).expect("badly formatted cargo metadata: target::kind is an empty array"); + if test && kind == "test" { if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args), &dep_path) { std::process::exit(code); } - } else if !test { - if target.kind.get(0).map_or(false, |kind| kind == "bin") { - if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), - &dep_path) { - std::process::exit(code); - } - } else { - panic!("badly formatted cargo metadata: target::kind is an empty array"); + } else if !test && kind == "bin" { + if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), + &dep_path) { + std::process::exit(code); } } } From 8f7e49230552cc1e7d160f14023363d77e8be2bd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 28 Jan 2017 15:27:20 +0100 Subject: [PATCH 0755/1096] drive-by rust update --- src/bin/miri.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 56c2e433d0671..231ceddc4699d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -7,7 +7,7 @@ extern crate rustc_driver; extern crate env_logger; extern crate log_settings; extern crate syntax; -#[macro_use] extern crate log; +extern crate log; use rustc::session::Session; use rustc_driver::{Compilation, CompilerCalls}; From b6e79dbbf53ff5c5e2fd65445bddc8e033df24e0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 28 Jan 2017 15:28:24 +0100 Subject: [PATCH 0756/1096] fix some ICEs --- src/eval_context.rs | 5 ++++- ...e_enum_variant_constructor_struct_pointer_opt.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 6eb9b3a1e56d9..10d0217b4c774 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -223,7 +223,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { - let substituted = ty.subst(self.tcx, substs); + // miri doesn't care about lifetimes, and will choke on some crazy ones + // let's simply get rid of them + let without_lifetimes = self.tcx.erase_regions(&ty); + let substituted = without_lifetimes.subst(self.tcx, substs); self.tcx.normalize_associated_type(&substituted) } diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs index e61e4af5753a0..059c4b972eee9 100644 --- a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs +++ b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs @@ -4,8 +4,21 @@ struct A<'a> { y: &'a i32, } +#[derive(Copy, Clone, PartialEq, Debug)] +struct B<'a>(i32, &'a i32); + fn main() { let x = 5; let a = A { x: 99, y: &x }; assert_eq!(Some(a).map(Some), Some(Some(a))); + let f = B; + assert_eq!(Some(B(42, &x)), Some(f(42, &x))); + // the following doesn't compile :( + //let f: for<'a> fn(i32, &'a i32) -> B<'a> = B; + //assert_eq!(Some(B(42, &x)), Some(f(42, &x))); + assert_eq!(B(42, &x), foo(&x, B)); +} + +fn foo<'a, F: Fn(i32, &'a i32) -> B<'a>>(i: &'a i32, f: F) -> B<'a> { + f(42, i) } From ce95ae592742517df40498b288938570c877e706 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 28 Jan 2017 15:46:46 +0100 Subject: [PATCH 0757/1096] correctly implement pointers to enum variant constructors --- src/eval_context.rs | 19 ++++++---- src/terminator/mod.rs | 36 ++++++++++++++----- ..._variant_constructor_struct_pointer_opt.rs | 9 +++++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 10d0217b4c774..ce44e34f9ed9e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -685,22 +685,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let path = discrfield.iter().skip(2).map(|&i| i as usize); // Handle the field index for the outer non-null variant. - let inner_ty = match ty.sty { + let (inner_offset, inner_ty) = match ty.sty { ty::TyAdt(adt_def, substs) => { let variant = &adt_def.variants[nndiscr as usize]; let index = discrfield[1]; let field = &variant.fields[index as usize]; - field.ty(self.tcx, substs) + (self.get_field_offset(ty, index as usize)?, field.ty(self.tcx, substs)) } _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), }; - self.field_path_offset_and_ty(inner_ty, path) + self.field_path_offset_and_ty(inner_offset, inner_ty, path) } - fn field_path_offset_and_ty>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { - let mut offset = Size::from_bytes(0); - + fn field_path_offset_and_ty>( + &self, + mut offset: Size, + mut ty: Ty<'tcx>, + path: I, + ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { // Skip the initial 0 intended for LLVM GEP. for field_index in path { let field_offset = self.get_field_offset(ty, field_index)?; @@ -747,6 +750,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let bytes = field_index as u64 * self.memory.pointer_size(); Ok(Size::from_bytes(bytes)) } + StructWrappedNullablePointer { ref nonnull, .. } => { + Ok(nonnull.offsets[field_index]) + } _ => { let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); Err(EvalError::Unimplemented(msg)) @@ -761,6 +767,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *layout { Univariant { ref variant, .. } => Ok(variant.offsets.len()), FatPointer { .. } => Ok(2), + StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()), _ => { let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); Err(EvalError::Unimplemented(msg)) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index bd93bd53a497d..b2124486f4918 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -238,27 +238,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); let dest_layout = self.type_layout(dest_ty)?; - let disr = v.disr_val.to_u128_unchecked(); + trace!("layout({:?}) = {:#?}", dest_ty, dest_layout); match *dest_layout { Layout::Univariant { ref variant, .. } => { - assert_eq!(disr, 0); + let disr_val = v.disr_val.to_u128_unchecked(); + assert_eq!(disr_val, 0); let offsets = variant.offsets.iter().map(|s| s.bytes()); self.assign_fields(lvalue, offsets, args)?; }, Layout::General { discr, ref variants, .. } => { - // FIXME: report a proper error for invalid discriminants - // right now we simply go into index out of bounds + let disr_val = v.disr_val.to_u128_unchecked(); let discr_size = discr.size().bytes(); self.assign_discr_and_fields( lvalue, - variants[disr as usize].offsets.iter().cloned().map(Size::bytes), + variants[disr_val as usize].offsets.iter().cloned().map(Size::bytes), args, - disr, + disr_val, discr_size, )?; }, - Layout::StructWrappedNullablePointer { .. } | + Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { + let disr_val = v.disr_val.to_u128_unchecked(); + if nndiscr as u128 == disr_val { + let offsets = nonnull.offsets.iter().map(|s| s.bytes()); + self.assign_fields(lvalue, offsets, args)?; + } else { + for (_, ty) in args { + assert_eq!(self.type_size(ty)?, Some(0)); + } + let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; + + // FIXME(solson) + let dest = self.force_allocation(lvalue)?.to_ptr(); + + let dest = dest.offset(offset.bytes()); + let dest_size = self.type_size(ty)? + .expect("bad StructWrappedNullablePointer discrfield"); + self.memory.write_int(dest, 0, dest_size)?; + } + }, Layout::RawNullablePointer { .. } => { assert_eq!(args.len(), 1); let (val, ty) = args.pop().unwrap(); @@ -307,7 +326,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; - trace!("read_discriminant_value {:?}", adt_layout); + trace!("read_discriminant_value {:#?}", adt_layout); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, signed: false, .. } => { @@ -344,6 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> { + trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size); let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, diff --git a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs index 059c4b972eee9..44441ed1d36c8 100644 --- a/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs +++ b/tests/run-pass/tuple_like_enum_variant_constructor_struct_pointer_opt.rs @@ -7,6 +7,13 @@ struct A<'a> { #[derive(Copy, Clone, PartialEq, Debug)] struct B<'a>(i32, &'a i32); +#[derive(Copy, Clone, PartialEq, Debug)] +enum C<'a> { + Value(i32, &'a i32), + #[allow(dead_code)] + NoValue, +} + fn main() { let x = 5; let a = A { x: 99, y: &x }; @@ -17,6 +24,8 @@ fn main() { //let f: for<'a> fn(i32, &'a i32) -> B<'a> = B; //assert_eq!(Some(B(42, &x)), Some(f(42, &x))); assert_eq!(B(42, &x), foo(&x, B)); + let f = C::Value; + assert_eq!(C::Value(42, &x), f(42, &x)); } fn foo<'a, F: Fn(i32, &'a i32) -> B<'a>>(i: &'a i32, f: F) -> B<'a> { From 4650e75d609a3937118b8065a11b470fdbc9a3b4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 28 Jan 2017 16:14:32 +0100 Subject: [PATCH 0758/1096] travis cargo doesn't support custom subcommands --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 52717dfc0dbfc..05fe244d04ffe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,13 @@ before_script: - sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust -y --disable-sudo script: - | - env RUST_SYSROOT=$HOME/rust travis-cargo build && - env RUST_SYSROOT=$HOME/rust travis-cargo test && - env RUST_SYSROOT=$HOME/rust travis-cargo install && + export RUST_SYSROOT=$HOME/rust && + travis-cargo build && + travis-cargo test && + travis-cargo install && cd cargo-miri-test && - env RUST_SYSROOT=$HOME/rust travis-cargo miri && - env RUST_SYSROOT=$HOME/rust travis-cargo miri test && + cargo miri && + cargo miri test && cd .. notifications: email: From 1752af689f57667173f844eafda93bfa86c98ba4 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 29 Jan 2017 15:21:24 +0800 Subject: [PATCH 0759/1096] TyCtxt's map renamed to hir --- src/bin/miri.rs | 6 +++--- src/step.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 0f8bb68549b56..7c7afc7f3f3d1 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -83,8 +83,8 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { fn visit_item(&mut self, i: &'hir hir::Item) { if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { if i.attrs.iter().any(|attr| attr.value.name == "test") { - let did = self.1.map.body_owner_def_id(body_id); - println!("running test: {}", self.1.map.def_path(did).to_string(self.1)); + let did = self.1.hir.body_owner_def_id(body_id); + println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); miri::eval_main(self.1, did, self.0); self.2.session.abort_if_errors(); } @@ -96,7 +96,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); } else { if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { - let entry_def_id = tcx.map.local_def_id(entry_node_id); + let entry_def_id = tcx.hir.local_def_id(entry_node_id); miri::eval_main(tcx, entry_def_id, limits); state.session.abort_if_errors(); diff --git a/src/step.rs b/src/step.rs index 9b4914f6831b1..1cc9eb4bb4dac 100644 --- a/src/step.rs +++ b/src/step.rs @@ -225,7 +225,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if let mir::Lvalue::Static(def_id) = *lvalue { let substs = self.ecx.tcx.intern_substs(&[]); let span = self.span; - if let Some(node_item) = self.ecx.tcx.map.get_if_local(def_id) { + if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) { if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { if let hir::ItemStatic(_, m, _) = *node { self.global_item(def_id, substs, span, m == hir::MutImmutable); From 5e34740ab60463a5f890bb1904fe7402f1e1b24a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 Jan 2017 09:44:52 +0100 Subject: [PATCH 0760/1096] implement packed struct field access --- src/eval_context.rs | 12 ++++++++++++ src/lib.rs | 1 + src/lvalue.rs | 17 ++++++++++++----- src/memory.rs | 30 ++++++++++++++++++++++++++---- src/step.rs | 1 + tests/run-pass/packed_struct.rs | 14 ++++++++++++++ 6 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 tests/run-pass/packed_struct.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index ce44e34f9ed9e..87f4cb9e5dd3c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -443,6 +443,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *dest_layout { Univariant { ref variant, .. } => { let offsets = variant.offsets.iter().map(|s| s.bytes()); + if variant.packed { + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + self.memory.mark_packed(ptr, variant.stride().bytes()); + } self.assign_fields(dest, offsets, operands)?; } @@ -460,6 +464,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked(); let discr_size = discr.size().bytes(); + if variants[variant].packed { + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + self.memory.mark_packed(ptr, variants[variant].stride().bytes()); + } self.assign_discr_and_fields( dest, @@ -496,6 +504,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { + if nonnull.packed { + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + self.memory.mark_packed(ptr, nonnull.stride().bytes()); + } if nndiscr == variant as u64 { let offsets = nonnull.offsets.iter().map(|s| s.bytes()); self.assign_fields(dest, offsets, operands)?; diff --git a/src/lib.rs b/src/lib.rs index 47ed4fd0cebfb..2908e31a4931c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ i128_type, pub_restricted, rustc_private, + collections_bound, )] // From rustc. diff --git a/src/lvalue.rs b/src/lvalue.rs index 7bd6cc3d989e1..64c01cc2fc4ea 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -173,13 +173,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field = field.index(); use rustc::ty::layout::Layout::*; - let offset = match *base_layout { - Univariant { ref variant, .. } => variant.offsets[field], + let (offset, packed) = match *base_layout { + Univariant { ref variant, .. } => { + (variant.offsets[field], variant.packed) + }, General { ref variants, .. } => { if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 - variants[variant_idx].offsets[field + 1] + (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) } else { bug!("field access on enum had no variant index"); } @@ -191,7 +193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } StructWrappedNullablePointer { ref nonnull, .. } => { - nonnull.offsets[field] + (nonnull.offsets[field], nonnull.packed) } UntaggedUnion { .. } => return Ok(base), @@ -200,12 +202,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field = field as u64; assert!(field < count); let elem_size = element.size(&self.tcx.data_layout).bytes(); - Size::from_bytes(field * elem_size) + (Size::from_bytes(field * elem_size), false) } _ => bug!("field access on non-product type: {:?}", base_layout), }; + if packed { + let size = self.type_size(field_ty)?.expect("packed struct must be sized"); + self.memory.mark_packed(base_ptr, size); + } + let ptr = base_ptr.offset(offset.bytes()); let extra = if self.type_is_sized(field_ty) { LvalueExtra::None diff --git a/src/memory.rs b/src/memory.rs index 79a1af50ad1f2..43bb0e23ba110 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,5 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; -use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; +use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet}; use std::{fmt, iter, ptr, mem, io}; use rustc::hir::def_id::DefId; @@ -120,6 +120,8 @@ pub struct Memory<'a, 'tcx> { function_alloc_cache: HashMap, AllocId>, next_id: AllocId, pub layout: &'a TargetDataLayout, + /// List of memory regions containing packed structures + packed: BTreeSet, } const ZST_ALLOC_ID: AllocId = AllocId(0); @@ -135,6 +137,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { layout, memory_size: max_memory, memory_usage: 0, + packed: BTreeSet::new(), } } @@ -280,8 +283,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx, ()> { + pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx, ()> { let alloc = self.get(ptr.alloc_id)?; + // check whether the memory was marked as aligned + let start = Entry(ptr.alloc_id, 0, ptr.offset + len); + let end = Entry(ptr.alloc_id, ptr.offset + len, 0); + for &Entry(_, start, end) in self.packed.range(start..end) { + if start <= ptr.offset && (ptr.offset + len) <= end { + return Ok(()); + } + } if alloc.align < align { return Err(EvalError::AlignmentCheckFailed { has: alloc.align, @@ -297,8 +308,19 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }) } } + + pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) { + self.packed.insert(Entry(ptr.alloc_id, ptr.offset, ptr.offset + len)); + } + + pub(crate) fn clear_packed(&mut self) { + self.packed.clear(); + } } +#[derive(Eq, PartialEq, Ord, PartialOrd)] +struct Entry(AllocId, u64, u64); + /// Allocation accessors impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { @@ -451,7 +473,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_align(ptr, align)?; + self.check_align(ptr, align, size)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -463,7 +485,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&mut []); } - self.check_align(ptr, align)?; + self.check_align(ptr, align, size)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) diff --git a/src/step.rs b/src/step.rs index 9b4914f6831b1..53a3aefed849f 100644 --- a/src/step.rs +++ b/src/step.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { + self.memory.clear_packed(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs new file mode 100644 index 0000000000000..93aecbb3d77c5 --- /dev/null +++ b/tests/run-pass/packed_struct.rs @@ -0,0 +1,14 @@ +#[repr(packed)] +struct S { + a: i32, + b: i64, +} + +fn main() { + let x = S { + a: 42, + b: 99, + }; + assert_eq!(x.a, 42); + assert_eq!(x.b, 99); +} From 93a75f954252f009572321e96a3ed7a1f991e3cb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 Jan 2017 09:45:21 +0100 Subject: [PATCH 0761/1096] get rid of serde rc3 dependencies through `cargo update` --- Cargo.lock | 101 ++++++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afe24d684725b..e3708fd1bc662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)", - "cargo_metadata 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -25,12 +25,12 @@ source = "git+https://github.com/quininer/byteorder.git?branch=i128#ef51df297aa8 [[package]] name = "cargo_metadata" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -39,12 +39,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "dtoa" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -53,12 +53,12 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -72,12 +72,12 @@ dependencies = [ [[package]] name = "lazy_static" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.16" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -90,7 +90,7 @@ name = "log_settings" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -98,7 +98,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -113,75 +113,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "0.1.77" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-serialize" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "0.9.0-rc3" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "serde_codegen" -version = "0.9.0-rc3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "serde_codegen_internals" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "0.9.0-rc3" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_codegen 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen_internals 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "0.9.0-rc2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dtoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.10.6" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -190,7 +182,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -203,7 +195,7 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.0.3" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -224,31 +216,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)" = "" -"checksum cargo_metadata 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb382367db7c8eb427e622e46b99eff500fb63d8cf22dc2df6bcc5587112a993" +"checksum cargo_metadata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "84e3b2d1646a740bb5aae05f7c0a7afd8ae40ea244f78bc36ac25fc8043a54a5" "checksum compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f344389765ad7bec166f64c1b39ed6dd2b54d81c4c5dd8af789169351d380c" -"checksum dtoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80e5dc7a4b2bbf348fb0afe68b3994daf1126223d2d9770221b8213c5e4565af" +"checksum dtoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87a527ff375a9761c677bb24a677ce48af8035ba260e01e831e4e4b04f945d2a" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" -"checksum itoa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8f9e7653c775f2ef8016f4181eb3ad62fe8a710e5dd73d4060a5903a58022f" +"checksum itoa 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5537accdedeaa51526addad01e989bdaeb690e8e5fcca8dce893350912778636" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" -"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" +"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" +"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" "checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" -"checksum regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)" = "64b03446c466d35b42f2a8b203c8e03ed8b91c0f17b56e1f84f7210a257aa665" -"checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd" -"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" -"checksum serde 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfeedfddd5db4465d96959431d7f3d8d618a6052cdaf3fddb2e981e86a7ad04c" -"checksum serde_codegen 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "c89a070576ea7af4c609e72fcdd3d283e9c4c77946bd3fd7a07c43ee15b9c144" -"checksum serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806" -"checksum serde_derive 0.9.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "1651978181e36fc90e1faaf91ae21fe74ffba77bc4ce4baf18b20fbb00e24cd4" -"checksum serde_json 0.9.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)" = "6efad3dc934e5032a92ea163adb13c8414359da950a0f304c1897214f28d9444" -"checksum syn 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)" = "17134635792e6a2361f53efbee798701796d8b5842c1c21b7cdb875e2950c8fc" +"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" +"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" +"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" +"checksum serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ff246881a798936bb630947e77add6c4b031fbf28312aca8e3d7c8949429e5f0" +"checksum serde_codegen_internals 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbca5cba592a2874e48fb67a61479f5b86c0b84a86cf82fa81f947ea538e1449" +"checksum serde_derive 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7278d46eaf402b063c25288d0e4232029e9fb2f20e272a932b2c15a9fed7f32d" +"checksum serde_json 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d30dd31e5b6b2752ba87da4bb34edc01391bbab71563fc1e95cdd1e30dce16b8" +"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" -"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" From 96607d45936c80eead2b1ac89b2c6a658a19489e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:36:27 +0100 Subject: [PATCH 0762/1096] document our packed struct strategy --- src/lib.rs | 1 - src/memory.rs | 49 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2908e31a4931c..47ed4fd0cebfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,6 @@ i128_type, pub_restricted, rustc_private, - collections_bound, )] // From rustc. diff --git a/src/memory.rs b/src/memory.rs index 43bb0e23ba110..12345b87804c0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -121,6 +121,14 @@ pub struct Memory<'a, 'tcx> { next_id: AllocId, pub layout: &'a TargetDataLayout, /// List of memory regions containing packed structures + /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking afterwards. + /// In the case where no packed structs are present, it's just a single emptyness check of a set + /// instead of heavily influencing all memory access code as other solutions would. + /// + /// One disadvantage of this solution is the fact that you can cast a pointer to a packed struct + /// to a pointer to a normal struct and if you access a field of both in the same MIR statement, + /// the normal struct access will succeed even though it shouldn't. + /// But even with mir optimizations, that situation is hard/impossible to produce. packed: BTreeSet, } @@ -285,11 +293,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx, ()> { let alloc = self.get(ptr.alloc_id)?; - // check whether the memory was marked as aligned - let start = Entry(ptr.alloc_id, 0, ptr.offset + len); - let end = Entry(ptr.alloc_id, ptr.offset + len, 0); - for &Entry(_, start, end) in self.packed.range(start..end) { - if start <= ptr.offset && (ptr.offset + len) <= end { + // check whether the memory was marked as packed + // we select all elements that have the correct alloc_id and are within + // the range given by the offset into the allocation and the length + let start = Entry { + alloc_id: ptr.alloc_id, + packed_start: 0, + packed_end: ptr.offset + len, + }; + let end = Entry { + alloc_id: ptr.alloc_id, + packed_start: ptr.offset + len, + packed_end: 0, + }; + for &Entry { packed_start, packed_end, .. } in self.packed.range(start..end) { + // if the region we are checking is covered by a region in `packed` + // ignore the actual alignment + if packed_start <= ptr.offset && (ptr.offset + len) <= packed_end { return Ok(()); } } @@ -310,7 +330,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) { - self.packed.insert(Entry(ptr.alloc_id, ptr.offset, ptr.offset + len)); + self.packed.insert(Entry { + alloc_id: ptr.alloc_id, + packed_start: ptr.offset, + packed_end: ptr.offset + len, + }); } pub(crate) fn clear_packed(&mut self) { @@ -318,8 +342,19 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } +// The derived `Ord` impl sorts first by the first field, then, if the fields are the same +// by the second field, and if those are the same, too, then by the third field. +// This is exactly what we need for our purposes, since a range within an allocation +// will give us all `Entry`s that have that `AllocId`, and whose `packed_start` is <= than +// the one we're looking for, but not > the end of the range we're checking. +// At the same time the `packed_end` is irrelevant for the sorting and range searching, but used for the check. +// This kind of search breaks, if `packed_end < packed_start`, so don't do that! #[derive(Eq, PartialEq, Ord, PartialOrd)] -struct Entry(AllocId, u64, u64); +struct Entry { + alloc_id: AllocId, + packed_start: u64, + packed_end: u64, +} /// Allocation accessors impl<'a, 'tcx> Memory<'a, 'tcx> { From 148c6de50703f611f272e86e568425349b3b1550 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:36:46 +0100 Subject: [PATCH 0763/1096] fix some unaligned reads --- src/memory.rs | 14 ++++++++------ tests/compile-fail/reference_to_packed.rs | 16 ++++++++++++++++ tests/compile-fail/reference_to_packed_unsafe.rs | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/compile-fail/reference_to_packed.rs create mode 100644 tests/compile-fail/reference_to_packed_unsafe.rs diff --git a/src/memory.rs b/src/memory.rs index 12345b87804c0..833e16a4950fe 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -474,10 +474,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { - fn get_bytes_unchecked(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { + fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } + self.check_align(ptr, align, size)?; let alloc = self.get(ptr.alloc_id)?; let allocation_size = alloc.bytes.len() as u64; if ptr.offset + size > allocation_size { @@ -489,10 +490,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(&alloc.bytes[offset..offset + size as usize]) } - fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { if size == 0 { return Ok(&mut []); } + self.check_align(ptr, align, size)?; let alloc = self.get_mut(ptr.alloc_id)?; let allocation_size = alloc.bytes.len() as u64; if ptr.offset + size > allocation_size { @@ -513,7 +515,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::ReadPointerAsBytes); } self.check_defined(ptr, size)?; - self.get_bytes_unchecked(ptr, size) + self.get_bytes_unchecked(ptr, size, align) } fn get_bytes_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { @@ -523,7 +525,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.check_align(ptr, align, size)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; - self.get_bytes_unchecked_mut(ptr, size) + self.get_bytes_unchecked_mut(ptr, size, align) } } @@ -558,7 +560,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } self.check_relocation_edges(src, size)?; - let src_bytes = self.get_bytes_unchecked(src, size)?.as_ptr(); + let src_bytes = self.get_bytes_unchecked(src, size, align)?.as_ptr(); let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes @@ -615,7 +617,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let size = self.pointer_size(); self.check_defined(ptr, size)?; let endianess = self.endianess(); - let bytes = self.get_bytes_unchecked(ptr, size)?; + let bytes = self.get_bytes_unchecked(ptr, size, size)?; let offset = read_target_uint(endianess, bytes).unwrap(); assert_eq!(offset as u64 as u128, offset); let offset = offset as u64; diff --git a/tests/compile-fail/reference_to_packed.rs b/tests/compile-fail/reference_to_packed.rs new file mode 100644 index 0000000000000..119225f3e369d --- /dev/null +++ b/tests/compile-fail/reference_to_packed.rs @@ -0,0 +1,16 @@ +#![allow(dead_code, unused_variables)] + +#[repr(packed)] +struct Foo { + x: i32, + y: i32, +} + +fn main() { + let foo = Foo { + x: 42, + y: 99, + }; + let p = &foo.x; + let i = *p; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required +} \ No newline at end of file diff --git a/tests/compile-fail/reference_to_packed_unsafe.rs b/tests/compile-fail/reference_to_packed_unsafe.rs new file mode 100644 index 0000000000000..5761f23b7dd41 --- /dev/null +++ b/tests/compile-fail/reference_to_packed_unsafe.rs @@ -0,0 +1,16 @@ +#![allow(dead_code, unused_variables)] + +#[repr(packed)] +struct Foo { + x: i32, + y: i32, +} + +fn main() { + let foo = Foo { + x: 42, + y: 99, + }; + let p: *const i32 = &foo.x; + let x = unsafe { *p + foo.x }; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required +} From d3e9e51d67b354c765a9820b2332652946499855 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:51:19 +0100 Subject: [PATCH 0764/1096] fix accessing fields other than the first in packed structs --- src/lvalue.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 64c01cc2fc4ea..586726d95ded5 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -208,12 +208,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; + let ptr = base_ptr.offset(offset.bytes()); + if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); - self.memory.mark_packed(base_ptr, size); + self.memory.mark_packed(ptr, size); } - let ptr = base_ptr.offset(offset.bytes()); let extra = if self.type_is_sized(field_ty) { LvalueExtra::None } else { From a3d83e5c8f9bfd504e5c6a6bbc6df8d2f451eee1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:51:52 +0100 Subject: [PATCH 0765/1096] can't call `assert_eq` on packed struct fields since that takes references --- tests/run-pass/packed_struct.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs index 93aecbb3d77c5..5b3f09c0dd096 100644 --- a/tests/run-pass/packed_struct.rs +++ b/tests/run-pass/packed_struct.rs @@ -9,6 +9,11 @@ fn main() { a: 42, b: 99, }; - assert_eq!(x.a, 42); - assert_eq!(x.b, 99); + let a = x.a; + let b = x.b; + assert_eq!(a, 42); + assert_eq!(b, 99); + // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference + assert_eq!({x.a}, 42); + assert_eq!({x.b}, 99); } From c47c3252526c8c7219c09a9f38915e968caa3d95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:56:37 +0100 Subject: [PATCH 0766/1096] remove duplicate alignment checks --- src/memory.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 833e16a4950fe..cbfd881526815 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -510,7 +510,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_align(ptr, align, size)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -522,7 +521,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&mut []); } - self.check_align(ptr, align, size)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) From 74d1a9a26c8a5aa3f2471ee784a2a0d12628c69b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 31 Jan 2017 10:59:38 +0100 Subject: [PATCH 0767/1096] more documentation of unintuitive packed struct solution --- src/step.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/step.rs b/src/step.rs index 53a3aefed849f..9c44468216725 100644 --- a/src/step.rs +++ b/src/step.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { + // see docs on the `Memory::packed` field for why we do this self.memory.clear_packed(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { From 02a6937926c92181c4bf51982862b542499f2fea Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Feb 2017 12:59:41 +0100 Subject: [PATCH 0768/1096] add docs for `cargo miri` --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 03949135b4b2b..3822e9ac10cc4 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,13 @@ environment variable to `trace`. These traces are indented based on call stack depth. You can get a much less verbose set of information with other logging levels such as `warn`. +## Running miri on your own project('s test suite) + +Install miri as a cargo subcommand with `cargo install --debug`. +Then, inside your own project, use `cargo +nightly miri` to run your project, if it is +a bin project, or run `cargo +nightly miri test` to run all tests in your project +through miri. + ## Contributing and getting help Check out the issues on this GitHub repository for some ideas. There's lots that From 0e77dd947304b892bf3277e0f4a75600737c6843 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Feb 2017 15:47:23 +0100 Subject: [PATCH 0769/1096] rustup --- src/bin/miri.rs | 2 +- src/eval_context.rs | 159 ++++++++++++++++++++++++------------------ src/lvalue.rs | 6 +- src/terminator/mod.rs | 3 +- 4 files changed, 96 insertions(+), 74 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 7c7afc7f3f3d1..8ff1b418b1a47 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -8,7 +8,7 @@ extern crate rustc_errors; extern crate env_logger; extern crate log_settings; extern crate syntax; -#[macro_use] extern crate log; +extern crate log; use rustc::session::Session; use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls}; diff --git a/src/eval_context.rs b/src/eval_context.rs index e02ea09be861f..defb4e5c882e9 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -643,9 +643,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyBox(ty) => !self.type_is_sized(ty), + ty::TyRawPtr(ref tam) | + ty::TyRef(_, ref tam) => !self.type_is_sized(tam.ty), + ty::TyAdt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()), _ => false, } } @@ -686,26 +686,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((offset, ty)) } + fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { + match (field_index, &self.tcx.struct_tail(pointee_ty).sty) { + (1, &ty::TyStr) | + (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), + (1, &ty::TyDynamic(..)) | + (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), + _ => bug!("invalid fat pointee type: {}", pointee_ty), + } + } pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match ty.sty { + ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index), ty::TyAdt(adt_def, substs) => { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } ty::TyTuple(fields) => Ok(fields[field_index]), - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyBox(ty) => { - match (field_index, &self.tcx.struct_tail(ty).sty) { - (1, &ty::TyStr) | - (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize), - (1, &ty::TyDynamic(..)) | - (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)), - _ => bug!("invalid fat pointee type: {}", ty), - } - } + ty::TyRef(_, ref tam) | + ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index), _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } } @@ -1044,9 +1045,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(_) => PrimValKind::FnPtr, - ty::TyBox(ty) | - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if self.type_is_sized(ty) => PrimValKind::Ptr, + ty::TyRef(_, ref tam) | + ty::TyRawPtr(ref tam) if self.type_is_sized(tam.ty) => PrimValKind::Ptr, + + ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr, ty::TyAdt(..) => { use rustc::ty::layout::Layout::*; @@ -1100,6 +1102,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + fn read_ptr(&mut self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + let p = self.memory.read_ptr(ptr)?; + if self.type_is_sized(pointee_ty) { + Ok(Value::ByVal(PrimVal::Ptr(p))) + } else { + trace!("reading fat pointer extra of type {}", pointee_ty); + let extra = ptr.offset(self.memory.pointer_size()); + let extra = match self.tcx.struct_tail(pointee_ty).sty { + ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), + ty::TySlice(..) | + ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), + _ => bug!("unsized primval ptr read from {:?}", pointee_ty), + }; + Ok(Value::ByValPair(PrimVal::Ptr(p), extra)) + } + } + fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; @@ -1143,26 +1162,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Ptr)?, - ty::TyBox(ty) | - ty::TyRef(_, ty::TypeAndMut { ty, .. }) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { - let p = self.memory.read_ptr(ptr)?; - if self.type_is_sized(ty) { - PrimVal::Ptr(p) - } else { - trace!("reading fat pointer extra of type {}", ty); - let extra = ptr.offset(self.memory.pointer_size()); - let extra = match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), - ty::TySlice(..) | - ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), - _ => bug!("unsized primval ptr read from {:?}", ty), - }; - return Ok(Some(Value::ByValPair(PrimVal::Ptr(p), extra))); - } - } + ty::TyRef(_, ref tam) | + ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, tam.ty).map(Some), - ty::TyAdt(..) => { + ty::TyAdt(def, _) => { + if def.is_box() { + return self.read_ptr(ptr, ty.boxed_ty()).map(Some); + } use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes(); @@ -1198,6 +1204,45 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.frame().substs } + fn unsize_into_ptr( + &mut self, + src: Value, + src_ty: Ty<'tcx>, + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + sty: Ty<'tcx>, + dty: Ty<'tcx>, + ) -> EvalResult<'tcx, ()> { + // A -> A conversion + let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty); + + match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { + (&ty::TyArray(_, length), &ty::TySlice(_)) => { + let ptr = src.read_ptr(&self.memory)?; + let len = PrimVal::from_u128(length as u128); + let ptr = PrimVal::Ptr(ptr); + self.write_value(Value::ByValPair(ptr, len), dest, dest_ty) + } + (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + self.write_value(src, dest, dest_ty) + }, + (_, &ty::TyDynamic(ref data, _)) => { + let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); + let trait_ref = self.tcx.erase_regions(&trait_ref); + let vtable = self.get_vtable(trait_ref)?; + let ptr = src.read_ptr(&self.memory)?; + let ptr = PrimVal::Ptr(ptr); + let extra = PrimVal::Ptr(vtable); + self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty) + }, + + _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), + } + } + fn unsize_into( &mut self, src: Value, @@ -1206,40 +1251,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, ()> { match (&src_ty.sty, &dest_ty.sty) { - (&ty::TyBox(sty), &ty::TyBox(dty)) | - (&ty::TyRef(_, ty::TypeAndMut { ty: sty, .. }), &ty::TyRef(_, ty::TypeAndMut { ty: dty, .. })) | - (&ty::TyRef(_, ty::TypeAndMut { ty: sty, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: dty, .. })) | - (&ty::TyRawPtr(ty::TypeAndMut { ty: sty, .. }), &ty::TyRawPtr(ty::TypeAndMut { ty: dty, .. })) => { - // A -> A conversion - let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty); - - match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { - (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let ptr = src.read_ptr(&self.memory)?; - let len = PrimVal::from_u128(length as u128); - let ptr = PrimVal::Ptr(ptr); - self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)?; + (&ty::TyRef(_, ref s), &ty::TyRef(_, ref d)) | + (&ty::TyRef(_, ref s), &ty::TyRawPtr(ref d)) | + (&ty::TyRawPtr(ref s), &ty::TyRawPtr(ref d)) => self.unsize_into_ptr(src, src_ty, dest, dest_ty, s.ty, d.ty), + (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => { + if def_a.is_box() || def_b.is_box() { + if !def_a.is_box() || !def_b.is_box() { + panic!("invalid unsizing between {:?} -> {:?}", src_ty, dest_ty); } - (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - self.write_value(src, dest, dest_ty)?; - }, - (_, &ty::TyDynamic(ref data, _)) => { - let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); - let trait_ref = self.tcx.erase_regions(&trait_ref); - let vtable = self.get_vtable(trait_ref)?; - let ptr = src.read_ptr(&self.memory)?; - let ptr = PrimVal::Ptr(ptr); - let extra = PrimVal::Ptr(vtable); - self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty)?; - }, - - _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), + return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty()); } - } - (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); // unsizing of generic struct with pointer fields @@ -1275,10 +1296,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } } + Ok(()) } _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", src_ty, dest_ty), } - Ok(()) } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { diff --git a/src/lvalue.rs b/src/lvalue.rs index 7bd6cc3d989e1..4a94abf0a569a 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -241,9 +241,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.eval_and_read_lvalue(&proj.base)?; let pointee_type = match base_ty.sty { - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty::TypeAndMut{ty, ..}) | - ty::TyBox(ty) => ty, + ty::TyRawPtr(ref tam) | + ty::TyRef(_, ref tam) => tam.ty, + ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(), _ => bug!("can only deref pointer types"), }; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 11082c633a755..b7e4549f7d5d2 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -626,7 +626,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match ty.sty { // special case `Box` to deallocate the inner allocation - ty::TyBox(contents_ty) => { + ty::TyAdt(ref def, _) if def.is_box() => { + let contents_ty = ty.boxed_ty(); let val = self.read_lvalue(lval); // we are going through the read_value path, because that already does all the // checks for the trait object types. We'd only be repeating ourselves here. From 7acbf7ef74166f63c3fb5f90a9c446f4163f79b9 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Sat, 4 Feb 2017 13:09:10 -0800 Subject: [PATCH 0770/1096] Cleanup: EvalResult<'a, ()> becomes EvalResult<'a>. --- src/error.rs | 2 +- src/eval_context.rs | 32 ++++++++++++------------ src/memory.rs | 49 ++++++++++++++++++++----------------- src/operator.rs | 2 +- src/step.rs | 8 +++--- src/terminator/intrinsic.rs | 2 +- src/terminator/mod.rs | 14 +++++------ 7 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/error.rs b/src/error.rs index c641fc24db52e..a7168bd19115e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,7 +55,7 @@ pub enum EvalError<'tcx> { Unreachable, } -pub type EvalResult<'tcx, T> = Result>; +pub type EvalResult<'tcx, T = ()> = Result>; impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { diff --git a/src/eval_context.rs b/src/eval_context.rs index ff3f947e2d964..a64b3fc3548c6 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -277,7 +277,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, temporaries: Vec, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local @@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx, ()> { + pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { @@ -369,7 +369,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { operands: J, discr_val: u128, discr_size: u64, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); @@ -390,7 +390,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, offsets: I, operands: J, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); @@ -410,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, rvalue: &mir::Rvalue<'tcx>, lvalue: &mir::Lvalue<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { let dest = self.eval_lvalue(lvalue)?; let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty)?; @@ -833,7 +833,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } - fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); let align = self.type_align(ty)?; self.memory.copy(src, dest, size, align)?; @@ -909,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, val: PrimVal, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { match dest { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); @@ -937,7 +937,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { src_val: Value, dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { match dest { Lvalue::Global(cid) => { let dest = *self.globals.get_mut(&cid).expect("global should be cached"); @@ -977,7 +977,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { write_dest: F, old_dest_val: Value, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { if let Value::ByRef(dest_ptr) = old_dest_val { // If the value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be @@ -1021,7 +1021,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { value: Value, dest: Pointer, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { match value { Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => { @@ -1038,7 +1038,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { b: PrimVal, ptr: Pointer, ty: Ty<'tcx> - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { assert_eq!(self.get_field_count(ty)?, 2); let field_0 = self.get_field_offset(ty, 0)?.bytes(); let field_1 = self.get_field_offset(ty, 1)?.bytes(); @@ -1127,7 +1127,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(kind) } - fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { match ty.sty { ty::TyBool if val.to_bytes()? > 1 => Err(EvalError::InvalidBool), @@ -1256,7 +1256,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, sty: Ty<'tcx>, dty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { // A -> A conversion let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(sty, dty); @@ -1293,7 +1293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { src_ty: Ty<'tcx>, dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { match (&src_ty.sty, &dest_ty.sty) { (&ty::TyRef(_, ref s), &ty::TyRef(_, ref d)) | (&ty::TyRef(_, ref s), &ty::TyRawPtr(ref d)) | @@ -1371,7 +1371,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } /// Convenience function to ensure correct usage of globals and code-sharing with locals. - pub fn modify_global(&mut self, cid: GlobalId<'tcx>, f: F) -> EvalResult<'tcx, ()> + pub fn modify_global(&mut self, cid: GlobalId<'tcx>, f: F) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { let mut val = *self.globals.get(&cid).expect("global not cached"); @@ -1389,7 +1389,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { frame: usize, local: mir::Local, f: F, - ) -> EvalResult<'tcx, ()> + ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { let val = self.stack[frame].get_local(local); diff --git a/src/memory.rs b/src/memory.rs index cbfd881526815..8df4940c113ec 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -258,7 +258,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } // TODO(solson): See comment on `reallocate`. - pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx, ()> { + pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> { if ptr.points_to_zst() { return Ok(()); } @@ -291,7 +291,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx, ()> { + pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; // check whether the memory was marked as packed // we select all elements that have the correct alloc_id and are within @@ -529,7 +529,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { + pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx> { // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { @@ -552,7 +552,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64) -> EvalResult<'tcx, ()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64) -> EvalResult<'tcx> { if size == 0 { return Ok(()); } @@ -599,13 +599,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes(ptr, size, 1) } - pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { + pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> { let bytes = self.get_bytes_mut(ptr, src.len() as u64, 1)?; bytes.clone_from_slice(src); Ok(()) } - pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx, ()> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) @@ -626,7 +626,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> { + pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> { self.write_usize(dest, ptr.offset as u64)?; self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); Ok(()) @@ -637,7 +637,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { dest: Pointer, val: PrimVal, size: u64, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { match val { PrimVal::Ptr(ptr) => { assert_eq!(size, self.pointer_size()); @@ -671,7 +671,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { + pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx> { let align = self.layout.i1_align.abi(); self.get_bytes_mut(ptr, 1, align) .map(|bytes| bytes[0] = b as u8) @@ -693,7 +693,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } - pub fn write_int(&mut self, ptr: Pointer, n: i128, size: u64) -> EvalResult<'tcx, ()> { + pub fn write_int(&mut self, ptr: Pointer, n: i128, size: u64) -> EvalResult<'tcx> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -706,7 +706,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } - pub fn write_uint(&mut self, ptr: Pointer, n: u128, size: u64) -> EvalResult<'tcx, ()> { + pub fn write_uint(&mut self, ptr: Pointer, n: u128, size: u64) -> EvalResult<'tcx> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -718,7 +718,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.read_int(ptr, self.pointer_size()).map(|i| i as i64) } - pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx, ()> { + pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx> { let size = self.pointer_size(); self.write_int(ptr, n as i128, size) } @@ -727,12 +727,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.read_uint(ptr, self.pointer_size()).map(|i| i as u64) } - pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { + pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx> { let size = self.pointer_size(); self.write_uint(ptr, n as u128, size) } - pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { + pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx> { let endianess = self.endianess(); let align = self.layout.f32_align.abi(); let b = self.get_bytes_mut(ptr, 4, align)?; @@ -740,7 +740,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { + pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx> { let endianess = self.endianess(); let align = self.layout.f64_align.abi(); let b = self.get_bytes_mut(ptr, 8, align)?; @@ -769,7 +769,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) } - fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { + fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { return Ok(()); } @@ -793,7 +793,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { + fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); let overlapping_end = self.relocations(ptr.offset(size), 0)?.count(); if overlapping_start + overlapping_end != 0 { @@ -802,7 +802,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx, ()> { + fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx> { let relocations: Vec<_> = self.relocations(src, size)? .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. @@ -817,7 +817,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Undefined bytes impl<'a, 'tcx> Memory<'a, 'tcx> { // FIXME(solson): This is a very naive, slow version. - fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx, ()> { + fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx> { // The bits have to be saved locally before writing to dest in case src and dest overlap. assert_eq!(size as usize as u64, size); let mut v = Vec::with_capacity(size as usize); @@ -831,7 +831,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn check_defined(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, ()> { + fn check_defined(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); @@ -839,9 +839,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn mark_definedness(&mut self, ptr: Pointer, size: u64, new_state: bool) - -> EvalResult<'tcx, ()> - { + pub fn mark_definedness( + &mut self, + ptr: Pointer, + size: u64, + new_state: bool + ) -> EvalResult<'tcx> { if size == 0 { return Ok(()) } diff --git a/src/operator.rs b/src/operator.rs index 417e7047ec00e..c4c724c594e2a 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { right: &mir::Operand<'tcx>, dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { let (val, overflowed) = self.binop_with_overflow(op, left, right)?; let val = Value::ByValPair(val, PrimVal::from_bool(overflowed)); self.write_value(val, dest, dest_ty) diff --git a/src/step.rs b/src/step.rs index 5b0e648d591d0..33237b84637a5 100644 --- a/src/step.rs +++ b/src/step.rs @@ -17,7 +17,7 @@ use lvalue::{Global, GlobalId, Lvalue}; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx, ()> { + pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { self.steps_remaining = self.steps_remaining.saturating_sub(n); if self.steps_remaining > 0 { Ok(()) @@ -76,7 +76,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(true) } - fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { + fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", stmt); use rustc::mir::StatementKind::*; @@ -124,7 +124,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> { + fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> { trace!("{:?}", terminator.kind); self.eval_terminator(terminator)?; if !self.stack.is_empty() { @@ -164,7 +164,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new()) }); } - fn try EvalResult<'tcx, ()>>(&mut self, f: F) { + fn try EvalResult<'tcx>>(&mut self, f: F) { if let Ok(ref mut n) = *self.new_constants { *n += 1; } else { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 27d73fb66481d..811ae7888d729 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -20,7 +20,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, target: mir::BasicBlock, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { let arg_vals: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 3c5dd53a73a69..85d52f1a08d44 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -27,7 +27,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_terminator( &mut self, terminator: &mir::Terminator<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { use rustc::mir::TerminatorKind::*; match terminator.kind { Return => { @@ -161,7 +161,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx, ()> { + pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { // add them to the stack in reverse order, because the impl that needs to run the last // is the one that needs to be at the bottom of the stack for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { @@ -194,7 +194,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { use syntax::abi::Abi; match fn_ty.abi { Abi::RustIntrinsic => { @@ -379,7 +379,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); let link_name = attr::first_attr_value_str_by_name(&attrs, "link_name") @@ -513,7 +513,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }) } - fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx, ()> { + fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { if let Some((last, last_ty)) = args.pop() { let last_layout = self.type_layout(last_ty)?; match (&last_ty.sty, last_layout) { @@ -649,7 +649,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lval: Lvalue<'tcx>, ty: Ty<'tcx>, drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx, ()> { + ) -> EvalResult<'tcx> { if !self.type_needs_drop(ty) { debug!("no need to drop {:?}", ty); return Ok(()); @@ -813,7 +813,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mut fields: I, lval: Lvalue<'tcx>, drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx, ()> + ) -> EvalResult<'tcx> where I: Iterator, ty::layout::Size)>, { // FIXME: some aggregates may be represented by Value::ByValPair From b36d8085a753e243728ec67fa8af3f925656a5fe Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 6 Feb 2017 09:26:01 -0800 Subject: [PATCH 0771/1096] Fix duplicate allocation printing. --- src/eval_context.rs | 2 +- src/memory.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index a64b3fc3548c6..21f71933d2388 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -200,7 +200,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), Function(_) => unimplemented!(), - Array(_) => unimplemented!(), + Array(_) => unimplemented!(), Repeat(_, _) => unimplemented!(), }; diff --git a/src/memory.rs b/src/memory.rs index 8df4940c113ec..e9748ba62c781 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -411,7 +411,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let mut allocs_seen = HashSet::new(); while let Some(id) = allocs_to_print.pop_front() { - allocs_seen.insert(id); if id == ZST_ALLOC_ID || id == NEVER_ALLOC_ID { continue; } let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); let prefix_len = msg.len(); @@ -433,7 +432,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { for i in 0..(alloc.bytes.len() as u64) { if let Some(&target_id) = alloc.relocations.get(&i) { - if !allocs_seen.contains(&target_id) { + if allocs_seen.insert(target_id) { allocs_to_print.push_back(target_id); } relocations.push((i, target_id)); From 097db58f306554dee3372fb26a798956f34924b3 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Mon, 6 Feb 2017 12:48:10 -0800 Subject: [PATCH 0772/1096] Print fn name and type in dump_allocs. --- src/memory.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index e9748ba62c781..cde494d7aac13 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -418,9 +418,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { (Some(a), None) => a, - (None, Some(_)) => { - // FIXME: print function name - trace!("{} function pointer", msg); + (None, Some(fn_def)) => { + let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); + trace!("{} function pointer: {}: {}", msg, name, fn_def.sig); continue; }, (None, None) => { From 0afcb0568e46cec1e82510f6cd52c2c4edd662f1 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 00:39:40 -0800 Subject: [PATCH 0773/1096] Clean up local var dumping. --- src/eval_context.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 21f71933d2388..3024b5c7edc5e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1,5 +1,6 @@ use std::cell::Ref; use std::collections::HashMap; +use std::fmt::Write; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; @@ -1347,27 +1348,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { - let mut allocs = Vec::new(); - if let Lvalue::Local { frame, local } = lvalue { + let mut allocs = Vec::new(); + let mut msg = format!("{:?}", local); + let last_frame = self.stack.len() - 1; + if frame != last_frame { + write!(msg, " ({} frames up)", last_frame - frame).unwrap(); + } + write!(msg, ":").unwrap(); + match self.stack[frame].get_local(local) { Value::ByRef(ptr) => { - trace!("frame[{}] {:?}:", frame, local); allocs.push(ptr.alloc_id); } Value::ByVal(val) => { - trace!("frame[{}] {:?}: {:?}", frame, local, val); + write!(msg, " {:?}", val).unwrap(); if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } } Value::ByValPair(val1, val2) => { - trace!("frame[{}] {:?}: ({:?}, {:?})", frame, local, val1, val2); + write!(msg, " ({:?}, {:?})", val1, val2).unwrap(); if let PrimVal::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } if let PrimVal::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } } } - } - self.memory.dump_allocs(allocs); + trace!("{}", msg); + self.memory.dump_allocs(allocs); + } } /// Convenience function to ensure correct usage of globals and code-sharing with locals. From a5b9a0cb787b69204a2861f7c49f711ddfafafea Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 00:45:22 -0800 Subject: [PATCH 0774/1096] Simplify logging output. --- src/bin/miri.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8ff1b418b1a47..f1a97d0a44b5c 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -143,24 +143,25 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits } fn init_logger() { - const MAX_INDENT: usize = 40; - let format = |record: &log::LogRecord| { if record.level() == log::LogLevel::Trace { // prepend spaces to indent the final string let indentation = log_settings::settings().indentation; - format!("{lvl}:{module}{depth:2}{indent: Date: Tue, 7 Feb 2017 01:03:40 -0800 Subject: [PATCH 0775/1096] Dump instrinsic and C ABI return values. --- src/terminator/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 85d52f1a08d44..ee75f03d8f5ce 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -205,6 +205,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => return Err(EvalError::Unreachable), }; self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; + self.dump_local(ret); Ok(()) } @@ -212,6 +213,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; + self.dump_local(ret); self.goto_block(target); Ok(()) } From 468dbe8eaa7dbd221694af32eb07554a57362a59 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 01:30:16 -0800 Subject: [PATCH 0776/1096] Don't print a span for call to main. --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 3024b5c7edc5e..100224fba69bc 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1432,7 +1432,7 @@ pub fn eval_main<'a, 'tcx: 'a>( ecx.push_stack_frame( def_id, - mir.span, + DUMMY_SP, mir, tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), From 9f7ca351e02e0b75b32f9deda96cb6ad25323d09 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 01:57:42 -0800 Subject: [PATCH 0777/1096] Dump fn ptr ABIs. --- src/memory.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index cde494d7aac13..419a24a5db701 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -420,7 +420,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { (Some(a), None) => a, (None, Some(fn_def)) => { let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); - trace!("{} function pointer: {}: {}", msg, name, fn_def.sig); + let abi = if fn_def.abi == Abi::Rust { + format!("") + } else { + format!("extern {} ", fn_def.abi) + }; + trace!("{} function pointer: {}: {}{}", msg, name, abi, fn_def.sig); continue; }, (None, None) => { From 0377990dc6c0d238e750965f017401efec7da4fc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 11:04:32 +0100 Subject: [PATCH 0778/1096] fix turning function items into closure trait objects --- src/terminator/mod.rs | 41 +++++++++++++------ .../fn_item_as_closure_trait_object.rs | 6 +++ ..._item_with_args_as_closure_trait_object.rs | 8 ++++ ...h_multiple_args_as_closure_trait_object.rs | 18 ++++++++ 4 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 tests/run-pass/fn_item_as_closure_trait_object.rs create mode 100644 tests/run-pass/fn_item_with_args_as_closure_trait_object.rs create mode 100644 tests/run-pass/fn_item_with_multiple_args_as_closure_trait_object.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ee75f03d8f5ce..95b62a0bcc074 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -6,7 +6,7 @@ use rustc::ty::layout::{Layout, Size}; use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use syntax::codemap::{DUMMY_SP, Span}; -use syntax::{ast, attr}; +use syntax::{ast, attr, abi}; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited}; @@ -313,6 +313,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; let arg_locals = self.frame().mir.args_iter(); + // FIXME: impl ExactSizeIterator and use args_locals.len() + // FIXME: last-use-in-cap-clause works by chance, insert some arguments and it will fail + // we currently write the first argument (unit) to the return field (unit) + //assert_eq!(self.frame().mir.args_iter().count(), args.len()); for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; @@ -624,18 +628,31 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; - if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { - let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?; - *first_arg = Value::ByVal(PrimVal::Ptr(self_ptr)); - let idx = idx + 3; - let offset = idx * self.memory.pointer_size(); - let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; - let (def_id, substs, _abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; - *first_ty = sig.inputs()[0]; - Ok((def_id, substs, Vec::new())) - } else { - Err(EvalError::VtableForArgumentlessMethod) + if args.is_empty() { + return Err(EvalError::VtableForArgumentlessMethod); + } + let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; + let idx = idx + 3; + let offset = idx * self.memory.pointer_size(); + let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; + let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + trace!("args: {:#?}", args); + trace!("sig: {:#?}", sig); + if abi != abi::Abi::RustCall && args.len() == 2 { + if let ty::TyTuple(wrapped_args) = args[1].1.sty { + assert_eq!(sig.inputs(), &wrapped_args[..]); + // a function item turned into a closure trait object + // the first arg is just there to give use the vtable + args.remove(0); + self.unpack_fn_args(args)?; + return Ok((def_id, substs, Vec::new())); + } } + args[0] = ( + Value::ByVal(PrimVal::Ptr(self_ptr)), + sig.inputs()[0], + ); + Ok((def_id, substs, Vec::new())) }, vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), } diff --git a/tests/run-pass/fn_item_as_closure_trait_object.rs b/tests/run-pass/fn_item_as_closure_trait_object.rs new file mode 100644 index 0000000000000..799f97a4f6fde --- /dev/null +++ b/tests/run-pass/fn_item_as_closure_trait_object.rs @@ -0,0 +1,6 @@ +fn foo() {} + +fn main() { + let f: &Fn() = &foo; + f(); +} diff --git a/tests/run-pass/fn_item_with_args_as_closure_trait_object.rs b/tests/run-pass/fn_item_with_args_as_closure_trait_object.rs new file mode 100644 index 0000000000000..79ece75c773bb --- /dev/null +++ b/tests/run-pass/fn_item_with_args_as_closure_trait_object.rs @@ -0,0 +1,8 @@ +fn foo(i: i32) { + assert_eq!(i, 42); +} + +fn main() { + let f: &Fn(i32) = &foo; + f(42); +} diff --git a/tests/run-pass/fn_item_with_multiple_args_as_closure_trait_object.rs b/tests/run-pass/fn_item_with_multiple_args_as_closure_trait_object.rs new file mode 100644 index 0000000000000..f4b5b449aa587 --- /dev/null +++ b/tests/run-pass/fn_item_with_multiple_args_as_closure_trait_object.rs @@ -0,0 +1,18 @@ +fn foo(i: i32, j: i32) { + assert_eq!(i, 42); + assert_eq!(j, 55); +} + +fn bar(i: i32, j: i32, k: f32) { + assert_eq!(i, 42); + assert_eq!(j, 55); + assert_eq!(k, 3.14159) +} + + +fn main() { + let f: &Fn(i32, i32) = &foo; + f(42, 55); + let f: &Fn(i32, i32, f32) = &bar; + f(42, 55, 3.14159); +} From 5118aadee2c45352bea4357649cae682a3ded452 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 12:32:39 +0100 Subject: [PATCH 0779/1096] reenable rustc run pass tests --- tests/compiletest.rs | 65 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 909538494e86a..ae0ac1f8adf0b 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -81,12 +81,63 @@ fn compile_test() { let host = std::str::from_utf8(&host).expect("sysroot is not utf8"); let host = host.split("\nhost: ").skip(1).next().expect("no host: part in rustc -vV"); let host = host.split("\n").next().expect("no \n after host"); - run_pass(); - for_all_targets(&sysroot, |target| { - miri_pass("tests/run-pass", &target, host); - if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - miri_pass(&path, &target, host); + + if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { + let mut mir_not_found = 0; + let mut crate_not_found = 0; + let mut success = 0; + let mut failed = 0; + for file in std::fs::read_dir(path).unwrap() { + let file = file.unwrap(); + let path = file.path(); + if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { + continue; + } + let stderr = std::io::stderr(); + write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap(); + let mut cmd = std::process::Command::new("target/debug/miri"); + cmd.arg(path); + let libs = Path::new(&sysroot).join("lib"); + let sysroot = libs.join("rustlib").join(&host).join("lib"); + let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); + cmd.env(compiletest::procsrv::dylib_env_var(), paths); + + match cmd.output() { + Ok(ref output) if output.status.success() => { + success += 1; + writeln!(stderr.lock(), "ok").unwrap() + }, + Ok(output) => { + let output_err = std::str::from_utf8(&output.stderr).unwrap(); + if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { + mir_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); + } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { + crate_not_found += 1; + let end = text.find('`').unwrap(); + writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); + } else { + failed += 1; + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); + writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + } + } + Err(e) => { + writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); + panic!("failed to execute miri"); + }, + } } - }); - compile_fail(&sysroot); + let stderr = std::io::stderr(); + writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); + assert_eq!(failed, 0, "some tests failed"); + } else { + run_pass(); + for_all_targets(&sysroot, |target| { + miri_pass("tests/run-pass", &target, host); + }); + compile_fail(&sysroot); + } } From 45df853da7197c619e842bec6b5d84c702f02024 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 14:22:20 +0100 Subject: [PATCH 0780/1096] cleanup buggy closure dispatch code --- src/error.rs | 11 +- src/eval_context.rs | 4 +- src/memory.rs | 124 ++++++++++++++---- src/terminator/mod.rs | 51 ++++--- src/vtable.rs | 10 +- .../fn_ptr_as_closure_trait_object.rs | 15 +++ tests/run-pass/mir_coercions.rs | 80 +++++++++++ tests/run-pass/multi_arg_closure.rs | 8 ++ 8 files changed, 255 insertions(+), 48 deletions(-) create mode 100644 tests/run-pass/fn_ptr_as_closure_trait_object.rs create mode 100644 tests/run-pass/mir_coercions.rs create mode 100644 tests/run-pass/multi_arg_closure.rs diff --git a/src/error.rs b/src/error.rs index a7168bd19115e..e0434c48e51b8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,7 @@ use std::fmt; use rustc::mir; use rustc::ty::{BareFnTy, Ty, FnSig, layout}; use syntax::abi::Abi; -use memory::Pointer; +use memory::{Pointer, Function}; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -53,6 +53,9 @@ pub enum EvalError<'tcx> { DeallocatedFrozenMemory, Layout(layout::LayoutError<'tcx>), Unreachable, + ExpectedConcreteFunction(Function<'tcx>), + ExpectedDropGlue(Function<'tcx>), + ManuallyCalledDropGlue, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -125,6 +128,12 @@ impl<'tcx> Error for EvalError<'tcx> { "attempted to get length of a null terminated string, but no null found before end of allocation", EvalError::Unreachable => "entered unreachable code", + EvalError::ExpectedConcreteFunction(_) => + "tried to use glue function as function", + EvalError::ExpectedDropGlue(_) => + "tried to use non-drop-glue function as drop glue", + EvalError::ManuallyCalledDropGlue => + "tried to manually invoke drop glue", } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 100224fba69bc..d372f37698d73 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -659,9 +659,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(unsafe_fn_ty) => { let src = self.eval_operand(operand)?; let ptr = src.read_ptr(&self.memory)?; - let (def_id, substs, _, _) = self.memory.get_fn(ptr.alloc_id)?; + let fn_def = self.memory.get_fn(ptr.alloc_id)?.expect_concrete()?; let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); - let fn_ptr = self.memory.create_fn_ptr(self.tcx, def_id, substs, unsafe_fn_ty); + let fn_ptr = self.memory.create_fn_ptr(self.tcx, fn_def.def_id, fn_def.substs, unsafe_fn_ty); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), diff --git a/src/memory.rs b/src/memory.rs index 419a24a5db701..3b7356cf06f9e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -94,14 +94,47 @@ impl Pointer { } } -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -struct FunctionDefinition<'tcx> { +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] +/// Identifies a specific monomorphized function +pub struct FunctionDefinition<'tcx> { pub def_id: DefId, pub substs: &'tcx Substs<'tcx>, pub abi: Abi, pub sig: &'tcx ty::FnSig<'tcx>, } +/// Either a concrete function, or a glue function +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] +pub enum Function<'tcx> { + /// A function or method created by compiling code + Concrete(FunctionDefinition<'tcx>), + /// Glue required to call a regular function through a Fn(Mut|Once) trait object + FnDefAsTraitObject(FunctionDefinition<'tcx>), + /// Glue required to call the actual drop impl's `drop` method. + /// Drop glue takes the `self` by value, while `Drop::drop` take `&mut self` + DropGlue(FunctionDefinition<'tcx>), + /// Glue required to treat the ptr part of a fat pointer + /// as a function pointer + FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>), + /// Glue for Closures + Closure(FunctionDefinition<'tcx>), +} + +impl<'tcx> Function<'tcx> { + pub fn expect_concrete(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { + match self { + Function::Concrete(fn_def) => Ok(fn_def), + other => Err(EvalError::ExpectedConcreteFunction(other)), + } + } + pub fn expect_drop_glue(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { + match self { + Function::DropGlue(fn_def) => Ok(fn_def), + other => Err(EvalError::ExpectedDropGlue(other)), + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// @@ -115,9 +148,9 @@ pub struct Memory<'a, 'tcx> { memory_size: u64, /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. - functions: HashMap>, + functions: HashMap>, /// Inverse map of `functions` so we don't allocate a new pointer every time we need one - function_alloc_cache: HashMap, AllocId>, + function_alloc_cache: HashMap, AllocId>, next_id: AllocId, pub layout: &'a TargetDataLayout, /// List of memory regions containing packed structures @@ -160,35 +193,61 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { abi: fn_ty.abi, sig: fn_ty.sig, }); - self.create_fn_alloc(FunctionDefinition { + self.create_fn_alloc(Function::Closure(FunctionDefinition { def_id, substs: substs.substs, abi: fn_ty.abi, // FIXME: why doesn't this compile? //sig: tcx.erase_late_bound_regions(&fn_ty.sig), sig: fn_ty.sig.skip_binder(), - }) + })) + } + + pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition { + def_id, + substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), + })) + } + + pub fn create_fn_ptr_as_trait_glue(&mut self, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder())) + } + + pub fn create_drop_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + self.create_fn_alloc(Function::DropGlue(FunctionDefinition { + def_id, + substs, + abi: fn_ty.abi, + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), + })) } pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { - self.create_fn_alloc(FunctionDefinition { + self.create_fn_alloc(Function::Concrete(FunctionDefinition { def_id, substs, abi: fn_ty.abi, // FIXME: why doesn't this compile? //sig: tcx.erase_late_bound_regions(&fn_ty.sig), sig: fn_ty.sig.skip_binder(), - }) + })) } - fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer { + fn create_fn_alloc(&mut self, def: Function<'tcx>) -> Pointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { return Pointer::new(alloc_id, 0); } let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; - self.functions.insert(id, def.clone()); + self.functions.insert(id, def); self.function_alloc_cache.insert(def, id); Pointer::new(id, 0) } @@ -381,15 +440,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Abi, &'tcx ty::FnSig<'tcx>)> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, Function<'tcx>> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { - Some(&FunctionDefinition { - def_id, - substs, - abi, - sig, - }) => Ok((def_id, substs, abi, sig)), + Some(&fndef) => Ok(fndef), None => match self.alloc_map.get(&id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), @@ -418,14 +472,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { (Some(a), None) => a, - (None, Some(fn_def)) => { - let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); - let abi = if fn_def.abi == Abi::Rust { - format!("") - } else { - format!("extern {} ", fn_def.abi) - }; - trace!("{} function pointer: {}: {}{}", msg, name, abi, fn_def.sig); + (None, Some(&Function::Concrete(fn_def))) => { + trace!("{} {}", msg, dump_fn_def(fn_def)); + continue; + }, + (None, Some(&Function::DropGlue(fn_def))) => { + trace!("{} drop glue for {}", msg, dump_fn_def(fn_def)); + continue; + }, + (None, Some(&Function::FnDefAsTraitObject(fn_def))) => { + trace!("{} fn as Fn glue for {}", msg, dump_fn_def(fn_def)); + continue; + }, + (None, Some(&Function::FnPtrAsTraitObject(fn_def))) => { + trace!("{} fn ptr as Fn glue (signature: {:?})", msg, fn_def); + continue; + }, + (None, Some(&Function::Closure(fn_def))) => { + trace!("{} closure glue for {}", msg, dump_fn_def(fn_def)); continue; }, (None, None) => { @@ -476,6 +540,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } +fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String { + let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); + let abi = if fn_def.abi == Abi::Rust { + format!("") + } else { + format!("extern {} ", fn_def.abi) + }; + format!("function pointer: {}: {}{}", name, abi, fn_def.sig) +} + /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 95b62a0bcc074..d813e13c48e13 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -11,7 +11,7 @@ use syntax::{ast, attr, abi}; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited}; use lvalue::{Lvalue, LvalueExtra}; -use memory::Pointer; +use memory::{Pointer, FunctionDefinition, Function}; use value::PrimVal; use value::Value; @@ -89,7 +89,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; + let FunctionDefinition {def_id, substs, abi, sig} = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); let bare_sig = self.tcx.erase_regions(&bare_sig); // transmuting function pointers in miri is fine as long as the number of @@ -314,9 +314,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_locals = self.frame().mir.args_iter(); // FIXME: impl ExactSizeIterator and use args_locals.len() - // FIXME: last-use-in-cap-clause works by chance, insert some arguments and it will fail - // we currently write the first argument (unit) to the return field (unit) - //assert_eq!(self.frame().mir.args_iter().count(), args.len()); + assert_eq!(arg_locals.size_hint().0, args.len()); + assert_eq!(arg_locals.size_hint().1, Some(args.len())); for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; @@ -635,24 +634,42 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; - let (def_id, substs, abi, sig) = self.memory.get_fn(fn_ptr.alloc_id)?; trace!("args: {:#?}", args); - trace!("sig: {:#?}", sig); - if abi != abi::Abi::RustCall && args.len() == 2 { - if let ty::TyTuple(wrapped_args) = args[1].1.sty { - assert_eq!(sig.inputs(), &wrapped_args[..]); + match self.memory.get_fn(fn_ptr.alloc_id)? { + Function::FnDefAsTraitObject(fn_def) => { + trace!("sig: {:#?}", fn_def.sig); + assert!(fn_def.abi != abi::Abi::RustCall); + assert_eq!(args.len(), 2); // a function item turned into a closure trait object // the first arg is just there to give use the vtable args.remove(0); self.unpack_fn_args(args)?; - return Ok((def_id, substs, Vec::new())); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + }, + Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), + Function::Concrete(fn_def) => { + trace!("sig: {:#?}", fn_def.sig); + args[0] = ( + Value::ByVal(PrimVal::Ptr(self_ptr)), + fn_def.sig.inputs()[0], + ); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + }, + Function::Closure(fn_def) => { + self.unpack_fn_args(args)?; + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + } + Function::FnPtrAsTraitObject(sig) => { + trace!("sig: {:#?}", sig); + // the first argument was the fat ptr + args.remove(0); + self.unpack_fn_args(args)?; + let fn_ptr = self.memory.read_ptr(self_ptr)?; + let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; + assert_eq!(sig, fn_def.sig); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) } } - args[0] = ( - Value::ByVal(PrimVal::Ptr(self_ptr)), - sig.inputs()[0], - ); - Ok((def_id, substs, Vec::new())) }, vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), } @@ -785,7 +802,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { - let (def_id, substs, _abi, sig) = self.memory.get_fn(drop_fn.alloc_id)?; + let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; let real_ty = sig.inputs()[0]; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs)); diff --git a/src/vtable.rs b/src/vtable.rs index 8e2607562b411..fb9ec7124cca3 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -55,12 +55,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter() } + // turn a function definition into a Fn trait object traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { match fn_ty.sty { ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_ptr(self.tcx, did, substs, bare_fn_ty))].into_iter() + vec![Some(self.memory.create_fn_as_trait_glue(self.tcx, did, substs, bare_fn_ty))].into_iter() }, - _ => bug!("bad VtableFnPointer fn_ty: {:?}", fn_ty), + ty::TyFnPtr(bare_fn_ty) => { + vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter() + }, + _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), } } @@ -94,7 +98,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; - let fn_ptr = self.memory.create_fn_ptr(self.tcx, drop_def_id, substs, fn_ty); + let fn_ptr = self.memory.create_drop_glue(self.tcx, drop_def_id, substs, fn_ty); self.memory.write_ptr(vtable, fn_ptr)?; } } diff --git a/tests/run-pass/fn_ptr_as_closure_trait_object.rs b/tests/run-pass/fn_ptr_as_closure_trait_object.rs new file mode 100644 index 0000000000000..24ae1f35bb60b --- /dev/null +++ b/tests/run-pass/fn_ptr_as_closure_trait_object.rs @@ -0,0 +1,15 @@ +fn foo() {} +fn bar(u: u32) { assert_eq!(u, 42); } +fn baa(u: u32, f: f32) { + assert_eq!(u, 42); + assert_eq!(f, 3.141); +} + +fn main() { + let f: &Fn() = &(foo as fn()); + f(); + let f: &Fn(u32) = &(bar as fn(u32)); + f(42); + let f: &Fn(u32, f32) = &(baa as fn(u32, f32)); + f(42, 3.141); +} diff --git a/tests/run-pass/mir_coercions.rs b/tests/run-pass/mir_coercions.rs new file mode 100644 index 0000000000000..36155297e32f0 --- /dev/null +++ b/tests/run-pass/mir_coercions.rs @@ -0,0 +1,80 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(coerce_unsized, unsize)] + +use std::ops::CoerceUnsized; +use std::marker::Unsize; + +fn identity_coercion(x: &(Fn(u32)->u32 + Send)) -> &Fn(u32)->u32 { + x +} +fn fn_coercions(f: &fn(u32) -> u32) -> + (unsafe fn(u32) -> u32, + &(Fn(u32) -> u32+Send)) +{ + (*f, f) +} + +fn simple_array_coercion(x: &[u8; 3]) -> &[u8] { x } + +fn square(a: u32) -> u32 { a * a } + +#[derive(PartialEq,Eq)] +struct PtrWrapper<'a, T: 'a+?Sized>(u32, u32, (), &'a T); +impl<'a, T: ?Sized+Unsize, U: ?Sized> + CoerceUnsized> for PtrWrapper<'a, T> {} + +struct TrivPtrWrapper<'a, T: 'a+?Sized>(&'a T); +impl<'a, T: ?Sized+Unsize, U: ?Sized> + CoerceUnsized> for TrivPtrWrapper<'a, T> {} + +fn coerce_ptr_wrapper(p: PtrWrapper<[u8; 3]>) -> PtrWrapper<[u8]> { + p +} + +fn coerce_triv_ptr_wrapper(p: TrivPtrWrapper<[u8; 3]>) -> TrivPtrWrapper<[u8]> { + p +} + +fn coerce_fat_ptr_wrapper(p: PtrWrapper u32+Send>) + -> PtrWrapper u32> { + p +} + +fn coerce_ptr_wrapper_poly<'a, T, Trait: ?Sized>(p: PtrWrapper<'a, T>) + -> PtrWrapper<'a, Trait> + where PtrWrapper<'a, T>: CoerceUnsized> +{ + p +} + +fn main() { + let a = [0,1,2]; + let square_local : fn(u32) -> u32 = square; + let (f,g) = fn_coercions(&square_local); + assert_eq!(f as *const (), square as *const()); + assert_eq!(g(4), 16); + assert_eq!(identity_coercion(g)(5), 25); + + assert_eq!(simple_array_coercion(&a), &a); + let w = coerce_ptr_wrapper(PtrWrapper(2,3,(),&a)); + assert!(w == PtrWrapper(2,3,(),&a) as PtrWrapper<[u8]>); + + let w = coerce_triv_ptr_wrapper(TrivPtrWrapper(&a)); + assert_eq!(&w.0, &a); + + let z = coerce_fat_ptr_wrapper(PtrWrapper(2,3,(),&square_local)); + assert_eq!((z.3)(6), 36); + + let z: PtrWrapper u32> = + coerce_ptr_wrapper_poly(PtrWrapper(2,3,(),&square_local)); + assert_eq!((z.3)(6), 36); +} diff --git a/tests/run-pass/multi_arg_closure.rs b/tests/run-pass/multi_arg_closure.rs new file mode 100644 index 0000000000000..30cfb5b685b20 --- /dev/null +++ b/tests/run-pass/multi_arg_closure.rs @@ -0,0 +1,8 @@ +fn foo(f: &mut FnMut(isize, isize) -> isize) -> isize { + f(1, 2) +} + +fn main() { + let z = foo(&mut |x, y| x * 10 + y); + assert_eq!(z, 12); +} From 74d8c8a7f429812255f34b09668288341f448e2c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 14:31:29 +0100 Subject: [PATCH 0781/1096] check the `arg_count` member of MIR instead of counting iterator length --- src/terminator/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d813e13c48e13..88dd36354caf4 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -313,9 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; let arg_locals = self.frame().mir.args_iter(); - // FIXME: impl ExactSizeIterator and use args_locals.len() - assert_eq!(arg_locals.size_hint().0, args.len()); - assert_eq!(arg_locals.size_hint().1, Some(args.len())); + assert_eq!(self.frame().mir.arg_count, args.len()); for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; From 9a7f76889a738219ae06c04e7384d23e2ea53b97 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 06:01:03 -0800 Subject: [PATCH 0782/1096] Update for changes in rustc. --- src/eval_context.rs | 2 +- src/terminator/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index d372f37698d73..402f911e9581b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -744,7 +744,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } - ty::TyTuple(fields) => Ok(fields[field_index]), + ty::TyTuple(fields, _) => Ok(fields[field_index]), ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index), diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 88dd36354caf4..1de5599136c81 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -520,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some((last, last_ty)) = args.pop() { let last_layout = self.type_layout(last_ty)?; match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields), + (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) => { let offsets = variant.offsets.iter().map(|s| s.bytes()); let last_ptr = match last { @@ -785,7 +785,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { drop, )?; }, - ty::TyTuple(fields) => { + ty::TyTuple(fields, _) => { let offsets = match *self.type_layout(ty)? { Layout::Univariant { ref variant, .. } => &variant.offsets, _ => bug!("tuples must be univariant"), From 52ae8eb7941afcbe07119bf418bc15100e5c5631 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 7 Feb 2017 07:02:45 -0800 Subject: [PATCH 0783/1096] Log global/promoted frame creation. --- src/step.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/step.rs b/src/step.rs index 33237b84637a5..744cdfdbcb0e4 100644 --- a/src/step.rs +++ b/src/step.rs @@ -161,6 +161,8 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } else { StackPopCleanup::None }; + let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + trace!("pushing stack frame for global: {}", name); this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new()) }); } @@ -205,6 +207,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let ty = this.ecx.monomorphize(mir.return_ty, this.substs); this.ecx.globals.insert(cid, Global::uninitialized(ty)); + trace!("pushing stack frame for {:?}", index); this.ecx.push_stack_frame(this.def_id, constant.span, mir, From 18f4ca3c6b38917976df442c89e15714c4c5ce43 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 17:41:30 +0100 Subject: [PATCH 0784/1096] fix drop impls for clike enums --- src/terminator/mod.rs | 1 + tests/run-pass/issue-15063.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/run-pass/issue-15063.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 88dd36354caf4..79668639dc977 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -776,6 +776,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); // nothing to do, this is zero sized (e.g. `None`) } }, + Layout::CEnum { .. } => return Ok(()), _ => bug!("{:?} is not an adt layout", layout), }; let tcx = self.tcx; diff --git a/tests/run-pass/issue-15063.rs b/tests/run-pass/issue-15063.rs new file mode 100644 index 0000000000000..726aee283e292 --- /dev/null +++ b/tests/run-pass/issue-15063.rs @@ -0,0 +1,20 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +enum Two { A, B } +impl Drop for Two { + fn drop(&mut self) { + } +} +fn main() { + let _k = Two::A; +} From 3c560f594119244af2a2d10c5f351b95de9f1b37 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 17:52:32 +0100 Subject: [PATCH 0785/1096] fix some leftover u128 errors --- src/operator.rs | 4 ++ tests/run-pass/u128.rs | 84 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tests/run-pass/u128.rs diff --git a/src/operator.rs b/src/operator.rs index c4c724c594e2a..7de006dd5c7e1 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -80,10 +80,12 @@ macro_rules! int_arithmetic { I16 => overflow!($int_op, l as i16, r as i16), I32 => overflow!($int_op, l as i32, r as i32), I64 => overflow!($int_op, l as i64, r as i64), + I128 => overflow!($int_op, l as i128, r as i128), U8 => overflow!($int_op, l as u8, r as u8), U16 => overflow!($int_op, l as u16, r as u16), U32 => overflow!($int_op, l as u32, r as u32), U64 => overflow!($int_op, l as u64, r as u64), + U128 => overflow!($int_op, l as u128, r as u128), _ => bug!("int_arithmetic should only be called on int primvals"), } }) @@ -98,10 +100,12 @@ macro_rules! int_shift { I16 => overflow!($int_op, l as i16, r), I32 => overflow!($int_op, l as i32, r), I64 => overflow!($int_op, l as i64, r), + I128 => overflow!($int_op, l as i128, r), U8 => overflow!($int_op, l as u8, r), U16 => overflow!($int_op, l as u16, r), U32 => overflow!($int_op, l as u32, r), U64 => overflow!($int_op, l as u64, r), + U128 => overflow!($int_op, l as u128, r), _ => bug!("int_shift should only be called on int primvals"), } }) diff --git a/tests/run-pass/u128.rs b/tests/run-pass/u128.rs new file mode 100644 index 0000000000000..bd68157e4bc22 --- /dev/null +++ b/tests/run-pass/u128.rs @@ -0,0 +1,84 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-stage0 +// ignore-stage1 + +// ignore-emscripten + +#![feature(i128_type)] + +fn b(t: T) -> T { t } + +fn main() { + let x: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF; + assert_eq!(0, !x); + assert_eq!(0, !x); + let y: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFE; + assert_eq!(!1, y); + assert_eq!(x, y | 1); + assert_eq!(0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFE, + y & + 0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFF); + let z: u128 = 0xABCD_EF; + assert_eq!(z * z, 0x734C_C2F2_A521); + assert_eq!(z * z * z * z, 0x33EE_0E2A_54E2_59DA_A0E7_8E41); + assert_eq!(z + z + z + z, 0x2AF3_7BC); + let k: u128 = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210; + assert_eq!(k + k, 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420); + assert_eq!(0, k - k); + assert_eq!(0x1234_5678_9ABC_DEFF_EDCB_A987_5A86_421, k - z); + assert_eq!(0x1000_0000_0000_0000_0000_0000_0000_000, + k - 0x234_5678_9ABC_DEFF_EDCB_A987_6543_210); + assert_eq!(0x6EF5_DE4C_D3BC_2AAA_3BB4_CC5D_D6EE_8, k / 42); + assert_eq!(0, k % 42); + assert_eq!(15, z % 42); + assert_eq!(0x169D_A8020_CEC18, k % 0x3ACB_FE49_FF24_AC); + assert_eq!(0x91A2_B3C4_D5E6_F7, k >> 65); + assert_eq!(0xFDB9_7530_ECA8_6420_0000_0000_0000_0000, k << 65); + assert!(k > z); + assert!(y > k); + assert!(y < x); + assert_eq!(x as u64, !0); + assert_eq!(z as u64, 0xABCD_EF); + assert_eq!(k as u64, 0xFEDC_BA98_7654_3210); + assert_eq!(k as i128, 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210); + assert_eq!((z as f64) as u128, z); + assert_eq!((z as f32) as u128, z); + assert_eq!((z as f64 * 16.0) as u128, z * 16); + assert_eq!((z as f32 * 16.0) as u128, z * 16); + let l :u128 = 432 << 100; + assert_eq!((l as f32) as u128, l); + assert_eq!((l as f64) as u128, l); + // formatting + let j: u128 = 1 << 67; + /* + assert_eq!("147573952589676412928", format!("{}", j)); + assert_eq!("80000000000000000", format!("{:x}", j)); + assert_eq!("20000000000000000000000", format!("{:o}", j)); + assert_eq!("10000000000000000000000000000000000000000000000000000000000000000000", + format!("{:b}", j)); + assert_eq!("340282366920938463463374607431768211455", + format!("{}", u128::max_value())); + assert_eq!("147573952589676412928", format!("{:?}", j)); + */ + // common traits + assert_eq!(x, b(x.clone())); + // overflow checks + assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521)); + assert_eq!((k).checked_mul(k), None); + let l: u128 = b(u128::max_value() - 10); + let o: u128 = b(17); + assert_eq!(l.checked_add(b(11)), None); + assert_eq!(l.checked_sub(l), Some(0)); + assert_eq!(o.checked_sub(b(18)), None); + assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); + assert_eq!(o.checked_shl(b(128)), None); +} From 01ac19d3585ef90d9aeb70aab501c4fbda931fca Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 19:20:16 +0100 Subject: [PATCH 0786/1096] fix `static mut` accidental dealloc or freeze --- src/error.rs | 8 ++--- src/eval_context.rs | 32 +++++++++--------- src/memory.rs | 52 +++++++++++++++++++++--------- src/step.rs | 8 ++--- src/vtable.rs | 2 +- tests/run-pass/const-vec-of-fns.rs | 29 +++++++++++++++++ tests/run-pass/static_mut.rs | 17 ++++++++++ 7 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 tests/run-pass/const-vec-of-fns.rs create mode 100644 tests/run-pass/static_mut.rs diff --git a/src/error.rs b/src/error.rs index e0434c48e51b8..30bd5beba256a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -49,8 +49,8 @@ pub enum EvalError<'tcx> { AssumptionNotHeld, InlineAsm, TypeNotPrimitive(Ty<'tcx>), - ReallocatedFrozenMemory, - DeallocatedFrozenMemory, + ReallocatedStaticMemory, + DeallocatedStaticMemory, Layout(layout::LayoutError<'tcx>), Unreachable, ExpectedConcreteFunction(Function<'tcx>), @@ -118,9 +118,9 @@ impl<'tcx> Error for EvalError<'tcx> { "cannot evaluate inline assembly", EvalError::TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", - EvalError::ReallocatedFrozenMemory => + EvalError::ReallocatedStaticMemory => "tried to reallocate frozen memory", - EvalError::DeallocatedFrozenMemory => + EvalError::DeallocatedStaticMemory => "tried to deallocate frozen memory", EvalError::Layout(_) => "rustc layout computation failed", diff --git a/src/eval_context.rs b/src/eval_context.rs index d372f37698d73..fbad94b67ae12 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -100,9 +100,11 @@ pub struct Frame<'tcx> { #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum StackPopCleanup { /// The stackframe existed to compute the initial value of a static/constant, make sure it - /// isn't modifyable afterwards. The allocation of the result is frozen iff it's an - /// actual allocation. `PrimVal`s are unmodifyable anyway. - Freeze, + /// isn't modifyable afterwards in case of constants. + /// In case of `static mut`, mark the memory to ensure it's never marked as immutable through + /// references or deallocated + /// The bool decides whether the value is mutable (true) or not (false) + MarkStatic(bool), /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), @@ -170,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; - self.memory.freeze(ptr.alloc_id)?; + self.memory.mark_static(ptr.alloc_id, false)?; Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } @@ -194,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len() as u64, 1)?; self.memory.write_bytes(ptr, bs)?; - self.memory.freeze(ptr.alloc_id)?; + self.memory.mark_static(ptr.alloc_id, false)?; PrimVal::Ptr(ptr) } @@ -310,25 +312,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { - StackPopCleanup::Freeze => if let Lvalue::Global(id) = frame.return_lvalue { + StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue { let global_value = self.globals.get_mut(&id) - .expect("global should have been cached (freeze)"); + .expect("global should have been cached (freeze/static)"); match global_value.value { - Value::ByRef(ptr) => self.memory.freeze(ptr.alloc_id)?, + Value::ByRef(ptr) => self.memory.mark_static(ptr.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.freeze(ptr.alloc_id)?; + self.memory.mark_static(ptr.alloc_id, mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.freeze(ptr.alloc_id)?; + self.memory.mark_static(ptr.alloc_id, mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.freeze(ptr.alloc_id)?; + self.memory.mark_static(ptr.alloc_id, mutable)?; } }, } assert!(global_value.mutable); - global_value.mutable = false; + global_value.mutable = mutable; } else { bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue); }, @@ -345,7 +347,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // by a constant. We could alternatively check whether the alloc_id is frozen // before calling deallocate, but this is much simpler and is probably the // rare case. - Ok(()) | Err(EvalError::DeallocatedFrozenMemory) => {}, + Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, other => return other, } } @@ -868,9 +870,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; - if !global_val.mutable { - self.memory.freeze(ptr.alloc_id)?; - } + self.memory.mark_static(ptr.alloc_id, global_val.mutable)?; let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { value: Value::ByRef(ptr), diff --git a/src/memory.rs b/src/memory.rs index 3b7356cf06f9e..8c2223215325a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -38,9 +38,19 @@ pub struct Allocation { /// The alignment of the allocation to detect unaligned reads. pub align: u64, /// Whether the allocation may be modified. - /// Use the `freeze` method of `Memory` to ensure that an error occurs, if the memory of this + /// Use the `mark_static` method of `Memory` to ensure that an error occurs, if the memory of this /// allocation is modified in the future. - pub immutable: bool, + pub static_kind: StaticKind, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum StaticKind { + /// may be deallocated without breaking miri's invariants + NotStatic, + /// may be modified, but never deallocated + Mutable, + /// may neither be modified nor deallocated + Immutable, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -272,7 +282,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, - immutable: false, + static_kind: StaticKind::NotStatic, }; let id = self.next_id; self.next_id.0 += 1; @@ -290,8 +300,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if ptr.points_to_zst() { return self.allocate(new_size, align); } - if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) { - return Err(EvalError::ReallocatedFrozenMemory); + if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { + return Err(EvalError::ReallocatedStaticMemory); } let size = self.get(ptr.alloc_id)?.bytes.len() as u64; @@ -325,8 +335,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - if self.get(ptr.alloc_id).map(|alloc| alloc.immutable).ok() == Some(true) { - return Err(EvalError::DeallocatedFrozenMemory); + if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { + return Err(EvalError::DeallocatedStaticMemory); } if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { @@ -430,8 +440,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { match self.alloc_map.get_mut(&id) { - Some(ref alloc) if alloc.immutable => Err(EvalError::ModifiedConstantMemory), - Some(alloc) => Ok(alloc), + Some(alloc) => match alloc.static_kind { + StaticKind::Mutable | + StaticKind::NotStatic => Ok(alloc), + StaticKind::Immutable => Err(EvalError::ModifiedConstantMemory), + }, None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), @@ -514,7 +527,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - let immutable = if alloc.immutable { " (immutable)" } else { "" }; + let immutable = match alloc.static_kind { + StaticKind::Mutable => "(static mut)", + StaticKind::Immutable => "(immutable)", + StaticKind::NotStatic => "", + }; trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable); if !relocations.is_empty() { @@ -607,15 +624,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx> { + /// mark an allocation as static, either mutable or not + pub fn mark_static(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(ref mut alloc) if !alloc.immutable => { - alloc.immutable = true; + Some(&mut Allocation { ref mut relocations, static_kind: ref mut kind @ StaticKind::NotStatic, .. }) => { + *kind = if mutable { + StaticKind::Mutable + } else { + StaticKind::Immutable + }; // take out the relocations vector to free the borrow on self, so we can call // freeze recursively - mem::replace(&mut alloc.relocations, Default::default()) + mem::replace(relocations, Default::default()) }, None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()), None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), @@ -623,7 +645,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - self.freeze(alloc)?; + self.mark_static(alloc, mutable)?; } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/step.rs b/src/step.rs index 33237b84637a5..0200e4580147e 100644 --- a/src/step.rs +++ b/src/step.rs @@ -156,11 +156,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let mir = this.ecx.load_mir(def_id)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() { - StackPopCleanup::Freeze - } else { - StackPopCleanup::None - }; + let cleanup = StackPopCleanup::MarkStatic(!immutable || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe()); this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new()) }); } @@ -210,7 +206,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { mir, this.substs, Lvalue::Global(cid), - StackPopCleanup::Freeze, + StackPopCleanup::MarkStatic(false), Vec::new()) }); } diff --git a/src/vtable.rs b/src/vtable.rs index fb9ec7124cca3..0b43b3494438f 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.memory.freeze(vtable.alloc_id)?; + self.memory.mark_static(vtable.alloc_id, false)?; Ok(vtable) } diff --git a/tests/run-pass/const-vec-of-fns.rs b/tests/run-pass/const-vec-of-fns.rs new file mode 100644 index 0000000000000..0338a766e2627 --- /dev/null +++ b/tests/run-pass/const-vec-of-fns.rs @@ -0,0 +1,29 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-expanded FIXME #23616 + +/*! + * Try to double-check that static fns have the right size (with or + * without dummy env ptr, as appropriate) by iterating a size-2 array. + * If the static size differs from the runtime size, the second element + * should be read as a null or otherwise wrong pointer and crash. + */ + +fn f() { } +static mut CLOSURES: &'static mut [fn()] = &mut [f as fn(), f as fn()]; + +pub fn main() { + unsafe { + for closure in &mut *CLOSURES { + (*closure)() + } + } +} diff --git a/tests/run-pass/static_mut.rs b/tests/run-pass/static_mut.rs new file mode 100644 index 0000000000000..be5830698b211 --- /dev/null +++ b/tests/run-pass/static_mut.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +static mut FOO: i32 = 42; +static BAR: Foo = Foo(unsafe { &FOO as *const _} ); + +struct Foo(*const i32); + +unsafe impl Sync for Foo {} + +fn main() { + unsafe { + assert_eq!(*BAR.0, 42); + FOO = 5; + assert_eq!(FOO, 5); + assert_eq!(*BAR.0, 5); + } +} From 98cda6cb07f09a910c493d6c16d8cb47234bf39a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 20:28:54 +0100 Subject: [PATCH 0787/1096] freeze -> static --- src/error.rs | 4 ++-- src/eval_context.rs | 10 ++++------ src/memory.rs | 6 +++--- tests/compile-fail/modifying_constants.rs | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/error.rs b/src/error.rs index 30bd5beba256a..f806110190c4d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -119,9 +119,9 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", EvalError::ReallocatedStaticMemory => - "tried to reallocate frozen memory", + "tried to reallocate static memory", EvalError::DeallocatedStaticMemory => - "tried to deallocate frozen memory", + "tried to deallocate static memory", EvalError::Layout(_) => "rustc layout computation failed", EvalError::UnterminatedCString(_) => diff --git a/src/eval_context.rs b/src/eval_context.rs index ac6fffd1e0dc3..1996bbf9dcb7e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -314,7 +314,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match frame.return_to_block { StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue { let global_value = self.globals.get_mut(&id) - .expect("global should have been cached (freeze/static)"); + .expect("global should have been cached (static)"); match global_value.value { Value::ByRef(ptr) => self.memory.mark_static(ptr.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { @@ -332,7 +332,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert!(global_value.mutable); global_value.mutable = mutable; } else { - bug!("StackPopCleanup::Freeze on: {:?}", frame.return_lvalue); + bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue); }, StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, @@ -343,10 +343,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("deallocating local"); self.memory.dump_alloc(ptr.alloc_id); match self.memory.deallocate(ptr) { - // Any frozen memory means that it belongs to a constant or something referenced - // by a constant. We could alternatively check whether the alloc_id is frozen - // before calling deallocate, but this is much simpler and is probably the - // rare case. + // We could alternatively check whether the alloc_id is static before calling + // deallocate, but this is much simpler and is probably the rare case. Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, other => return other, } diff --git a/src/memory.rs b/src/memory.rs index 8c2223215325a..0b5f90f3fc8bd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -39,7 +39,7 @@ pub struct Allocation { pub align: u64, /// Whether the allocation may be modified. /// Use the `mark_static` method of `Memory` to ensure that an error occurs, if the memory of this - /// allocation is modified in the future. + /// allocation is modified or deallocated in the future. pub static_kind: StaticKind, } @@ -626,7 +626,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as static, either mutable or not pub fn mark_static(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { - // do not use `self.get_mut(alloc_id)` here, because we might have already frozen a + // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { Some(&mut Allocation { ref mut relocations, static_kind: ref mut kind @ StaticKind::NotStatic, .. }) => { @@ -636,7 +636,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { StaticKind::Immutable }; // take out the relocations vector to free the borrow on self, so we can call - // freeze recursively + // mark recursively mem::replace(relocations, Default::default()) }, None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()), diff --git a/tests/compile-fail/modifying_constants.rs b/tests/compile-fail/modifying_constants.rs index 5a8fd189aae7d..cb2e7217d5797 100644 --- a/tests/compile-fail/modifying_constants.rs +++ b/tests/compile-fail/modifying_constants.rs @@ -1,5 +1,5 @@ fn main() { - let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is frozen, not the pointee + let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is marked static, not the pointee let y = unsafe { &mut *(x as *const i32 as *mut i32) }; *y = 42; //~ ERROR tried to modify constant memory assert_eq!(*x, 42); From aaba692effbcaff0acc6a0700f66818b648cdf47 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Feb 2017 20:38:23 +0100 Subject: [PATCH 0788/1096] add regression test for #120 --- tests/run-pass/recursive_static.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/run-pass/recursive_static.rs diff --git a/tests/run-pass/recursive_static.rs b/tests/run-pass/recursive_static.rs new file mode 100644 index 0000000000000..5b27324964b47 --- /dev/null +++ b/tests/run-pass/recursive_static.rs @@ -0,0 +1,11 @@ +#![feature(static_recursion)] + +struct S(&'static S); +static S1: S = S(&S2); +static S2: S = S(&S1); + +fn main() { + let p: *const S = S2.0; + let q: *const S = &S1; + assert_eq!(p, q); +} From fbfd2d4bca94ba2d19b89724d8450cbff52a0484 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Feb 2017 09:17:48 +0100 Subject: [PATCH 0789/1096] re-add spaces before static kind --- src/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 0b5f90f3fc8bd..8f0c3a5622c96 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -528,8 +528,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } let immutable = match alloc.static_kind { - StaticKind::Mutable => "(static mut)", - StaticKind::Immutable => "(immutable)", + StaticKind::Mutable => " (static mut)", + StaticKind::Immutable => " (immutable)", StaticKind::NotStatic => "", }; trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable); From 3db6ec3f11385c17e5f712cadfb3d31417ac166a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Feb 2017 15:32:49 +0100 Subject: [PATCH 0790/1096] prevent more deallocations of statics --- src/eval_context.rs | 8 +++++++- src/lvalue.rs | 6 ++++++ tests/run-pass/issue-5917.rs | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/issue-5917.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 1996bbf9dcb7e..7ea4003b0f74a 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -329,6 +329,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, } + // see comment on `initialized` field + assert!(!global_value.initialized); + global_value.initialized = true; assert!(global_value.mutable); global_value.mutable = mutable; } else { @@ -868,7 +871,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; - self.memory.mark_static(ptr.alloc_id, global_val.mutable)?; + // see comment on `initialized` field + if global_val.initialized { + self.memory.mark_static(ptr.alloc_id, global_val.mutable)?; + } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { value: Value::ByRef(ptr), diff --git a/src/lvalue.rs b/src/lvalue.rs index df1f166c074e9..d54c27904763c 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -57,6 +57,11 @@ pub struct GlobalId<'tcx> { #[derive(Copy, Clone, Debug)] pub struct Global<'tcx> { pub(super) value: Value, + /// Only used in `force_allocation` to ensure we don't mark the memory + /// before the static is initialized. It is possible to convert a + /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets + /// lifted to an allocation before the static is fully initialized + pub(super) initialized: bool, pub(super) mutable: bool, pub(super) ty: Ty<'tcx>, } @@ -102,6 +107,7 @@ impl<'tcx> Global<'tcx> { value: Value::ByVal(PrimVal::Undef), mutable: true, ty, + initialized: false, } } } diff --git a/tests/run-pass/issue-5917.rs b/tests/run-pass/issue-5917.rs new file mode 100644 index 0000000000000..112ad0185d8cb --- /dev/null +++ b/tests/run-pass/issue-5917.rs @@ -0,0 +1,17 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +struct T (&'static [isize]); +static t : T = T (&[5, 4, 3]); +pub fn main () { + let T(ref v) = t; + assert_eq!(v[0], 5); +} From e23fc79d257095b1347dab58e369d55063857f15 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Feb 2017 15:38:48 +0100 Subject: [PATCH 0791/1096] silence some style warning --- tests/run-pass/issue-5917.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/issue-5917.rs b/tests/run-pass/issue-5917.rs index 112ad0185d8cb..69b95f2cd7e10 100644 --- a/tests/run-pass/issue-5917.rs +++ b/tests/run-pass/issue-5917.rs @@ -10,8 +10,8 @@ struct T (&'static [isize]); -static t : T = T (&[5, 4, 3]); +static STATIC : T = T (&[5, 4, 3]); pub fn main () { - let T(ref v) = t; + let T(ref v) = STATIC; assert_eq!(v[0], 5); } From 080d3e435519e2665502c8bae6c3168ae4232e33 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Feb 2017 16:27:28 +0100 Subject: [PATCH 0792/1096] properly prevent recursive statics from marking each other --- src/eval_context.rs | 16 +++++++++------- src/memory.rs | 24 ++++++++++++++++++++---- src/vtable.rs | 2 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7ea4003b0f74a..dedb8a53b718b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len() as u64, 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; - self.memory.mark_static(ptr.alloc_id, false)?; + self.memory.mark_static_initalized(ptr.alloc_id, false)?; Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } @@ -194,9 +194,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { + // FIXME: cache these allocs let ptr = self.memory.allocate(bs.len() as u64, 1)?; self.memory.write_bytes(ptr, bs)?; - self.memory.mark_static(ptr.alloc_id, false)?; + self.memory.mark_static_initalized(ptr.alloc_id, false)?; PrimVal::Ptr(ptr) } @@ -316,16 +317,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let global_value = self.globals.get_mut(&id) .expect("global should have been cached (static)"); match global_value.value { - Value::ByRef(ptr) => self.memory.mark_static(ptr.alloc_id, mutable)?, + Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.mark_static(ptr.alloc_id, mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.mark_static(ptr.alloc_id, mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.mark_static(ptr.alloc_id, mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; } }, } @@ -870,10 +871,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr) => Lvalue::from_ptr(ptr), _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; + self.memory.mark_static(ptr.alloc_id); self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; // see comment on `initialized` field if global_val.initialized { - self.memory.mark_static(ptr.alloc_id, global_val.mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { diff --git a/src/memory.rs b/src/memory.rs index 8f0c3a5622c96..6dee7ce49a1df 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -38,7 +38,7 @@ pub struct Allocation { /// The alignment of the allocation to detect unaligned reads. pub align: u64, /// Whether the allocation may be modified. - /// Use the `mark_static` method of `Memory` to ensure that an error occurs, if the memory of this + /// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this /// allocation is modified or deallocated in the future. pub static_kind: StaticKind, } @@ -152,6 +152,11 @@ impl<'tcx> Function<'tcx> { pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, + /// Set of statics, constants, promoteds, vtables, ... to prevent `mark_static_initalized` from stepping + /// out of its own allocations. + /// This set only contains statics backed by an allocation. If they are ByVal or ByValPair they + /// are not here, but will be inserted once they become ByRef. + static_alloc: HashSet, /// Number of virtual bytes allocated memory_usage: u64, /// Maximum number of virtual bytes that may be allocated @@ -189,6 +194,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { memory_size: max_memory, memory_usage: 0, packed: BTreeSet::new(), + static_alloc: HashSet::new(), } } @@ -624,8 +630,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - /// mark an allocation as static, either mutable or not - pub fn mark_static(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { + /// mark an allocation as being the entry point to a static (see `static_alloc` field) + pub fn mark_static(&mut self, alloc_id: AllocId) { + if !self.static_alloc.insert(alloc_id) { + bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); + } + } + + /// mark an allocation as static and initialized, either mutable or not + pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { @@ -645,7 +658,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - self.mark_static(alloc, mutable)?; + // relocations into other statics are not "inner allocations" + if !self.static_alloc.contains(&alloc) { + self.mark_static_initalized(alloc, mutable)?; + } } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/vtable.rs b/src/vtable.rs index 0b43b3494438f..bfae608ecbc10 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.memory.mark_static(vtable.alloc_id, false)?; + self.memory.mark_static_initalized(vtable.alloc_id, false)?; Ok(vtable) } From 4beb774caac83e3a80662c7f141b16cce0404cb1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 8 Feb 2017 17:24:20 +0100 Subject: [PATCH 0793/1096] don't mark the zst allocation as static --- src/memory.rs | 2 +- tests/run-pass/issue-31267-additional.rs | 31 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/issue-31267-additional.rs diff --git a/src/memory.rs b/src/memory.rs index 6dee7ce49a1df..cbef71c68f6da 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -632,7 +632,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as being the entry point to a static (see `static_alloc` field) pub fn mark_static(&mut self, alloc_id: AllocId) { - if !self.static_alloc.insert(alloc_id) { + if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); } } diff --git a/tests/run-pass/issue-31267-additional.rs b/tests/run-pass/issue-31267-additional.rs new file mode 100644 index 0000000000000..90160ebcdf94f --- /dev/null +++ b/tests/run-pass/issue-31267-additional.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_variables)] + +#![feature(associated_consts)] + +#[derive(Clone, Copy, Debug)] +struct Bar; + +const BAZ: Bar = Bar; + +#[derive(Debug)] +struct Foo([Bar; 1]); + +struct Biz; + +impl Biz { + const BAZ: Foo = Foo([BAZ; 1]); +} + +fn main() { + let foo = Biz::BAZ; +} From 1844381ad024f322f7427d03ac261d131eb0e86b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 08:38:01 +0100 Subject: [PATCH 0794/1096] compute the offset of dst fields by checking the vtable --- src/lvalue.rs | 11 ++++++++++- src/terminator/intrinsic.rs | 2 +- tests/run-pass/dst-field-align.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass/dst-field-align.rs diff --git a/src/lvalue.rs b/src/lvalue.rs index d54c27904763c..2cce67689d72e 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -214,7 +214,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let ptr = base_ptr.offset(offset.bytes()); + let offset = match base_extra { + LvalueExtra::Vtable(tab) => { + let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; + // magical formula taken from rustc + (offset.bytes() + (align - 1)) & (-(align as i64) as u64) + } + _ => offset.bytes(), + }; + + let ptr = base_ptr.offset(offset); if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 811ae7888d729..44a636997d952 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -405,7 +405,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn size_and_align_of_dst( + pub fn size_and_align_of_dst( &self, ty: ty::Ty<'tcx>, value: Value, diff --git a/tests/run-pass/dst-field-align.rs b/tests/run-pass/dst-field-align.rs new file mode 100644 index 0000000000000..ce3fb82cb3f28 --- /dev/null +++ b/tests/run-pass/dst-field-align.rs @@ -0,0 +1,30 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +struct Foo { + a: u16, + b: T +} + +trait Bar { + fn get(&self) -> usize; +} + +impl Bar for usize { + fn get(&self) -> usize { *self } +} + +fn main() { + let f : Foo = Foo { a: 0, b: 11 }; + let f : &Foo = &f; + assert_eq!(f.b.get(), 11); +} From 8030800b15931f50dbe8944addc1afb4c3750a67 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 08:50:35 +0100 Subject: [PATCH 0795/1096] use pre-existing `abi_align` method instead of magic formula --- src/lvalue.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 2cce67689d72e..e86c480aa0b66 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::layout::Size; +use rustc::ty::layout::{Size, Align}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; @@ -217,8 +217,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = match base_extra { LvalueExtra::Vtable(tab) => { let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; - // magical formula taken from rustc - (offset.bytes() + (align - 1)) & (-(align as i64) as u64) + offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() } _ => offset.bytes(), }; From 6aed897c70abdd1171545fbe5695cc89797ffa9c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 09:40:23 +0100 Subject: [PATCH 0796/1096] 1 > -1 --- src/operator.rs | 4 +++ src/value.rs | 8 ++++++ tests/run-pass/issue-15523-big.rs | 48 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/run-pass/issue-15523-big.rs diff --git a/src/operator.rs b/src/operator.rs index 7de006dd5c7e1..2823e3edbea9e 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -235,9 +235,13 @@ pub fn binary_op<'tcx>( (Eq, _) => PrimVal::from_bool(l == r), (Ne, _) => PrimVal::from_bool(l != r), + (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), (Lt, _) => PrimVal::from_bool(l < r), + (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), (Le, _) => PrimVal::from_bool(l <= r), + (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), (Gt, _) => PrimVal::from_bool(l > r), + (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), (Ge, _) => PrimVal::from_bool(l >= r), (BitOr, _) => PrimVal::Bytes(l | r), diff --git a/src/value.rs b/src/value.rs index 8d153528d4f08..e812d116286ac 100644 --- a/src/value.rs +++ b/src/value.rs @@ -214,6 +214,14 @@ impl PrimValKind { } } + pub fn is_signed_int(self) -> bool { + use self::PrimValKind::*; + match self { + I8 | I16 | I32 | I64 | I128 => true, + _ => false, + } + } + pub fn from_uint_size(size: u64) -> Self { match size { 1 => PrimValKind::U8, diff --git a/tests/run-pass/issue-15523-big.rs b/tests/run-pass/issue-15523-big.rs new file mode 100644 index 0000000000000..33c81cab3817b --- /dev/null +++ b/tests/run-pass/issue-15523-big.rs @@ -0,0 +1,48 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Issue 15523: derive(PartialOrd) should use the provided +// discriminant values for the derived ordering. +// +// This test is checking corner cases that arise when you have +// 64-bit values in the variants. + +#[derive(PartialEq, PartialOrd)] +#[repr(u64)] +enum Eu64 { + Pos2 = 2, + PosMax = !0, + Pos1 = 1, +} + +#[derive(PartialEq, PartialOrd)] +#[repr(i64)] +enum Ei64 { + Pos2 = 2, + Neg1 = -1, + NegMin = 1 << 63, + PosMax = !(1 << 63), + Pos1 = 1, +} + +fn main() { + assert!(Eu64::Pos2 > Eu64::Pos1); + assert!(Eu64::Pos2 < Eu64::PosMax); + assert!(Eu64::Pos1 < Eu64::PosMax); + + + assert!(Ei64::Pos2 > Ei64::Pos1); + assert!(Ei64::Pos2 > Ei64::Neg1); + assert!(Ei64::Pos1 > Ei64::Neg1); + assert!(Ei64::Pos2 > Ei64::NegMin); + assert!(Ei64::Pos1 > Ei64::NegMin); + assert!(Ei64::Pos2 < Ei64::PosMax); + assert!(Ei64::Pos1 < Ei64::PosMax); +} From 250f66562c4209b367325e718c845d11eeac0619 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 10:34:23 +0100 Subject: [PATCH 0797/1096] ignore `print!`, turn `panic!` into a EvalError --- src/error.rs | 3 +++ src/terminator/mod.rs | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index f806110190c4d..bc6510b1f129f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -56,6 +56,7 @@ pub enum EvalError<'tcx> { ExpectedConcreteFunction(Function<'tcx>), ExpectedDropGlue(Function<'tcx>), ManuallyCalledDropGlue, + Panic, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -134,6 +135,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to use non-drop-glue function as drop glue", EvalError::ManuallyCalledDropGlue => "tried to manually invoke drop glue", + EvalError::Panic => + "the evaluated program panicked", } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e8fe9865783cb..6f6bb4b59789b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -292,7 +292,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - let mir = self.load_mir(resolved_def_id)?; + let mir = match self.load_mir(resolved_def_id) { + Ok(mir) => mir, + Err(EvalError::NoMirFor(path)) => { + match &path[..] { + // let's just ignore all output for now + "std::io::_print" => { + self.goto_block(destination.unwrap().1); + return Ok(()); + }, + "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + _ => {}, + } + return Err(EvalError::NoMirFor(path)); + }, + Err(other) => return Err(other), + }; let (return_lvalue, return_to_block) = match destination { Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), None => { From fb2d393427021adc1a09f938c9d0a5190607c30c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 10:59:42 +0100 Subject: [PATCH 0798/1096] update tests --- tests/compile-fail/env_args.rs | 4 +--- tests/compile-fail/panic.rs | 5 +++++ tests/compile-fail/unimplemented.rs | 5 ----- 3 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 tests/compile-fail/panic.rs delete mode 100644 tests/compile-fail/unimplemented.rs diff --git a/tests/compile-fail/env_args.rs b/tests/compile-fail/env_args.rs index ac061920b53c1..fe17e0a7b4897 100644 --- a/tests/compile-fail/env_args.rs +++ b/tests/compile-fail/env_args.rs @@ -1,6 +1,4 @@ -//error-pattern: no mir for `std::env::args` - fn main() { - let x = std::env::args(); + let x = std::env::args(); //~ ERROR miri does not support program arguments assert_eq!(x.count(), 1); } diff --git a/tests/compile-fail/panic.rs b/tests/compile-fail/panic.rs new file mode 100644 index 0000000000000..0d594f9bd4c3b --- /dev/null +++ b/tests/compile-fail/panic.rs @@ -0,0 +1,5 @@ +//error-pattern: the evaluated program panicked + +fn main() { + assert_eq!(5, 6); +} diff --git a/tests/compile-fail/unimplemented.rs b/tests/compile-fail/unimplemented.rs deleted file mode 100644 index 9611631a47a3b..0000000000000 --- a/tests/compile-fail/unimplemented.rs +++ /dev/null @@ -1,5 +0,0 @@ -//error-pattern:begin_panic_fmt - -fn main() { - assert_eq!(5, 6); -} From 0d3cee2db3b6eb6e3c554200b1776dc68c4dc6e0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 11:15:56 +0100 Subject: [PATCH 0799/1096] fix size of dst in size_of_val intrinsic --- src/terminator/intrinsic.rs | 14 ++++---------- tests/run-pass/issue-35815.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 tests/run-pass/issue-35815.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 811ae7888d729..24345cfce0115 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::layout::Layout; +use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; @@ -426,10 +426,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (sized_size, sized_align) = match *layout { ty::layout::Layout::Univariant { ref variant, .. } => { - // The offset of the start of the last field gives the size of the - // sized part of the type. - let size = variant.offsets.last().map_or(0, |f| f.bytes()); - (size, variant.align.abi()) + (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align.abi()) } _ => { bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", @@ -470,11 +467,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // `(size + (align-1)) & -align` - if size & (align - 1) != 0 { - Ok((size + align, align)) - } else { - Ok((size, align)) - } + let size = Size::from_bytes(size).abi_align(Align::from_bytes(align, align).unwrap()).bytes(); + Ok((size, align)) } ty::TyDynamic(..) => { let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; diff --git a/tests/run-pass/issue-35815.rs b/tests/run-pass/issue-35815.rs new file mode 100644 index 0000000000000..619542926500b --- /dev/null +++ b/tests/run-pass/issue-35815.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem; + +struct Foo { + a: i64, + b: bool, + c: T, +} + +fn main() { + let foo: &Foo = &Foo { a: 1, b: false, c: 2i32 }; + let foo_unsized: &Foo = foo; + assert_eq!(mem::size_of_val(foo), mem::size_of_val(foo_unsized)); +} From e6006c35fba74cbd8779beec09b30bf47b01e01e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 13:00:30 +0100 Subject: [PATCH 0800/1096] don't unnecessarily convert Align -> u64 -> Align --- src/terminator/intrinsic.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 24345cfce0115..fbee1c02bff7c 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -426,14 +426,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (sized_size, sized_align) = match *layout { ty::layout::Layout::Univariant { ref variant, .. } => { - (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align.abi()) + (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align) } _ => { bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}", ty, layout); } }; - debug!("DST {} statically sized prefix size: {} align: {}", + debug!("DST {} statically sized prefix size: {} align: {:?}", ty, sized_size, sized_align); // Recurse to get the size of the dynamically sized field (must be @@ -454,7 +454,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). - let align = ::std::cmp::max(sized_align, unsized_align); + let align = sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap()); // Issue #27023: must add any necessary padding to `size` // (to make it a multiple of `align`) before returning it. @@ -467,8 +467,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // `(size + (align-1)) & -align` - let size = Size::from_bytes(size).abi_align(Align::from_bytes(align, align).unwrap()).bytes(); - Ok((size, align)) + let size = Size::from_bytes(size).abi_align(align).bytes(); + Ok((size, align.abi())) } ty::TyDynamic(..) => { let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; From d23c3ae516d342c0b736075d8720b8bac14d2227 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 13:00:38 +0100 Subject: [PATCH 0801/1096] fix a failing test --- tests/run-pass/issue-35815.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-pass/issue-35815.rs b/tests/run-pass/issue-35815.rs index 619542926500b..216e06c0732c8 100644 --- a/tests/run-pass/issue-35815.rs +++ b/tests/run-pass/issue-35815.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(dead_code)] + use std::mem; struct Foo { From 0f183dc8667040283c6b9c8a35db6212f5395c1d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 15:07:50 +0100 Subject: [PATCH 0802/1096] we cannot panic, thus `panicking` always returns false --- src/terminator/mod.rs | 9 +++++++++ .../send-is-not-static-par-for.rs | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) rename tests/{compile-fail => run-pass}/send-is-not-static-par-for.rs (96%) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6f6bb4b59789b..a3ebcd929039c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -305,6 +305,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), "std::panicking::rust_panic_with_hook" | "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + "std::panicking::panicking" | + "std::rt::panicking" => { + let (lval, block) = destination.expect("std::rt::panicking does not diverge"); + // we abort on panic -> `std::rt::panicking` always returns true + let bool = self.tcx.types.bool; + self.write_primval(lval, PrimVal::from_bool(false), bool)?; + self.goto_block(block); + return Ok(()); + } _ => {}, } return Err(EvalError::NoMirFor(path)); diff --git a/tests/compile-fail/send-is-not-static-par-for.rs b/tests/run-pass/send-is-not-static-par-for.rs similarity index 96% rename from tests/compile-fail/send-is-not-static-par-for.rs rename to tests/run-pass/send-is-not-static-par-for.rs index afb401a919e91..1b913aed4c89e 100644 --- a/tests/compile-fail/send-is-not-static-par-for.rs +++ b/tests/run-pass/send-is-not-static-par-for.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//error-pattern: no mir for `std:: - use std::sync::Mutex; fn par_for(iter: I, f: F) From f6fbd060ad72e0f00e78241594e46184650f0267 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 15:21:37 +0100 Subject: [PATCH 0803/1096] no mir for mutex::lock in windows-gnu --- tests/run-pass/send-is-not-static-par-for.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-pass/send-is-not-static-par-for.rs b/tests/run-pass/send-is-not-static-par-for.rs index 1b913aed4c89e..60e5048432bac 100644 --- a/tests/run-pass/send-is-not-static-par-for.rs +++ b/tests/run-pass/send-is-not-static-par-for.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//ignore-windows-gnu + use std::sync::Mutex; fn par_for(iter: I, f: F) From 6e9e7af8e78de03604eaf22de195542c52f17d88 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Thu, 9 Feb 2017 06:58:20 -0800 Subject: [PATCH 0804/1096] Fix comment. --- src/terminator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a3ebcd929039c..2a9fe179fa22c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -308,7 +308,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "std::panicking::panicking" | "std::rt::panicking" => { let (lval, block) = destination.expect("std::rt::panicking does not diverge"); - // we abort on panic -> `std::rt::panicking` always returns true + // we abort on panic -> `std::rt::panicking` always returns false let bool = self.tcx.types.bool; self.write_primval(lval, PrimVal::from_bool(false), bool)?; self.goto_block(block); From 8c2832f4192501acb54547641c666b776e6ec90e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 18:03:21 +0100 Subject: [PATCH 0805/1096] add the full test from rust --- tests/run-pass/dst-field-align.rs | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/run-pass/dst-field-align.rs b/tests/run-pass/dst-field-align.rs index ce3fb82cb3f28..5631b65ed9d8a 100644 --- a/tests/run-pass/dst-field-align.rs +++ b/tests/run-pass/dst-field-align.rs @@ -23,8 +23,55 @@ impl Bar for usize { fn get(&self) -> usize { *self } } +struct Baz { + a: T +} + +struct HasDrop { + ptr: Box, + data: T +} + fn main() { + // Test that zero-offset works properly + let b : Baz = Baz { a: 7 }; + assert_eq!(b.a.get(), 7); + let b : &Baz = &b; + assert_eq!(b.a.get(), 7); + + // Test that the field is aligned properly let f : Foo = Foo { a: 0, b: 11 }; + assert_eq!(f.b.get(), 11); + let ptr1 : *const u8 = &f.b as *const _ as *const u8; + let f : &Foo = &f; + let ptr2 : *const u8 = &f.b as *const _ as *const u8; assert_eq!(f.b.get(), 11); + + // The pointers should be the same + assert_eq!(ptr1, ptr2); + + // Test that nested DSTs work properly + let f : Foo> = Foo { a: 0, b: Foo { a: 1, b: 17 }}; + assert_eq!(f.b.b.get(), 17); + let f : &Foo> = &f; + assert_eq!(f.b.b.get(), 17); + + // Test that get the pointer via destructuring works + + let f : Foo = Foo { a: 0, b: 11 }; + let f : &Foo = &f; + let &Foo { a: _, b: ref bar } = f; + assert_eq!(bar.get(), 11); + + // Make sure that drop flags don't screw things up + + let d : HasDrop> = HasDrop { + ptr: Box::new(0), + data: Baz { a: [1,2,3,4] } + }; + assert_eq!([1,2,3,4], d.data.a); + + let d : &HasDrop> = &d; + assert_eq!(&[1,2,3,4], &d.data.a); } From 06a02187babfbe5b6a5abdee9b5625bed7a15493 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 18:12:59 +0100 Subject: [PATCH 0806/1096] move drop code into its own file --- src/terminator/drop.rs | 236 +++++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 226 +-------------------------------------- 2 files changed, 240 insertions(+), 222 deletions(-) create mode 100644 src/terminator/drop.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs new file mode 100644 index 0000000000000..32c5073421b11 --- /dev/null +++ b/src/terminator/drop.rs @@ -0,0 +1,236 @@ +use rustc::hir::def_id::DefId; +use rustc::ty::layout::Layout; +use rustc::ty::subst::{Substs, Kind}; +use rustc::ty::{self, Ty}; +use rustc::mir; +use syntax::codemap::Span; + +use error::{EvalError, EvalResult}; +use eval_context::{EvalContext, monomorphize_field_ty, StackPopCleanup}; +use lvalue::{Lvalue, LvalueExtra}; +use memory::{Pointer, FunctionDefinition}; +use value::PrimVal; +use value::Value; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + + /// Creates stack frames for all drop impls. See `drop` for the actual content. + pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { + // add them to the stack in reverse order, because the impl that needs to run the last + // is the one that needs to be at the bottom of the stack + for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { + let mir = self.load_mir(drop_def_id)?; + trace!("substs for drop glue: {:?}", substs); + self.push_stack_frame( + drop_def_id, + span, + mir, + substs, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + Vec::new(), + )?; + let mut arg_locals = self.frame().mir.args_iter(); + let first = arg_locals.next().expect("drop impl has self arg"); + assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; + let ty = self.frame().mir.local_decls[first].ty; + self.write_value(self_arg, dest, ty)?; + } + Ok(()) + } + + /// push DefIds of drop impls and their argument on the given vector + pub fn drop( + &mut self, + lval: Lvalue<'tcx>, + ty: Ty<'tcx>, + drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, + ) -> EvalResult<'tcx> { + if !self.type_needs_drop(ty) { + debug!("no need to drop {:?}", ty); + return Ok(()); + } + trace!("-need to drop {:?} at {:?}", ty, lval); + + match ty.sty { + // special case `Box` to deallocate the inner allocation + ty::TyAdt(ref def, _) if def.is_box() => { + let contents_ty = ty.boxed_ty(); + let val = self.read_lvalue(lval); + // we are going through the read_value path, because that already does all the + // checks for the trait object types. We'd only be repeating ourselves here. + let val = self.follow_by_ref_value(val, ty)?; + trace!("box dealloc on {:?}", val); + match val { + Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), + Value::ByVal(ptr) => { + assert!(self.type_is_sized(contents_ty)); + let contents_ptr = ptr.to_ptr()?; + self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; + }, + Value::ByValPair(prim_ptr, extra) => { + let ptr = prim_ptr.to_ptr()?; + let extra = match self.tcx.struct_tail(contents_ty).sty { + ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), + ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), + _ => bug!("invalid fat pointer type: {}", ty), + }; + self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; + }, + } + let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); + let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); + // this is somewhat hacky, but hey, there's no representation difference between + // pointers and references, so + // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) + // is the same as + // fn drop(&mut self) if Self is Box + drop.push((box_free_fn, val, substs)); + }, + + ty::TyAdt(adt_def, substs) => { + // FIXME: some structs are represented as ByValPair + let lval = self.force_allocation(lval)?; + let adt_ptr = match lval { + Lvalue::Ptr { ptr, .. } => ptr, + _ => bug!("force allocation can only yield Lvalue::Ptr"), + }; + // run drop impl before the fields' drop impls + if let Some(drop_def_id) = adt_def.destructor() { + drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs)); + } + let layout = self.type_layout(ty)?; + let fields = match *layout { + Layout::Univariant { ref variant, .. } => { + adt_def.struct_variant().fields.iter().zip(&variant.offsets) + }, + Layout::General { ref variants, .. } => { + let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; + match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) { + // start at offset 1, to skip over the discriminant + Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]), + None => return Err(EvalError::InvalidDiscriminant), + } + }, + Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { + let discr = self.read_discriminant_value(adt_ptr, ty)?; + if discr == nndiscr as u128 { + assert_eq!(discr as usize as u128, discr); + adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) + } else { + // FIXME: the zst variant might contain zst types that impl Drop + return Ok(()); // nothing to do, this is zero sized (e.g. `None`) + } + }, + Layout::RawNullablePointer { nndiscr, .. } => { + let discr = self.read_discriminant_value(adt_ptr, ty)?; + if discr == nndiscr as u128 { + assert_eq!(discr as usize as u128, discr); + assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); + let field_ty = &adt_def.variants[discr as usize].fields[0]; + let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); + // FIXME: once read_discriminant_value works with lvalue, don't force + // alloc in the RawNullablePointer case + self.drop(lval, field_ty, drop)?; + return Ok(()); + } else { + // FIXME: the zst variant might contain zst types that impl Drop + return Ok(()); // nothing to do, this is zero sized (e.g. `None`) + } + }, + Layout::CEnum { .. } => return Ok(()), + _ => bug!("{:?} is not an adt layout", layout), + }; + let tcx = self.tcx; + self.drop_fields( + fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)), + lval, + drop, + )?; + }, + ty::TyTuple(fields, _) => { + let offsets = match *self.type_layout(ty)? { + Layout::Univariant { ref variant, .. } => &variant.offsets, + _ => bug!("tuples must be univariant"), + }; + self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; + }, + ty::TyDynamic(..) => { + let (ptr, vtable) = match lval { + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), + _ => bug!("expected an lvalue with a vtable"), + }; + let drop_fn = self.memory.read_ptr(vtable)?; + // some values don't need to call a drop impl, so the value is null + if drop_fn != Pointer::from_int(0) { + let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; + let real_ty = sig.inputs()[0]; + self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; + drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs)); + } else { + // just a sanity check + assert_eq!(drop_fn.offset, 0); + } + }, + ty::TySlice(elem_ty) => { + let (ptr, len) = match lval { + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), + _ => bug!("expected an lvalue with a length"), + }; + let size = self.type_size(elem_ty)?.expect("slice element must be sized"); + // FIXME: this creates a lot of stack frames if the element type has + // a drop impl + for i in 0..len { + self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; + } + }, + ty::TyArray(elem_ty, len) => { + let lval = self.force_allocation(lval)?; + let (ptr, extra) = match lval { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + _ => bug!("expected an lvalue with optional extra data"), + }; + let size = self.type_size(elem_ty)?.expect("array element cannot be unsized"); + // FIXME: this creates a lot of stack frames if the element type has + // a drop impl + for i in 0..(len as u64) { + self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; + } + }, + // FIXME: what about TyClosure and TyAnon? + // other types do not need to process drop + _ => {}, + } + + Ok(()) + } + + fn drop_fields( + &mut self, + mut fields: I, + lval: Lvalue<'tcx>, + drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, + ) -> EvalResult<'tcx> + where I: Iterator, ty::layout::Size)>, + { + // FIXME: some aggregates may be represented by Value::ByValPair + let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); + // manual iteration, because we need to be careful about the last field if it is unsized + while let Some((field_ty, offset)) = fields.next() { + let ptr = adt_ptr.offset(offset.bytes()); + if self.type_is_sized(field_ty) { + self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; + } else { + self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?; + break; // if it is not sized, then this is the last field anyway + } + } + assert!(fields.next().is_none()); + Ok(()) + } + + fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) + } +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 2a9fe179fa22c..e6ed715280e0a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -3,19 +3,20 @@ use rustc::mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{Layout, Size}; -use rustc::ty::subst::{Substs, Kind}; +use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; use syntax::codemap::{DUMMY_SP, Span}; use syntax::{ast, attr, abi}; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, IntegerExt, StackPopCleanup, monomorphize_field_ty, is_inhabited}; -use lvalue::{Lvalue, LvalueExtra}; +use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; +use lvalue::Lvalue; use memory::{Pointer, FunctionDefinition, Function}; use value::PrimVal; use value::Value; mod intrinsic; +mod drop; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -161,31 +162,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { - // add them to the stack in reverse order, because the impl that needs to run the last - // is the one that needs to be at the bottom of the stack - for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { - let mir = self.load_mir(drop_def_id)?; - trace!("substs for drop glue: {:?}", substs); - self.push_stack_frame( - drop_def_id, - span, - mir, - substs, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - Vec::new(), - )?; - let mut arg_locals = self.frame().mir.args_iter(); - let first = arg_locals.next().expect("drop impl has self arg"); - assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; - let ty = self.frame().mir.local_decls[first].ty; - self.write_value(self_arg, dest, ty)?; - } - Ok(()) - } - fn eval_fn_call( &mut self, def_id: DefId, @@ -699,200 +675,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), } } - - pub(super) fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) - } - - /// push DefIds of drop impls and their argument on the given vector - pub fn drop( - &mut self, - lval: Lvalue<'tcx>, - ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> { - if !self.type_needs_drop(ty) { - debug!("no need to drop {:?}", ty); - return Ok(()); - } - trace!("-need to drop {:?} at {:?}", ty, lval); - - match ty.sty { - // special case `Box` to deallocate the inner allocation - ty::TyAdt(ref def, _) if def.is_box() => { - let contents_ty = ty.boxed_ty(); - let val = self.read_lvalue(lval); - // we are going through the read_value path, because that already does all the - // checks for the trait object types. We'd only be repeating ourselves here. - let val = self.follow_by_ref_value(val, ty)?; - trace!("box dealloc on {:?}", val); - match val { - Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), - Value::ByVal(ptr) => { - assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.to_ptr()?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; - }, - Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.to_ptr()?; - let extra = match self.tcx.struct_tail(contents_ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), - _ => bug!("invalid fat pointer type: {}", ty), - }; - self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; - }, - } - let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); - let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); - // this is somewhat hacky, but hey, there's no representation difference between - // pointers and references, so - // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) - // is the same as - // fn drop(&mut self) if Self is Box - drop.push((box_free_fn, val, substs)); - }, - - ty::TyAdt(adt_def, substs) => { - // FIXME: some structs are represented as ByValPair - let lval = self.force_allocation(lval)?; - let adt_ptr = match lval { - Lvalue::Ptr { ptr, .. } => ptr, - _ => bug!("force allocation can only yield Lvalue::Ptr"), - }; - // run drop impl before the fields' drop impls - if let Some(drop_def_id) = adt_def.destructor() { - drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs)); - } - let layout = self.type_layout(ty)?; - let fields = match *layout { - Layout::Univariant { ref variant, .. } => { - adt_def.struct_variant().fields.iter().zip(&variant.offsets) - }, - Layout::General { ref variants, .. } => { - let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; - match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) { - // start at offset 1, to skip over the discriminant - Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]), - None => return Err(EvalError::InvalidDiscriminant), - } - }, - Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { - let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr as u128 { - assert_eq!(discr as usize as u128, discr); - adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) - } else { - // FIXME: the zst variant might contain zst types that impl Drop - return Ok(()); // nothing to do, this is zero sized (e.g. `None`) - } - }, - Layout::RawNullablePointer { nndiscr, .. } => { - let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr as u128 { - assert_eq!(discr as usize as u128, discr); - assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); - let field_ty = &adt_def.variants[discr as usize].fields[0]; - let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); - // FIXME: once read_discriminant_value works with lvalue, don't force - // alloc in the RawNullablePointer case - self.drop(lval, field_ty, drop)?; - return Ok(()); - } else { - // FIXME: the zst variant might contain zst types that impl Drop - return Ok(()); // nothing to do, this is zero sized (e.g. `None`) - } - }, - Layout::CEnum { .. } => return Ok(()), - _ => bug!("{:?} is not an adt layout", layout), - }; - let tcx = self.tcx; - self.drop_fields( - fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)), - lval, - drop, - )?; - }, - ty::TyTuple(fields, _) => { - let offsets = match *self.type_layout(ty)? { - Layout::Univariant { ref variant, .. } => &variant.offsets, - _ => bug!("tuples must be univariant"), - }; - self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; - }, - ty::TyDynamic(..) => { - let (ptr, vtable) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), - _ => bug!("expected an lvalue with a vtable"), - }; - let drop_fn = self.memory.read_ptr(vtable)?; - // some values don't need to call a drop impl, so the value is null - if drop_fn != Pointer::from_int(0) { - let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; - let real_ty = sig.inputs()[0]; - self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs)); - } else { - // just a sanity check - assert_eq!(drop_fn.offset, 0); - } - }, - ty::TySlice(elem_ty) => { - let (ptr, len) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), - _ => bug!("expected an lvalue with a length"), - }; - let size = self.type_size(elem_ty)?.expect("slice element must be sized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..len { - self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; - } - }, - ty::TyArray(elem_ty, len) => { - let lval = self.force_allocation(lval)?; - let (ptr, extra) = match lval { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("expected an lvalue with optional extra data"), - }; - let size = self.type_size(elem_ty)?.expect("array element cannot be unsized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..(len as u64) { - self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; - } - }, - // FIXME: what about TyClosure and TyAnon? - // other types do not need to process drop - _ => {}, - } - - Ok(()) - } - - fn drop_fields( - &mut self, - mut fields: I, - lval: Lvalue<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> - where I: Iterator, ty::layout::Size)>, - { - // FIXME: some aggregates may be represented by Value::ByValPair - let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); - // manual iteration, because we need to be careful about the last field if it is unsized - while let Some((field_ty, offset)) = fields.next() { - let ptr = adt_ptr.offset(offset.bytes()); - if self.type_is_sized(field_ty) { - self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; - } else { - self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?; - break; // if it is not sized, then this is the last field anyway - } - } - assert!(fields.next().is_none()); - Ok(()) - } } #[derive(Debug)] From d92085fd0e113d4e4cb7f6c204008541760e39cc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 19:08:08 +0100 Subject: [PATCH 0807/1096] properly extract the inner type in a drop impl --- src/terminator/drop.rs | 6 ++++-- tests/run-pass/box_box_trait.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass/box_box_trait.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 32c5073421b11..3b0ca371bba07 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -165,9 +165,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; - let real_ty = sig.inputs()[0]; + let real_ty = match sig.inputs()[0].sty { + ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), + _ => bug!("first argument of Drop::drop must be &mut T"), + }; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - drop.push((def_id, Value::ByVal(PrimVal::Ptr(ptr)), substs)); } else { // just a sanity check assert_eq!(drop_fn.offset, 0); diff --git a/tests/run-pass/box_box_trait.rs b/tests/run-pass/box_box_trait.rs new file mode 100644 index 0000000000000..57eef52d573b9 --- /dev/null +++ b/tests/run-pass/box_box_trait.rs @@ -0,0 +1,29 @@ +#![feature(box_syntax)] + +struct DroppableStruct; + +static mut DROPPED: bool = false; + +impl Drop for DroppableStruct { + fn drop(&mut self) { + unsafe { DROPPED = true; } + } +} + +trait MyTrait { fn dummy(&self) { } } +impl MyTrait for Box {} + +struct Whatever { w: Box } +impl Whatever { + fn new(w: Box) -> Whatever { + Whatever { w: w } + } +} + +fn main() { + { + let f: Box<_> = box DroppableStruct; + let _a = Whatever::new(box f as Box); + } + assert!(unsafe { DROPPED }); +} From c06251b0d2765f31c7e1b3f94490c5e7f454a3b6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 19:08:24 +0100 Subject: [PATCH 0808/1096] double space --- src/vtable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vtable.rs b/src/vtable.rs index bfae608ecbc10..602edba0f5459 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -94,7 +94,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_usize(vtable, 0)?; if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { if let Some(drop_def_id) = adt_def.destructor() { - let fn_ty = match self.tcx.item_type(drop_def_id).sty { + let fn_ty = match self.tcx.item_type(drop_def_id).sty { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; From 023ec3e39599993bb64e724ae14c797bd3ebb96e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 19:15:40 +0100 Subject: [PATCH 0809/1096] add some comments for clarification --- src/terminator/drop.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 3b0ca371bba07..4ee87c34fcece 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -79,10 +79,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; }, } + // We cannot use Box's destructor, because it is a no-op and only exists to reduce + // the number of hacks required in the compiler around the Box type. let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); // this is somewhat hacky, but hey, there's no representation difference between - // pointers and references, so + // pointers, `Box`es and references, so // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) // is the same as // fn drop(&mut self) if Self is Box @@ -164,7 +166,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { - let FunctionDefinition {def_id, substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; + // FIXME: change the `DropGlue` variant of `Function` to only contain `real_ty` + let FunctionDefinition {substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; + // The real type is taken from the self argument in `fn drop(&mut self)` let real_ty = match sig.inputs()[0].sty { ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), _ => bug!("first argument of Drop::drop must be &mut T"), From e58f750a49ce1a160a4de36a01bddcb1f1b38d42 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 9 Feb 2017 19:27:07 +0100 Subject: [PATCH 0810/1096] refactor drop glue --- src/memory.rs | 25 +++++++++---------------- src/terminator/drop.rs | 10 ++-------- src/vtable.rs | 7 ++++++- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index cbef71c68f6da..0f3094522e0c0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -120,9 +120,9 @@ pub enum Function<'tcx> { Concrete(FunctionDefinition<'tcx>), /// Glue required to call a regular function through a Fn(Mut|Once) trait object FnDefAsTraitObject(FunctionDefinition<'tcx>), - /// Glue required to call the actual drop impl's `drop` method. - /// Drop glue takes the `self` by value, while `Drop::drop` take `&mut self` - DropGlue(FunctionDefinition<'tcx>), + /// A drop glue function only needs to know the real type, and then miri can extract the + /// actual type at runtime. + DropGlue(ty::Ty<'tcx>), /// Glue required to treat the ptr part of a fat pointer /// as a function pointer FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>), @@ -137,9 +137,9 @@ impl<'tcx> Function<'tcx> { other => Err(EvalError::ExpectedConcreteFunction(other)), } } - pub fn expect_drop_glue(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { + pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> { match self { - Function::DropGlue(fn_def) => Ok(fn_def), + Function::DropGlue(real_ty) => Ok(real_ty), other => Err(EvalError::ExpectedDropGlue(other)), } } @@ -234,15 +234,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder())) } - pub fn create_drop_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { - self.create_fn_alloc(Function::DropGlue(FunctionDefinition { - def_id, - substs, - abi: fn_ty.abi, - // FIXME: why doesn't this compile? - //sig: tcx.erase_late_bound_regions(&fn_ty.sig), - sig: fn_ty.sig.skip_binder(), - })) + pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer { + self.create_fn_alloc(Function::DropGlue(ty)) } pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { @@ -495,8 +488,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { trace!("{} {}", msg, dump_fn_def(fn_def)); continue; }, - (None, Some(&Function::DropGlue(fn_def))) => { - trace!("{} drop glue for {}", msg, dump_fn_def(fn_def)); + (None, Some(&Function::DropGlue(real_ty))) => { + trace!("{} drop glue for {}", msg, real_ty); continue; }, (None, Some(&Function::FnDefAsTraitObject(fn_def))) => { diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 4ee87c34fcece..cb971eca29c7d 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -8,7 +8,7 @@ use syntax::codemap::Span; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, monomorphize_field_ty, StackPopCleanup}; use lvalue::{Lvalue, LvalueExtra}; -use memory::{Pointer, FunctionDefinition}; +use memory::Pointer; use value::PrimVal; use value::Value; @@ -166,13 +166,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop_fn = self.memory.read_ptr(vtable)?; // some values don't need to call a drop impl, so the value is null if drop_fn != Pointer::from_int(0) { - // FIXME: change the `DropGlue` variant of `Function` to only contain `real_ty` - let FunctionDefinition {substs, sig, ..} = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue()?; - // The real type is taken from the self argument in `fn drop(&mut self)` - let real_ty = match sig.inputs()[0].sty { - ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), - _ => bug!("first argument of Drop::drop must be &mut T"), - }; + let real_ty = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty()?; self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; } else { // just a sanity check diff --git a/src/vtable.rs b/src/vtable.rs index 602edba0f5459..78acfd9dded4c 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -98,7 +98,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; - let fn_ptr = self.memory.create_drop_glue(self.tcx, drop_def_id, substs, fn_ty); + // The real type is taken from the self argument in `fn drop(&mut self)` + let real_ty = match fn_ty.sig.skip_binder().inputs()[0].sty { + ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), + _ => bug!("first argument of Drop::drop must be &mut T"), + }; + let fn_ptr = self.memory.create_drop_glue(real_ty); self.memory.write_ptr(vtable, fn_ptr)?; } } From 333264c956cc4d2d821b8707abc5657a0f1e1369 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 08:13:18 +0100 Subject: [PATCH 0811/1096] clarify comment on drop glue --- src/memory.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 0f3094522e0c0..835c7bca7f60f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -120,8 +120,10 @@ pub enum Function<'tcx> { Concrete(FunctionDefinition<'tcx>), /// Glue required to call a regular function through a Fn(Mut|Once) trait object FnDefAsTraitObject(FunctionDefinition<'tcx>), - /// A drop glue function only needs to know the real type, and then miri can extract the - /// actual type at runtime. + /// A drop glue function only needs to know the real type, and then miri can extract + /// that type from a vtable's drop pointer. + /// Instead of storing some drop function, we act as if there are no trait objects, by + /// mapping trait objects to their real types before acting on them. DropGlue(ty::Ty<'tcx>), /// Glue required to treat the ptr part of a fat pointer /// as a function pointer From 6d97d02c52a3d1f49b5c8617c1999bf2542ee98e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 09:33:05 +0100 Subject: [PATCH 0812/1096] autogenerate markdown for rustc test suite result --- tests/compiletest.rs | 100 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ae0ac1f8adf0b..100b79401dfbb 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -83,10 +83,15 @@ fn compile_test() { let host = host.split("\n").next().expect("no \n after host"); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { - let mut mir_not_found = 0; - let mut crate_not_found = 0; + let mut mir_not_found = Vec::new(); + let mut crate_not_found = Vec::new(); let mut success = 0; - let mut failed = 0; + let mut failed = Vec::new(); + let mut c_abi_fns = Vec::new(); + let mut abi = Vec::new(); + let mut unsupported = Vec::new(); + let mut unimplemented_intrinsic = Vec::new(); + let mut limits = Vec::new(); for file in std::fs::read_dir(path).unwrap() { let file = file.unwrap(); let path = file.path(); @@ -110,15 +115,37 @@ fn compile_test() { Ok(output) => { let output_err = std::str::from_utf8(&output.stderr).unwrap(); if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) { - mir_not_found += 1; let end = text.find('`').unwrap(); + mir_not_found.push(text[..end].to_string()); writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap(); } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) { - crate_not_found += 1; let end = text.find('`').unwrap(); + crate_not_found.push(text[..end].to_string()); writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap(); } else { - failed += 1; + for text in output_err.split("error: ").skip(1) { + let end = text.find('\n').unwrap_or(text.len()); + let c_abi = "can't call C ABI function: "; + let unimplemented_intrinsic_s = "unimplemented intrinsic: "; + let unsupported_s = "miri does not support "; + let abi_s = "can't handle function with "; + let limit_s = "reached the configured maximum "; + if text.starts_with(c_abi) { + c_abi_fns.push(text[c_abi.len()..end].to_string()); + } else if text.starts_with(unimplemented_intrinsic_s) { + unimplemented_intrinsic.push(text[unimplemented_intrinsic_s.len()..end].to_string()); + } else if text.starts_with(unsupported_s) { + unsupported.push(text[unsupported_s.len()..end].to_string()); + } else if text.starts_with(abi_s) { + abi.push(text[abi_s.len()..end].to_string()); + } else if text.starts_with(limit_s) { + limits.push(text[limit_s.len()..end].to_string()); + } else { + if text.find("aborting").is_none() { + failed.push(text[..end].to_string()); + } + } + } writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); @@ -131,8 +158,34 @@ fn compile_test() { } } let stderr = std::io::stderr(); - writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap(); - assert_eq!(failed, 0, "some tests failed"); + let mut stderr = stderr.lock(); + writeln!(stderr, "{} success, {} no mir, {} crate not found, {} failed, \ + {} C fn, {} ABI, {} unsupported, {} intrinsic", + success, mir_not_found.len(), crate_not_found.len(), failed.len(), + c_abi_fns.len(), abi.len(), unsupported.len(), unimplemented_intrinsic.len()).unwrap(); + writeln!(stderr, "# The \"other reasons\" errors").unwrap(); + writeln!(stderr, "(sorted, deduplicated)").unwrap(); + print_vec(&mut stderr, failed); + + writeln!(stderr, "# can't call C ABI function").unwrap(); + print_vec(&mut stderr, c_abi_fns); + + writeln!(stderr, "# unsupported ABI").unwrap(); + print_vec(&mut stderr, abi); + + writeln!(stderr, "# unsupported").unwrap(); + print_vec(&mut stderr, unsupported); + + writeln!(stderr, "# unimplemented intrinsics").unwrap(); + print_vec(&mut stderr, unimplemented_intrinsic); + + writeln!(stderr, "# mir not found").unwrap(); + print_vec(&mut stderr, mir_not_found); + + writeln!(stderr, "# crate not found").unwrap(); + print_vec(&mut stderr, crate_not_found); + + panic!("ran miri on rustc test suite. Test failing for convenience"); } else { run_pass(); for_all_targets(&sysroot, |target| { @@ -141,3 +194,34 @@ fn compile_test() { compile_fail(&sysroot); } } + +fn print_vec(stderr: &mut W, v: Vec) { + writeln!(stderr, "```").unwrap(); + for (n, s) in vec_to_hist(v).into_iter().rev() { + writeln!(stderr, "{:4} {}", n, s).unwrap(); + } + writeln!(stderr, "```").unwrap(); +} + +fn vec_to_hist(mut v: Vec) -> Vec<(usize, T)> { + v.sort(); + let mut v = v.into_iter(); + let mut result = Vec::new(); + let mut current = v.next(); + 'outer: while let Some(current_val) = current { + let mut n = 1; + while let Some(next) = v.next() { + if next == current_val { + n += 1; + } else { + result.push((n, current_val)); + current = Some(next); + continue 'outer; + } + } + result.push((n, current_val)); + break; + } + result.sort(); + result +} From 9e248938793b213eeb1d725c298dc7b5cfd9cfb8 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 02:28:51 -0800 Subject: [PATCH 0813/1096] Rename "vtable" mod to "traits". --- src/lib.rs | 2 +- src/{vtable.rs => traits.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{vtable.rs => traits.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index 47ed4fd0cebfb..290b478eb648b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,8 +30,8 @@ mod memory; mod operator; mod step; mod terminator; +mod traits; mod value; -mod vtable; pub use error::{ EvalError, diff --git a/src/vtable.rs b/src/traits.rs similarity index 100% rename from src/vtable.rs rename to src/traits.rs From d8d813c4ad7cb357f56636c0974147914e2b1311 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 03:12:33 -0800 Subject: [PATCH 0814/1096] Resolve associated constants. Fixes #130. --- src/eval_context.rs | 1 + src/step.rs | 1 + src/traits.rs | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/eval_context.rs b/src/eval_context.rs index dedb8a53b718b..31c4915185da1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -814,6 +814,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // function items are zero sized Value::ByRef(self.memory.allocate(0, 0)?) } else { + let (def_id, substs) = self.resolve_associated_const(def_id, substs); let cid = GlobalId { def_id, substs, promoted: None }; self.read_lvalue(Lvalue::Global(cid)) } diff --git a/src/step.rs b/src/step.rs index fd90b33c82b7e..f44a292f0ce82 100644 --- a/src/step.rs +++ b/src/step.rs @@ -149,6 +149,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) { + let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs); let cid = GlobalId { def_id, substs, promoted: None }; if self.ecx.globals.contains_key(&cid) { return; diff --git a/src/traits.rs b/src/traits.rs index 78acfd9dded4c..a40e5a95542e9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -209,4 +209,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fulfill_cx.select_all_or_error(&infcx).is_ok() }) } + + pub(crate) fn resolve_associated_const( + &self, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + ) -> (DefId, &'tcx Substs<'tcx>) { + if let Some(trait_id) = self.tcx.trait_of_item(def_id) { + let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); + let vtable = self.fulfill_obligation(trait_ref); + if let traits::VtableImpl(vtable_impl) = vtable { + let name = self.tcx.item_name(def_id); + let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id) + .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); + if let Some(assoc_const) = assoc_const_opt { + return (assoc_const.def_id, vtable_impl.substs); + } + } + } + (def_id, substs) + } } From 68daab974de75078f24d4982b0ca1d81d06ed80b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 03:21:33 -0800 Subject: [PATCH 0815/1096] Move relevant code into "traits" module. --- src/terminator/mod.rs | 246 +---------------------------------------- src/traits.rs | 250 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 250 insertions(+), 246 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e6ed715280e0a..a3b47f533f4be 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,17 +1,15 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::traits::{self, Reveal}; -use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::{Layout, Size}; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use syntax::codemap::{DUMMY_SP, Span}; -use syntax::{ast, attr, abi}; +use rustc::ty::{self, Ty, BareFnTy}; +use syntax::codemap::Span; +use syntax::attr; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, FunctionDefinition, Function}; +use memory::{Pointer, FunctionDefinition}; use value::PrimVal; use value::Value; @@ -19,7 +17,6 @@ mod intrinsic; mod drop; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { self.frame_mut().block = target; self.frame_mut().stmt = 0; @@ -497,29 +494,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - pub(super) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { - // Do the initial selection for the obligation. This yields the shallow result we are - // looking for -- that is, what specific impl. - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = traits::SelectionContext::new(&infcx); - - let obligation = traits::Obligation::new( - traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), - trait_ref.to_poly_trait_predicate(), - ); - let selection = selcx.select(&obligation).unwrap().unwrap(); - - // Currently, we use a fulfillment context to completely resolve all nested obligations. - // This is because they can inform the inference of the impl's type parameters. - let mut fulfill_cx = traits::FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) - }) - } - - fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { + pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { if let Some((last, last_ty)) = args.pop() { let last_layout = self.type_layout(last_ty)?; match (&last_ty.sty, last_layout) { @@ -540,215 +515,4 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ok(()) } - - /// Trait method, which has to be resolved to an impl method. - fn trait_method( - &mut self, - trait_id: DefId, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { - let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); - let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); - - match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(vtable_impl) => { - let impl_did = vtable_impl.impl_def_id; - let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the impl - // and those from the method: - let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - - Ok((did, substs, Vec::new())) - } - - traits::VtableClosure(vtable_closure) => { - let trait_closure_kind = self.tcx - .lang_items - .fn_trait_kind(trait_id) - .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); - let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); - trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); - self.unpack_fn_args(args)?; - let mut temporaries = Vec::new(); - match (closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. - - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. - // We want a `fn(self, ...)`. - // We can produce this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - - // Interpreter magic: insert an intermediate pointer, so we can skip the - // intermediate function call. - let ptr = match args[0].0 { - Value::ByRef(ptr) => ptr, - Value::ByVal(primval) => { - let ptr = self.alloc_ptr(args[0].1)?; - let size = self.type_size(args[0].1)?.expect("closures are sized"); - self.memory.write_primval(ptr, primval, size)?; - temporaries.push(ptr); - ptr - }, - Value::ByValPair(a, b) => { - let ptr = self.alloc_ptr(args[0].1)?; - self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - temporaries.push(ptr); - ptr - }, - }; - args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); - args[0].1 = self.tcx.mk_mut_ptr(args[0].1); - } - - _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), - } - Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries)) - } - - traits::VtableFnPointer(vtable_fn_ptr) => { - if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty { - args.remove(0); - self.unpack_fn_args(args)?; - Ok((did, substs, Vec::new())) - } else { - bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) - } - } - - traits::VtableObject(ref data) => { - let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; - if args.is_empty() { - return Err(EvalError::VtableForArgumentlessMethod); - } - let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; - let idx = idx + 3; - let offset = idx * self.memory.pointer_size(); - let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; - trace!("args: {:#?}", args); - match self.memory.get_fn(fn_ptr.alloc_id)? { - Function::FnDefAsTraitObject(fn_def) => { - trace!("sig: {:#?}", fn_def.sig); - assert!(fn_def.abi != abi::Abi::RustCall); - assert_eq!(args.len(), 2); - // a function item turned into a closure trait object - // the first arg is just there to give use the vtable - args.remove(0); - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(fn_def) => { - trace!("sig: {:#?}", fn_def.sig); - args[0] = ( - Value::ByVal(PrimVal::Ptr(self_ptr)), - fn_def.sig.inputs()[0], - ); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::Closure(fn_def) => { - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - Function::FnPtrAsTraitObject(sig) => { - trace!("sig: {:#?}", sig); - // the first argument was the fat ptr - args.remove(0); - self.unpack_fn_args(args)?; - let fn_ptr = self.memory.read_ptr(self_ptr)?; - let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; - assert_eq!(sig, fn_def.sig); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - } - }, - vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), - } - } -} - -#[derive(Debug)] -pub(super) struct ImplMethod<'tcx> { - pub(super) method: ty::AssociatedItem, - pub(super) substs: &'tcx Substs<'tcx>, - pub(super) is_provided: bool, -} - -/// Locates the applicable definition of a method, given its name. -pub(super) fn get_impl_method<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name, -) -> ImplMethod<'tcx> { - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, - substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("trans::meth::get_impl_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - ImplMethod { - method: node_item.item, - substs, - is_provided: node_item.node.is_from_trait(), - } - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} - -/// Locates the applicable definition of a method, given its name. -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name) - -> (DefId, &'tcx Substs<'tcx>) -{ - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("find_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - (node_item.item.def_id, substs) - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } } diff --git a/src/traits.rs b/src/traits.rs index a40e5a95542e9..b892efbf569bb 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,14 +1,178 @@ -use rustc::hir::def_id::DefId; use rustc::traits::{self, Reveal, SelectionContext}; -use rustc::ty::subst::Substs; -use rustc::ty; -use error::EvalResult; use eval_context::EvalContext; use memory::Pointer; -use terminator::{get_impl_method, ImplMethod}; + +use rustc::hir::def_id::DefId; +use rustc::ty::fold::TypeFoldable; +use rustc::ty::subst::Substs; +use rustc::ty::{self, Ty, TyCtxt}; +use syntax::codemap::DUMMY_SP; +use syntax::{ast, abi}; + +use error::{EvalError, EvalResult}; +use memory::Function; +use value::PrimVal; +use value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { + /// Trait method, which has to be resolved to an impl method. + pub(crate) fn trait_method( + &mut self, + trait_id: DefId, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + args: &mut Vec<(Value, Ty<'tcx>)>, + ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { + let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); + let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); + + match self.fulfill_obligation(trait_ref) { + traits::VtableImpl(vtable_impl) => { + let impl_did = vtable_impl.impl_def_id; + let mname = self.tcx.item_name(def_id); + // Create a concatenated set of substitutions which includes those from the impl + // and those from the method: + let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); + + Ok((did, substs, Vec::new())) + } + + traits::VtableClosure(vtable_closure) => { + let trait_closure_kind = self.tcx + .lang_items + .fn_trait_kind(trait_id) + .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); + let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); + trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); + self.unpack_fn_args(args)?; + let mut temporaries = Vec::new(); + match (closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. + + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. + // We want a `fn(self, ...)`. + // We can produce this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + + // Interpreter magic: insert an intermediate pointer, so we can skip the + // intermediate function call. + let ptr = match args[0].0 { + Value::ByRef(ptr) => ptr, + Value::ByVal(primval) => { + let ptr = self.alloc_ptr(args[0].1)?; + let size = self.type_size(args[0].1)?.expect("closures are sized"); + self.memory.write_primval(ptr, primval, size)?; + temporaries.push(ptr); + ptr + }, + Value::ByValPair(a, b) => { + let ptr = self.alloc_ptr(args[0].1)?; + self.write_pair_to_ptr(a, b, ptr, args[0].1)?; + temporaries.push(ptr); + ptr + }, + }; + args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); + args[0].1 = self.tcx.mk_mut_ptr(args[0].1); + } + + _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), + } + Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries)) + } + + traits::VtableFnPointer(vtable_fn_ptr) => { + if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty { + args.remove(0); + self.unpack_fn_args(args)?; + Ok((did, substs, Vec::new())) + } else { + bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) + } + } + + traits::VtableObject(ref data) => { + let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; + if args.is_empty() { + return Err(EvalError::VtableForArgumentlessMethod); + } + let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; + let idx = idx + 3; + let offset = idx * self.memory.pointer_size(); + let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; + trace!("args: {:#?}", args); + match self.memory.get_fn(fn_ptr.alloc_id)? { + Function::FnDefAsTraitObject(fn_def) => { + trace!("sig: {:#?}", fn_def.sig); + assert!(fn_def.abi != abi::Abi::RustCall); + assert_eq!(args.len(), 2); + // a function item turned into a closure trait object + // the first arg is just there to give use the vtable + args.remove(0); + self.unpack_fn_args(args)?; + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + }, + Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), + Function::Concrete(fn_def) => { + trace!("sig: {:#?}", fn_def.sig); + args[0] = ( + Value::ByVal(PrimVal::Ptr(self_ptr)), + fn_def.sig.inputs()[0], + ); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + }, + Function::Closure(fn_def) => { + self.unpack_fn_args(args)?; + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + } + Function::FnPtrAsTraitObject(sig) => { + trace!("sig: {:#?}", sig); + // the first argument was the fat ptr + args.remove(0); + self.unpack_fn_args(args)?; + let fn_ptr = self.memory.read_ptr(self_ptr)?; + let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; + assert_eq!(sig, fn_def.sig); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + } + } + }, + vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), + } + } + + pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { + // Do the initial selection for the obligation. This yields the shallow result we are + // looking for -- that is, what specific impl. + self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation = traits::Obligation::new( + traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + trait_ref.to_poly_trait_predicate(), + ); + let selection = selcx.select(&obligation).unwrap().unwrap(); + + // Currently, we use a fulfillment context to completely resolve all nested obligations. + // This is because they can inform the inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable) + }) + } + /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -230,3 +394,79 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (def_id, substs) } } + +#[derive(Debug)] +pub(super) struct ImplMethod<'tcx> { + pub(super) method: ty::AssociatedItem, + pub(super) substs: &'tcx Substs<'tcx>, + pub(super) is_provided: bool, +} + +/// Locates the applicable definition of a method, given its name. +pub(super) fn get_impl_method<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + impl_def_id: DefId, + impl_substs: &'tcx Substs<'tcx>, + name: ast::Name, +) -> ImplMethod<'tcx> { + assert!(!substs.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { + Some(node_item) => { + let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); + let substs = traits::translate_substs(&infcx, impl_def_id, + substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("trans::meth::get_impl_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); + ImplMethod { + method: node_item.item, + substs, + is_provided: node_item.node.is_from_trait(), + } + } + None => { + bug!("method {:?} not found in {:?}", name, impl_def_id) + } + } +} + +/// Locates the applicable definition of a method, given its name. +pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + impl_def_id: DefId, + impl_substs: &'tcx Substs<'tcx>, + name: ast::Name) + -> (DefId, &'tcx Substs<'tcx>) +{ + assert!(!substs.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + + match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { + Some(node_item) => { + let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); + let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); + tcx.lift(&substs).unwrap_or_else(|| { + bug!("find_method: translate_substs \ + returned {:?} which contains inference types/regions", + substs); + }) + }); + (node_item.item.def_id, substs) + } + None => { + bug!("method {:?} not found in {:?}", name, impl_def_id) + } + } +} From 459f898b911f8a3a1711b3be80b7fd2f167a7217 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 03:24:05 -0800 Subject: [PATCH 0816/1096] Reformatting. --- src/step.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/step.rs b/src/step.rs index f44a292f0ce82..c3764d3c51004 100644 --- a/src/step.rs +++ b/src/step.rs @@ -148,7 +148,13 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { } impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { - fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, immutable: bool) { + fn global_item( + &mut self, + def_id: DefId, + substs: &'tcx subst::Substs<'tcx>, + span: Span, + shared: bool, + ) { let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs); let cid = GlobalId { def_id, substs, promoted: None }; if self.ecx.globals.contains_key(&cid) { @@ -157,10 +163,19 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let mir = this.ecx.load_mir(def_id)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let cleanup = StackPopCleanup::MarkStatic(!immutable || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe()); + let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe(); + let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); trace!("pushing stack frame for global: {}", name); - this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::Global(cid), cleanup, Vec::new()) + this.ecx.push_stack_frame( + def_id, + span, + mir, + substs, + Lvalue::Global(cid), + cleanup, + Vec::new(), + ) }); } fn try EvalResult<'tcx>>(&mut self, f: F) { From d971a63c4b81340634f17579da0792a7467b8ea0 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 05:27:02 -0800 Subject: [PATCH 0817/1096] Mark more errors as unsupported. --- src/error.rs | 2 +- src/eval_context.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index bc6510b1f129f..72eedba5e3e18 100644 --- a/src/error.rs +++ b/src/error.rs @@ -116,7 +116,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::AssumptionNotHeld => "`assume` argument was false", EvalError::InlineAsm => - "cannot evaluate inline assembly", + "miri does not support inline assembly", EvalError::TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", EvalError::ReallocatedStaticMemory => diff --git a/src/eval_context.rs b/src/eval_context.rs index 31c4915185da1..2a2a5829d3777 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1437,6 +1437,12 @@ pub fn eval_main<'a, 'tcx: 'a>( let mut ecx = EvalContext::new(tcx, limits); let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); + if !mir.return_ty.is_nil() || mir.arg_count != 0 { + let msg = "miri does not support main functions without `fn()` type signatures"; + tcx.sess.err(&EvalError::Unimplemented(String::from(msg)).to_string()); + return; + } + ecx.push_stack_frame( def_id, DUMMY_SP, From 07e1e7b7e5d0cf6962322f035dd204d45106a29f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 10:27:31 +0100 Subject: [PATCH 0818/1096] move base computation into each projection to allow optimizations and corner cases --- src/lvalue.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index e86c480aa0b66..8ba50b324f1b7 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -164,13 +164,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, proj: &mir::LvalueProjection<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty)?; - use rustc::mir::ProjectionElem::*; let (ptr, extra) = match proj.elem { Field(field, field_ty) => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty)?; // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, base_extra) = base.to_ptr_and_extra(); @@ -246,6 +245,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Downcast(_, variant) => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); + let base_layout = self.type_layout(base_ty)?; // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, base_extra) = base.to_ptr_and_extra(); @@ -260,6 +262,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { + let base_ty = self.lvalue_ty(&proj.base); let val = self.eval_and_read_lvalue(&proj.base)?; let pointee_type = match base_ty.sty { @@ -285,6 +288,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Index(ref operand) => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _) = base.to_ptr_and_extra(); @@ -300,6 +305,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ConstantIndex { offset, min_length, from_end } => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _) = base.to_ptr_and_extra(); @@ -319,6 +326,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Subslice { from, to } => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _) = base.to_ptr_and_extra(); From 680e6498054b496787707cb1cf77994c2ba8744c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 11:29:07 +0100 Subject: [PATCH 0819/1096] get rid of useless calls into `eval_lvalue` --- src/eval_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 2a2a5829d3777..739274ce3986c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -816,7 +816,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { let (def_id, substs) = self.resolve_associated_const(def_id, substs); let cid = GlobalId { def_id, substs, promoted: None }; - self.read_lvalue(Lvalue::Global(cid)) + self.globals.get(&cid).expect("static/const not cached").value } } @@ -826,7 +826,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: self.substs(), promoted: Some(index), }; - self.read_lvalue(Lvalue::Global(cid)) + self.globals.get(&cid).expect("promoted not cached").value } }; From 12826fb8a3b9f5b296a7cc9a8a30eff781e4f6e9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 12:57:47 +0100 Subject: [PATCH 0820/1096] factor out lvalue field access into its own function --- src/lvalue.rs | 147 ++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 8ba50b324f1b7..61ffeef7d593d 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -160,88 +160,97 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(lvalue) } - fn eval_lvalue_projection( + fn lvalue_field( &mut self, - proj: &mir::LvalueProjection<'tcx>, + base: Lvalue<'tcx>, + field: usize, + base_ty: Ty<'tcx>, + field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { - use rustc::mir::ProjectionElem::*; - let (ptr, extra) = match proj.elem { - Field(field, field_ty) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - let field_ty = self.monomorphize(field_ty, self.substs()); - let field = field.index(); - - use rustc::ty::layout::Layout::*; - let (offset, packed) = match *base_layout { - Univariant { ref variant, .. } => { - (variant.offsets[field], variant.packed) - }, + let base_layout = self.type_layout(base_ty)?; + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + + let field_ty = self.monomorphize(field_ty, self.substs()); + + use rustc::ty::layout::Layout::*; + let (offset, packed) = match *base_layout { + Univariant { ref variant, .. } => { + (variant.offsets[field], variant.packed) + }, + + General { ref variants, .. } => { + if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { + // +1 for the discriminant, which is field 0 + (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) + } else { + bug!("field access on enum had no variant index"); + } + } - General { ref variants, .. } => { - if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { - // +1 for the discriminant, which is field 0 - (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) - } else { - bug!("field access on enum had no variant index"); - } - } + RawNullablePointer { .. } => { + assert_eq!(field, 0); + return Ok(base); + } - RawNullablePointer { .. } => { - assert_eq!(field, 0); - return Ok(base); - } + StructWrappedNullablePointer { ref nonnull, .. } => { + (nonnull.offsets[field], nonnull.packed) + } - StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field], nonnull.packed) - } + UntaggedUnion { .. } => return Ok(base), - UntaggedUnion { .. } => return Ok(base), + Vector { element, count } => { + let field = field as u64; + assert!(field < count); + let elem_size = element.size(&self.tcx.data_layout).bytes(); + (Size::from_bytes(field * elem_size), false) + } - Vector { element, count } => { - let field = field as u64; - assert!(field < count); - let elem_size = element.size(&self.tcx.data_layout).bytes(); - (Size::from_bytes(field * elem_size), false) - } + _ => bug!("field access on non-product type: {:?}", base_layout), + }; - _ => bug!("field access on non-product type: {:?}", base_layout), - }; + let offset = match base_extra { + LvalueExtra::Vtable(tab) => { + let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; + offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() + } + _ => offset.bytes(), + }; - let offset = match base_extra { - LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; - offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() - } - _ => offset.bytes(), - }; + let ptr = base_ptr.offset(offset); - let ptr = base_ptr.offset(offset); + if packed { + let size = self.type_size(field_ty)?.expect("packed struct must be sized"); + self.memory.mark_packed(ptr, size); + } - if packed { - let size = self.type_size(field_ty)?.expect("packed struct must be sized"); - self.memory.mark_packed(ptr, size); - } + let extra = if self.type_is_sized(field_ty) { + LvalueExtra::None + } else { + match base_extra { + LvalueExtra::None => bug!("expected fat pointer"), + LvalueExtra::DowncastVariant(..) => + bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::Vtable(_) | + LvalueExtra::Length(_) => {}, + } + base_extra + }; - let extra = if self.type_is_sized(field_ty) { - LvalueExtra::None - } else { - match base_extra { - LvalueExtra::None => bug!("expected fat pointer"), - LvalueExtra::DowncastVariant(..) => - bug!("Rust doesn't support unsized fields in enum variants"), - LvalueExtra::Vtable(_) | - LvalueExtra::Length(_) => {}, - } - base_extra - }; + Ok(Lvalue::Ptr { ptr, extra }) + } - (ptr, extra) + fn eval_lvalue_projection( + &mut self, + proj: &mir::LvalueProjection<'tcx>, + ) -> EvalResult<'tcx, Lvalue<'tcx>> { + use rustc::mir::ProjectionElem::*; + let (ptr, extra) = match proj.elem { + Field(field, field_ty) => { + let base = self.eval_lvalue(&proj.base)?; + let base_ty = self.lvalue_ty(&proj.base); + return self.lvalue_field(base, field.index(), base_ty, field_ty); } Downcast(_, variant) => { From 2e185485b34b5ab783d9faa3f17ead6059b6341b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 14:32:58 +0100 Subject: [PATCH 0821/1096] use the `lvalue_field` function more often to save needless `force_allocation`s --- src/lvalue.rs | 2 +- src/step.rs | 17 ++++++------- src/terminator/drop.rs | 54 ++++++++++++++++++------------------------ 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 61ffeef7d593d..5ac5ad78b8aaf 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -160,7 +160,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(lvalue) } - fn lvalue_field( + pub fn lvalue_field( &mut self, base: Lvalue<'tcx>, field: usize, diff --git a/src/step.rs b/src/step.rs index c3764d3c51004..5c957f0cdc08b 100644 --- a/src/step.rs +++ b/src/step.rs @@ -14,6 +14,7 @@ use rustc::ty::{subst, self}; use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup, MirRef}; use lvalue::{Global, GlobalId, Lvalue}; +use value::{Value, PrimVal}; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -89,15 +90,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_layout = self.type_layout(dest_ty)?; match *dest_layout { - Layout::General { discr, ref variants, .. } => { - let discr_size = discr.size().bytes(); - let discr_offset = variants[variant_index].offsets[0].bytes(); + Layout::General { discr, .. } => { + // FIXME: I (oli-obk) think we need to check the + // `dest_ty` for the variant's discriminant and write + // instead of the variant index + // We don't have any tests actually going through these lines + let discr_ty = discr.to_ty(&self.tcx, false); + let discr_lval = self.lvalue_field(dest, 0, dest_ty, discr_ty)?; - // FIXME(solson) - let dest = self.force_allocation(dest)?; - let discr_dest = (dest.to_ptr()).offset(discr_offset); - - self.memory.write_uint(discr_dest, variant_index as u128, discr_size)?; + self.write_value(Value::ByVal(PrimVal::Bytes(variant_index as u128)), discr_lval, discr_ty)?; } Layout::RawNullablePointer { nndiscr, .. } => { diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index cb971eca29c7d..13c851244bfad 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -93,7 +93,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair - let lval = self.force_allocation(lval)?; + let mut lval = self.force_allocation(lval)?; let adt_ptr = match lval { Lvalue::Ptr { ptr, .. } => ptr, _ => bug!("force allocation can only yield Lvalue::Ptr"), @@ -104,22 +104,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let layout = self.type_layout(ty)?; let fields = match *layout { - Layout::Univariant { ref variant, .. } => { - adt_def.struct_variant().fields.iter().zip(&variant.offsets) - }, - Layout::General { ref variants, .. } => { + Layout::Univariant { .. } => &adt_def.struct_variant().fields, + Layout::General { .. } => { let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; + let ptr = self.force_allocation(lval)?.to_ptr(); match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) { - // start at offset 1, to skip over the discriminant - Some(i) => adt_def.variants[i].fields.iter().zip(&variants[i].offsets[1..]), + Some(i) => { + lval = Lvalue::Ptr { + ptr, + extra: LvalueExtra::DowncastVariant(i), + }; + &adt_def.variants[i].fields + }, None => return Err(EvalError::InvalidDiscriminant), } }, - Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { + Layout::StructWrappedNullablePointer { nndiscr, .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; if discr == nndiscr as u128 { assert_eq!(discr as usize as u128, discr); - adt_def.variants[discr as usize].fields.iter().zip(&nonnull.offsets) + &adt_def.variants[discr as usize].fields } else { // FIXME: the zst variant might contain zst types that impl Drop return Ok(()); // nothing to do, this is zero sized (e.g. `None`) @@ -146,18 +150,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let tcx = self.tcx; self.drop_fields( - fields.map(|(ty, &offset)| (monomorphize_field_ty(tcx, ty, substs), offset)), + fields.iter().map(|field| monomorphize_field_ty(tcx, field, substs)), lval, + ty, drop, )?; }, - ty::TyTuple(fields, _) => { - let offsets = match *self.type_layout(ty)? { - Layout::Univariant { ref variant, .. } => &variant.offsets, - _ => bug!("tuples must be univariant"), - }; - self.drop_fields(fields.iter().cloned().zip(offsets.iter().cloned()), lval, drop)?; - }, + ty::TyTuple(fields, _) => self.drop_fields(fields.into_iter().cloned(), lval, ty, drop)?, ty::TyDynamic(..) => { let (ptr, vtable) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), @@ -208,25 +207,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn drop_fields( &mut self, - mut fields: I, + fields: I, lval: Lvalue<'tcx>, + ty: Ty<'tcx>, drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, ) -> EvalResult<'tcx> - where I: Iterator, ty::layout::Size)>, + where I: Iterator>, { - // FIXME: some aggregates may be represented by Value::ByValPair - let (adt_ptr, extra) = self.force_allocation(lval)?.to_ptr_and_extra(); - // manual iteration, because we need to be careful about the last field if it is unsized - while let Some((field_ty, offset)) = fields.next() { - let ptr = adt_ptr.offset(offset.bytes()); - if self.type_is_sized(field_ty) { - self.drop(Lvalue::from_ptr(ptr), field_ty, drop)?; - } else { - self.drop(Lvalue::Ptr { ptr, extra }, field_ty, drop)?; - break; // if it is not sized, then this is the last field anyway - } + trace!("drop_fields: {:?} of type {}", lval, ty); + for (i, field_ty) in fields.enumerate() { + let field_lval = self.lvalue_field(lval, i, ty, field_ty)?; + self.drop(field_lval, field_ty, drop)?; } - assert!(fields.next().is_none()); Ok(()) } From e1725a810142bb95fb5d3beb3e19a1a6f1885836 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 05:39:30 -0800 Subject: [PATCH 0822/1096] Add test for assoc consts. --- tests/run-pass/associated-const.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/run-pass/associated-const.rs diff --git a/tests/run-pass/associated-const.rs b/tests/run-pass/associated-const.rs new file mode 100644 index 0000000000000..d906544500931 --- /dev/null +++ b/tests/run-pass/associated-const.rs @@ -0,0 +1,23 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_consts)] + +trait Foo { + const ID: i32; +} + +impl Foo for i32 { + const ID: i32 = 1; +} + +fn main() { + assert_eq!(1, ::ID); +} From 31f3aabdd46e93bdbe88c08c1e62d5530dab182f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 14:50:24 +0100 Subject: [PATCH 0823/1096] move some variables closer to their use site. --- src/lvalue.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 5ac5ad78b8aaf..859ed32405ca6 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -168,11 +168,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { @@ -181,6 +176,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, General { ref variants, .. } => { + let (_, base_extra) = base.to_ptr_and_extra(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) @@ -210,6 +206,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let offset = match base_extra { LvalueExtra::Vtable(tab) => { let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; @@ -220,6 +220,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = base_ptr.offset(offset); + let field_ty = self.monomorphize(field_ty, self.substs()); + if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); self.memory.mark_packed(ptr, size); From 5a2cdc2689a81cd4d1b9e0d43d36e27b6158b44e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 05:58:34 -0800 Subject: [PATCH 0824/1096] Implement the fast float math intrinsics. --- src/terminator/intrinsic.rs | 14 +++++++++++--- tests/run-pass/float_fast_math.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/float_fast_math.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2798790feb5a..b89fd4c2c5509 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -217,12 +217,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::from_f64(f.abs()), dest_ty)?; } - "fadd_fast" => { + "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { let ty = substs.type_at(0); let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; - let b = self.value_to_primval(arg_vals[0], ty)?; - let result = operator::binary_op(mir::BinOp::Add, a, kind, b, kind)?; + let b = self.value_to_primval(arg_vals[1], ty)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let result = operator::binary_op(op, a, kind, b, kind)?; self.write_primval(dest, result.0, dest_ty)?; } diff --git a/tests/run-pass/float_fast_math.rs b/tests/run-pass/float_fast_math.rs new file mode 100644 index 0000000000000..c1b4b55bd3723 --- /dev/null +++ b/tests/run-pass/float_fast_math.rs @@ -0,0 +1,30 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core_intrinsics)] + +use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast}; + +#[inline(never)] +pub fn test_operations(a: f64, b: f64) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } +} + +fn main() { + test_operations(1., 2.); + test_operations(10., 5.); +} From 0b86d30594fe03858416139a940262d2277f53a0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 16:14:59 +0100 Subject: [PATCH 0825/1096] enable Lvalue::Local to refer to a ValPair field --- src/eval_context.rs | 88 +++++++++++++++++++++++++++++-------- src/lvalue.rs | 46 ++++++++++++++----- src/terminator/intrinsic.rs | 4 +- 3 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 739274ce3986c..114927b9574bb 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -851,17 +851,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lvalue: Lvalue<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { - Lvalue::Local { frame, local } => { - match self.stack[frame].get_local(local) { - Value::ByRef(ptr) => Lvalue::from_ptr(ptr), + Lvalue::Local { frame, local, field } => { + // -1 since we don't store the return value + match self.stack[frame].locals[local.index() - 1] { + Value::ByRef(ptr) => { + assert!(field.is_none()); + Lvalue::from_ptr(ptr) + }, val => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].substs); let substs = self.stack[frame].substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].set_local(local, Value::ByRef(ptr)); + self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.write_value_to_ptr(val, ptr, ty)?; - Lvalue::from_ptr(ptr) + let lval = Lvalue::from_ptr(ptr); + if let Some((field, field_ty)) = field { + self.lvalue_field(lval, field, ty, field_ty)? + } else { + lval + } } } } @@ -924,8 +933,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(ptr, val, size) } - Lvalue::Local { frame, local } => { - self.stack[frame].set_local(local, Value::ByVal(val)); + Lvalue::Local { frame, local, field } => { + self.stack[frame].set_local(local, field.map(|(i, _)| i), Value::ByVal(val)); Ok(()) } Lvalue::Global(cid) => { @@ -966,11 +975,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(src_val, ptr, dest_ty) } - Lvalue::Local { frame, local } => { - let dest = self.stack[frame].get_local(local); + Lvalue::Local { frame, local, field } => { + let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i)); self.write_value_possibly_by_val( src_val, - |this, val| this.stack[frame].set_local(local, val), + |this, val| this.stack[frame].set_local(local, field.map(|(i, _)| i), val), dest, dest_ty, ) @@ -1355,16 +1364,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { - if let Lvalue::Local { frame, local } = lvalue { + if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } + if let Some(field) = field { + write!(msg, " (field {:?})", field).unwrap(); + } write!(msg, ":").unwrap(); - match self.stack[frame].get_local(local) { + match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { Value::ByRef(ptr) => { allocs.push(ptr.alloc_id); } @@ -1402,13 +1414,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, frame: usize, local: mir::Local, + field: Option, f: F, ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let val = self.stack[frame].get_local(local); + let val = self.stack[frame].get_local(local, field); let new_val = f(self, val)?; - self.stack[frame].set_local(local, new_val); + self.stack[frame].set_local(local, field, new_val); // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; @@ -1418,14 +1431,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { - pub fn get_local(&self, local: mir::Local) -> Value { + pub fn get_local(&self, local: mir::Local, field: Option) -> Value { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + val @ Value::ByVal(_) => { + assert_eq!(field, 0); + val + }, + Value::ByValPair(a, b) => { + match field { + 0 => Value::ByVal(a), + 1 => Value::ByVal(b), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] + } } - fn set_local(&mut self, local: mir::Local, value: Value) { + fn set_local(&mut self, local: mir::Local, field: Option, value: Value) { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] = value; + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + Value::ByVal(_) => { + assert_eq!(field, 0); + self.set_local(local, None, value); + }, + Value::ByValPair(a, b) => { + let prim = match value { + Value::ByRef(_) => bug!("can't set ValPair field to ByRef"), + Value::ByVal(val) => val, + Value::ByValPair(_, _) => bug!("can't set ValPair field to ValPair"), + }; + match field { + 0 => self.set_local(local, None, Value::ByValPair(prim, b)), + 1 => self.set_local(local, None, Value::ByValPair(a, prim)), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] = value; + } } } diff --git a/src/lvalue.rs b/src/lvalue.rs index 859ed32405ca6..6a4bfb3c7d472 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -23,6 +23,8 @@ pub enum Lvalue<'tcx> { Local { frame: usize, local: mir::Local, + /// Optionally, this lvalue can point to a field of the stack value + field: Option<(usize, Ty<'tcx>)>, }, /// An lvalue referring to a global @@ -116,7 +118,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Local(index) = proj.base { - if let Value::ByValPair(a, b) = self.frame().get_local(index) { + if let Value::ByValPair(a, b) = self.frame().get_local(index, None) { if let mir::ProjectionElem::Field(ref field, _) = proj.elem { let val = [a, b][field.index()]; return Ok(Value::ByVal(val)); @@ -134,7 +136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(extra, LvalueExtra::None); Value::ByRef(ptr) } - Lvalue::Local { frame, local } => self.stack[frame].get_local(local), + Lvalue::Local { frame, local, field } => self.stack[frame].get_local(local, field.map(|(i, _)| i)), Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value, } } @@ -143,7 +145,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local }, + Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(def_id) => { let substs = self.tcx.intern_substs(&[]); @@ -163,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn lvalue_field( &mut self, base: Lvalue<'tcx>, - field: usize, + field_index: usize, base_ty: Ty<'tcx>, field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { @@ -172,32 +174,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { Univariant { ref variant, .. } => { - (variant.offsets[field], variant.packed) + (variant.offsets[field_index], variant.packed) }, General { ref variants, .. } => { let (_, base_extra) = base.to_ptr_and_extra(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 - (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) + (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed) } else { bug!("field access on enum had no variant index"); } } RawNullablePointer { .. } => { - assert_eq!(field, 0); + assert_eq!(field_index, 0); return Ok(base); } StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field], nonnull.packed) + (nonnull.offsets[field_index], nonnull.packed) } UntaggedUnion { .. } => return Ok(base), Vector { element, count } => { - let field = field as u64; + let field = field_index as u64; assert!(field < count); let elem_size = element.size(&self.tcx.data_layout).bytes(); (Size::from_bytes(field * elem_size), false) @@ -206,9 +208,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let (base_ptr, base_extra) = match base { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { + Value::ByRef(ptr) => { + assert!(field.is_none(), "local can't be ByRef and have a field offset"); + (ptr, LvalueExtra::None) + }, + Value::ByVal(_) => { + assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + }, + Value::ByValPair(_, _) => { + assert!(field_index < 2); + return Ok(Lvalue::Local { + frame, + local, + field: Some((field_index, field_ty)), + }); + }, + }, + // FIXME: do for globals what we did for locals + Lvalue::Global(_) => self.force_allocation(base)?.to_ptr_and_extra(), + }; let offset = match base_extra { LvalueExtra::Vtable(tab) => { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2798790feb5a..f7e34d2bc6396 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(zero_val) }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, @@ -386,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), From 1e7481f96e2b17c269c2f96b6820efa15971d30f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 16:26:59 +0100 Subject: [PATCH 0826/1096] remove a hack that is now useless --- src/lvalue.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 6a4bfb3c7d472..f3f8c3fde0cc4 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -116,16 +116,6 @@ impl<'tcx> Global<'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { - if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Local(index) = proj.base { - if let Value::ByValPair(a, b) = self.frame().get_local(index, None) { - if let mir::ProjectionElem::Field(ref field, _) = proj.elem { - let val = [a, b][field.index()]; - return Ok(Value::ByVal(val)); - } - } - } - } let lvalue = self.eval_lvalue(lvalue)?; Ok(self.read_lvalue(lvalue)) } From 55bfbf58a2e9deced24fdf5aed5d30481bc7e407 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 07:55:05 -0800 Subject: [PATCH 0827/1096] Resolve Drop impls to get the right substs. Fixes #133. --- src/terminator/drop.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 13c851244bfad..26e2b6d3dc099 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -1,4 +1,5 @@ use rustc::hir::def_id::DefId; +use rustc::traits; use rustc::ty::layout::Layout; use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, Ty}; @@ -13,7 +14,6 @@ use value::PrimVal; use value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Creates stack frames for all drop impls. See `drop` for the actual content. pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { // add them to the stack in reverse order, because the impl that needs to run the last @@ -98,10 +98,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, .. } => ptr, _ => bug!("force allocation can only yield Lvalue::Ptr"), }; + // run drop impl before the fields' drop impls if let Some(drop_def_id) = adt_def.destructor() { - drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), substs)); + let trait_ref = ty::Binder(ty::TraitRef { + def_id: self.tcx.lang_items.drop_trait().unwrap(), + substs: self.tcx.mk_substs_trait(ty, &[]), + }); + let vtable = match self.fulfill_obligation(trait_ref) { + traits::VtableImpl(data) => data, + _ => bug!("dtor for {:?} is not an impl???", ty) + }; + drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), vtable.substs)); } + let layout = self.type_layout(ty)?; let fields = match *layout { Layout::Univariant { .. } => &adt_def.struct_variant().fields, From e0d635929777c77baf1527efd8c03bf6eeb90f35 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 08:00:28 -0800 Subject: [PATCH 0828/1096] Add test for #133. --- tests/run-pass/miri-issue-133.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/run-pass/miri-issue-133.rs diff --git a/tests/run-pass/miri-issue-133.rs b/tests/run-pass/miri-issue-133.rs new file mode 100644 index 0000000000000..406b5e102c8b4 --- /dev/null +++ b/tests/run-pass/miri-issue-133.rs @@ -0,0 +1,30 @@ +use std::mem::size_of; + +struct S { + _u: U, + size_of_u: usize, + _v: V, + size_of_v: usize +} + +impl S { + fn new(u: U, v: V) -> Self { + S { + _u: u, + size_of_u: size_of::(), + _v: v, + size_of_v: size_of::() + } + } +} + +impl Drop for S { + fn drop(&mut self) { + assert_eq!(size_of::(), self.size_of_u); + assert_eq!(size_of::(), self.size_of_v); + } +} + +fn main() { + S::new(0u8, 1u16); +} From 523c1877d98c5e0796f745c84328b2fa2864639a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 21:59:29 +0100 Subject: [PATCH 0829/1096] print local fields as `_2.1` instead of `_2 (field 1)` --- src/eval_context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 114927b9574bb..280117a4247ed 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1367,13 +1367,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); + if let Some(field) = field { + write!(msg, ".{}", field).unwrap(); + } let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } - if let Some(field) = field { - write!(msg, " (field {:?})", field).unwrap(); - } write!(msg, ":").unwrap(); match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { From 35cf19f38a5aef8657b53f15b2df69c3a7080491 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 22:15:30 +0100 Subject: [PATCH 0830/1096] only print the index part --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 280117a4247ed..d2d33610f2841 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1367,7 +1367,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); - if let Some(field) = field { + if let Some((field, _)) = field { write!(msg, ".{}", field).unwrap(); } let last_frame = self.stack.len() - 1; From 6ffd7005c150dac4a99eea73eeb72189e0f5f694 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 13:35:33 -0800 Subject: [PATCH 0831/1096] Cache string and bytestring literal allocs. --- src/eval_context.rs | 12 +++--------- src/memory.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index d2d33610f2841..6ed6b6bebc066 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -168,11 +168,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } - pub(super) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { - // FIXME: cache these allocs - let ptr = self.memory.allocate(s.len() as u64, 1)?; - self.memory.write_bytes(ptr, s.as_bytes())?; - self.memory.mark_static_initalized(ptr.alloc_id, false)?; + pub(crate) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { + let ptr = self.memory.allocate_cached(s.as_bytes())?; Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } @@ -194,10 +191,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { - // FIXME: cache these allocs - let ptr = self.memory.allocate(bs.len() as u64, 1)?; - self.memory.write_bytes(ptr, bs)?; - self.memory.mark_static_initalized(ptr.alloc_id, false)?; + let ptr = self.memory.allocate_cached(bs)?; PrimVal::Ptr(ptr) } diff --git a/src/memory.rs b/src/memory.rs index 835c7bca7f60f..0d3bec62e9939 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -180,6 +180,10 @@ pub struct Memory<'a, 'tcx> { /// the normal struct access will succeed even though it shouldn't. /// But even with mir optimizations, that situation is hard/impossible to produce. packed: BTreeSet, + + /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate + /// allocations for string and bytestring literals. + literal_alloc_cache: HashMap, AllocId>, } const ZST_ALLOC_ID: AllocId = AllocId(0); @@ -197,6 +201,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { memory_usage: 0, packed: BTreeSet::new(), static_alloc: HashSet::new(), + literal_alloc_cache: HashMap::new(), } } @@ -263,6 +268,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Pointer::new(id, 0) } + pub fn allocate_cached(&mut self, bytes: &[u8]) -> EvalResult<'tcx, Pointer> { + if let Some(&alloc_id) = self.literal_alloc_cache.get(bytes) { + return Ok(Pointer::new(alloc_id, 0)); + } + + let ptr = self.allocate(bytes.len() as u64, 1)?; + self.write_bytes(ptr, bytes)?; + self.mark_static_initalized(ptr.alloc_id, false)?; + self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id); + Ok(ptr) + } + pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, Pointer> { if size == 0 { return Ok(Pointer::zst_ptr()); From 0f714b72a0c6936c26a354eac560a51a33606119 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 13:35:45 -0800 Subject: [PATCH 0832/1096] Formatting. --- src/eval_context.rs | 12 ++++++------ src/memory.rs | 44 ++++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 6ed6b6bebc066..2dd6662e63086 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -24,24 +24,24 @@ pub type MirRef<'tcx> = Ref<'tcx, mir::Mir<'tcx>>; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. - pub(super) tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub(crate) tcx: TyCtxt<'a, 'tcx, 'tcx>, /// The virtual memory system. - pub(super) memory: Memory<'a, 'tcx>, + pub(crate) memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. - pub(super) globals: HashMap, Global<'tcx>>, + pub(crate) globals: HashMap, Global<'tcx>>, /// The virtual call stack. - pub(super) stack: Vec>, + pub(crate) stack: Vec>, /// The maximum number of stack frames allowed - pub(super) stack_limit: usize, + pub(crate) stack_limit: usize, /// The maximum number of operations that may be executed. /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. - pub(super) steps_remaining: u64, + pub(crate) steps_remaining: u64, } /// A stack frame. diff --git a/src/memory.rs b/src/memory.rs index 0d3bec62e9939..0c7b4971e1df7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -152,33 +152,45 @@ impl<'tcx> Function<'tcx> { //////////////////////////////////////////////////////////////////////////////// pub struct Memory<'a, 'tcx> { - /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) + /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations). alloc_map: HashMap, - /// Set of statics, constants, promoteds, vtables, ... to prevent `mark_static_initalized` from stepping - /// out of its own allocations. - /// This set only contains statics backed by an allocation. If they are ByVal or ByValPair they - /// are not here, but will be inserted once they become ByRef. + + /// The AllocId to assign to the next new allocation. Always incremented, never gets smaller. + next_id: AllocId, + + /// Set of statics, constants, promoteds, vtables, ... to prevent `mark_static_initalized` from + /// stepping out of its own allocations. This set only contains statics backed by an + /// allocation. If they are ByVal or ByValPair they are not here, but will be inserted once + /// they become ByRef. static_alloc: HashSet, - /// Number of virtual bytes allocated + + /// Number of virtual bytes allocated. memory_usage: u64, - /// Maximum number of virtual bytes that may be allocated + + /// Maximum number of virtual bytes that may be allocated. memory_size: u64, + /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. functions: HashMap>, + /// Inverse map of `functions` so we don't allocate a new pointer every time we need one function_alloc_cache: HashMap, AllocId>, - next_id: AllocId, + + /// Target machine data layout to emulate. pub layout: &'a TargetDataLayout, - /// List of memory regions containing packed structures - /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking afterwards. - /// In the case where no packed structs are present, it's just a single emptyness check of a set - /// instead of heavily influencing all memory access code as other solutions would. + + /// List of memory regions containing packed structures. + /// + /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking + /// afterwards. In the case where no packed structs are present, it's just a single emptyness + /// check of a set instead of heavily influencing all memory access code as other solutions + /// would. /// - /// One disadvantage of this solution is the fact that you can cast a pointer to a packed struct - /// to a pointer to a normal struct and if you access a field of both in the same MIR statement, - /// the normal struct access will succeed even though it shouldn't. - /// But even with mir optimizations, that situation is hard/impossible to produce. + /// One disadvantage of this solution is the fact that you can cast a pointer to a packed + /// struct to a pointer to a normal struct and if you access a field of both in the same MIR + /// statement, the normal struct access will succeed even though it shouldn't. But even with + /// mir optimizations, that situation is hard/impossible to produce. packed: BTreeSet, /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate From f73f001ce58026633c2399daf75bc22ac7b698cc Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 14:53:56 -0800 Subject: [PATCH 0833/1096] Fix fabsf{32,64} intrinsics. --- src/terminator/intrinsic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index c6e33b0a08e79..0443206e80dba 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -208,12 +208,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fabsf32" => { - let f = self.value_to_primval(arg_vals[2], f32)?.to_f32()?; + let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; self.write_primval(dest, PrimVal::from_f32(f.abs()), dest_ty)?; } "fabsf64" => { - let f = self.value_to_primval(arg_vals[2], f64)?.to_f64()?; + let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; self.write_primval(dest, PrimVal::from_f64(f.abs()), dest_ty)?; } From b755a91c21d23fd8852a2c9b42fe0a7a4b9fb04b Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 14:54:14 -0800 Subject: [PATCH 0834/1096] Do drop glue for closures. --- src/terminator/drop.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 26e2b6d3dc099..c8875398918f4 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -51,7 +51,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { debug!("no need to drop {:?}", ty); return Ok(()); } - trace!("-need to drop {:?} at {:?}", ty, lval); + trace!("need to drop {:?} at {:?}", ty, lval); match ty.sty { // special case `Box` to deallocate the inner allocation @@ -89,7 +89,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // is the same as // fn drop(&mut self) if Self is Box drop.push((box_free_fn, val, substs)); - }, + } ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair @@ -165,8 +165,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty, drop, )?; - }, - ty::TyTuple(fields, _) => self.drop_fields(fields.into_iter().cloned(), lval, ty, drop)?, + } + + ty::TyTuple(fields, _) => + self.drop_fields(fields.into_iter().cloned(), lval, ty, drop)?, + ty::TyDynamic(..) => { let (ptr, vtable) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), @@ -181,7 +184,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // just a sanity check assert_eq!(drop_fn.offset, 0); } - }, + } + ty::TySlice(elem_ty) => { let (ptr, len) = match lval { Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), @@ -193,7 +197,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for i in 0..len { self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; } - }, + } + ty::TyArray(elem_ty, len) => { let lval = self.force_allocation(lval)?; let (ptr, extra) = match lval { @@ -206,10 +211,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for i in 0..(len as u64) { self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; } - }, - // FIXME: what about TyClosure and TyAnon? - // other types do not need to process drop - _ => {}, + } + + ty::TyClosure(def_id, substs) => { + let fields = substs.upvar_tys(def_id, self.tcx); + self.drop_fields(fields, lval, ty, drop)?; + } + + _ => bug!(), } Ok(()) From d8e5500c6dedfa9a754b1e8831c5ae37c368756e Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 15:05:29 -0800 Subject: [PATCH 0835/1096] Add test for closure drop. --- tests/run-pass/closure-drop.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/run-pass/closure-drop.rs diff --git a/tests/run-pass/closure-drop.rs b/tests/run-pass/closure-drop.rs new file mode 100644 index 0000000000000..913df06f15908 --- /dev/null +++ b/tests/run-pass/closure-drop.rs @@ -0,0 +1,30 @@ +struct Foo<'a>(&'a mut bool); + +impl<'a> Drop for Foo<'a> { + fn drop(&mut self) { + *self.0 = true; + } +} + +fn f(t: T) { + t() +} + +fn main() { + let mut ran_drop = false; + { + // FIXME: v is a temporary hack to force the below closure to be a FnOnce-only closure + // (with sig fn(self)). Without it, the closure sig would be fn(&self) which requires a + // shim to call via FnOnce::call_once, and Miri's current shim doesn't correctly call + // destructors. + let v = vec![1]; + let x = Foo(&mut ran_drop); + let g = move || { + let _ = x; + drop(v); // Force the closure to be FnOnce-only by using a capture by-value. + }; + f(g); + } + assert!(ran_drop); +} + From eeae478e7428f40959d048adce14605c47a6d378 Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Fri, 10 Feb 2017 15:13:50 -0800 Subject: [PATCH 0836/1096] Remove stable feature flag. --- src/bin/cargo-miri.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs index 58109ad59587d..b643c83395fd6 100644 --- a/src/bin/cargo-miri.rs +++ b/src/bin/cargo-miri.rs @@ -1,5 +1,3 @@ -#![feature(static_in_const)] - extern crate cargo_metadata; use std::path::{PathBuf, Path}; From a727ceb7e9c1eb2d4d27f787d66425bc57534213 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Feb 2017 13:46:39 +0100 Subject: [PATCH 0837/1096] fast path for zsts --- src/eval_context.rs | 3 +++ src/terminator/mod.rs | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 2dd6662e63086..9fb6492efd39e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -436,6 +436,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, operator::unary_op(un_op, val, kind)?, dest_ty)?; } + // Skip everything for zsts + Aggregate(..) if self.type_size(dest_ty)? == Some(0) => {} + Aggregate(ref kind, ref operands) => { self.inc_step_counter_and_check_limit(operands.len() as u64)?; use rustc::ty::layout::Layout::*; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a3b47f533f4be..d8035c9f6700f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -501,13 +501,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) => { let offsets = variant.offsets.iter().map(|s| s.bytes()); - let last_ptr = match last { - Value::ByRef(ptr) => ptr, - _ => bug!("rust-call ABI tuple argument wasn't Value::ByRef"), - }; - for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset)); - args.push((arg, ty)); + match last { + Value::ByRef(last_ptr) => { + for (offset, ty) in offsets.zip(fields) { + let arg = Value::ByRef(last_ptr.offset(offset)); + args.push((arg, ty)); + } + }, + // propagate undefs + undef @ Value::ByVal(PrimVal::Undef) => { + for field_ty in fields { + args.push((undef, field_ty)); + } + }, + _ => bug!("rust-call ABI tuple argument was {:?}", last), } } ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), From ac71d6f345001031a46fb49f0513ec576752eaee Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Feb 2017 11:58:42 +0100 Subject: [PATCH 0838/1096] don't duplicate field access logic, always go through lvalue_field --- src/eval_context.rs | 50 ++++++++++++++++++------------------------- src/lvalue.rs | 17 +++++++++++++++ src/terminator/mod.rs | 17 +++++++-------- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9fb6492efd39e..b57e3a49bd6a7 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -358,45 +358,45 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn assign_discr_and_fields< - I: IntoIterator, V: IntoValTyPair<'tcx>, J: IntoIterator, >( &mut self, dest: Lvalue<'tcx>, - offsets: I, + dest_ty: Ty<'tcx>, + discr_offset: u64, operands: J, discr_val: u128, + variant_idx: usize, discr_size: u64, ) -> EvalResult<'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); - let mut offsets = offsets.into_iter(); - let discr_offset = offsets.next().unwrap(); let discr_dest = dest_ptr.offset(discr_offset); self.memory.write_uint(discr_dest, discr_val, discr_size)?; - self.assign_fields(dest, offsets, operands) + let dest = Lvalue::Ptr { + ptr: dest_ptr, + extra: LvalueExtra::DowncastVariant(variant_idx), + }; + + self.assign_fields(dest, dest_ty, operands) } pub fn assign_fields< - I: IntoIterator, V: IntoValTyPair<'tcx>, J: IntoIterator, >( &mut self, dest: Lvalue<'tcx>, - offsets: I, + dest_ty: Ty<'tcx>, operands: J, ) -> EvalResult<'tcx> { - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); - - for (offset, operand) in offsets.into_iter().zip(operands) { + for (field_index, operand) in operands.into_iter().enumerate() { let (value, value_ty) = operand.into_val_ty_pair(self)?; - let field_dest = dest.offset(offset); - self.write_value_to_ptr(value, field_dest, value_ty)?; + let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?; + self.write_value(value, field_dest, value_ty)?; } Ok(()) } @@ -444,22 +444,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *dest_layout { Univariant { ref variant, .. } => { - let offsets = variant.offsets.iter().map(|s| s.bytes()); if variant.packed { let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; self.memory.mark_packed(ptr, variant.stride().bytes()); } - self.assign_fields(dest, offsets, operands)?; + self.assign_fields(dest, dest_ty, operands)?; } Array { .. } => { - let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty)? - .expect("array elements are sized") as u64, - _ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), - }; - let offsets = (0..).map(|i| i * elem_size); - self.assign_fields(dest, offsets, operands)?; + self.assign_fields(dest, dest_ty, operands)?; } General { discr, ref variants, .. } => { @@ -473,9 +466,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.assign_discr_and_fields( dest, - variants[variant].offsets.iter().cloned().map(Size::bytes), + dest_ty, + variants[variant].offsets[0].bytes(), operands, discr_val, + variant, discr_size, )?; } else { @@ -511,8 +506,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.mark_packed(ptr, nonnull.stride().bytes()); } if nndiscr == variant as u64 { - let offsets = nonnull.offsets.iter().map(|s| s.bytes()); - self.assign_fields(dest, offsets, operands)?; + self.assign_fields(dest, dest_ty, operands)?; } else { for operand in operands { let operand_ty = self.operand_ty(operand); @@ -543,11 +537,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - Vector { element, count } => { - let elem_size = element.size(&self.tcx.data_layout).bytes(); + Vector { count, .. } => { debug_assert_eq!(count, operands.len() as u64); - let offsets = (0..).map(|i| i * elem_size); - self.assign_fields(dest, offsets, operands)?; + self.assign_fields(dest, dest_ty, operands)?; } UntaggedUnion { .. } => { diff --git a/src/lvalue.rs b/src/lvalue.rs index f3f8c3fde0cc4..d4d292cd4e53e 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -195,6 +195,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Size::from_bytes(field * elem_size), false) } + // We treat arrays + fixed sized indexing like field accesses + Array { .. } => { + let field = field_index as u64; + let elem_size = match base_ty.sty { + ty::TyArray(elem_ty, n) => { + assert!(field < n as u64); + self.type_size(elem_ty)?.expect("array elements are sized") as u64 + }, + _ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty), + }; + (Size::from_bytes(field * elem_size), false) + } + _ => bug!("field access on non-product type: {:?}", base_layout), }; @@ -205,6 +218,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert!(field.is_none(), "local can't be ByRef and have a field offset"); (ptr, LvalueExtra::None) }, + Value::ByVal(PrimVal::Undef) => { + // FIXME: add some logic for when to not allocate + (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + }, Value::ByVal(_) => { assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); return Ok(base); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d8035c9f6700f..4fb3968161343 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::layout::{Layout, Size}; +use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, BareFnTy}; use syntax::codemap::Span; @@ -215,29 +215,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_layout = self.type_layout(dest_ty)?; trace!("layout({:?}) = {:#?}", dest_ty, dest_layout); match *dest_layout { - Layout::Univariant { ref variant, .. } => { + Layout::Univariant { .. } => { let disr_val = v.disr_val.to_u128_unchecked(); assert_eq!(disr_val, 0); - let offsets = variant.offsets.iter().map(|s| s.bytes()); - - self.assign_fields(lvalue, offsets, args)?; + self.assign_fields(lvalue, dest_ty, args)?; }, Layout::General { discr, ref variants, .. } => { let disr_val = v.disr_val.to_u128_unchecked(); let discr_size = discr.size().bytes(); self.assign_discr_and_fields( lvalue, - variants[disr_val as usize].offsets.iter().cloned().map(Size::bytes), + dest_ty, + variants[disr_val as usize].offsets[0].bytes(), args, disr_val, + disr_val as usize, discr_size, )?; }, - Layout::StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { + Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let disr_val = v.disr_val.to_u128_unchecked(); if nndiscr as u128 == disr_val { - let offsets = nonnull.offsets.iter().map(|s| s.bytes()); - self.assign_fields(lvalue, offsets, args)?; + self.assign_fields(lvalue, dest_ty, args)?; } else { for (_, ty) in args { assert_eq!(self.type_size(ty)?, Some(0)); From d4da7c46dd1d8f22804a2c4767bc98ce6fe03a83 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 10:17:00 +0100 Subject: [PATCH 0839/1096] rustup --- src/eval_context.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b57e3a49bd6a7..13fad7b4644af 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -11,7 +11,6 @@ use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::indexed_vec::Idx; -use rustc_data_structures::fx::FxHashSet; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -1588,7 +1587,7 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty:: } pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty() + ty.uninhabited_from(&mut HashMap::default(), tcx).is_empty() } pub trait IntoValTyPair<'tcx> { From 4730cdf8250192d1fb268dd027b9cb40fa6fe370 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 12:48:46 +0100 Subject: [PATCH 0840/1096] fix a bug in drop code of structs with unsized fields --- src/terminator/drop.rs | 13 ++++++++----- tests/run-pass/issue-26709.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass/issue-26709.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index c8875398918f4..71f3d46736605 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -94,10 +94,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(adt_def, substs) => { // FIXME: some structs are represented as ByValPair let mut lval = self.force_allocation(lval)?; - let adt_ptr = match lval { - Lvalue::Ptr { ptr, .. } => ptr, - _ => bug!("force allocation can only yield Lvalue::Ptr"), - }; + let (adt_ptr, extra) = lval.to_ptr_and_extra(); // run drop impl before the fields' drop impls if let Some(drop_def_id) = adt_def.destructor() { @@ -109,7 +106,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableImpl(data) => data, _ => bug!("dtor for {:?} is not an impl???", ty) }; - drop.push((drop_def_id, Value::ByVal(PrimVal::Ptr(adt_ptr)), vtable.substs)); + let val = match extra { + LvalueExtra::None => Value::ByVal(PrimVal::Ptr(adt_ptr)), + LvalueExtra::DowncastVariant(_) => bug!("downcast variant in drop"), + LvalueExtra::Length(n) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::from_u128(n as u128)), + LvalueExtra::Vtable(vtable) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::Ptr(vtable)), + }; + drop.push((drop_def_id, val, vtable.substs)); } let layout = self.type_layout(ty)?; diff --git a/tests/run-pass/issue-26709.rs b/tests/run-pass/issue-26709.rs new file mode 100644 index 0000000000000..62626d75865cf --- /dev/null +++ b/tests/run-pass/issue-26709.rs @@ -0,0 +1,26 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Wrapper<'a, T: ?Sized>(&'a mut i32, T); + +impl<'a, T: ?Sized> Drop for Wrapper<'a, T> { + fn drop(&mut self) { + *self.0 = 432; + } +} + +fn main() { + let mut x = 0; + { + let wrapper = Box::new(Wrapper(&mut x, 123)); + let _: Box> = wrapper; + } + assert_eq!(432, x) +} From 545f70010cd02dc7090e99d89745e4d01d7962f0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 10:17:00 +0100 Subject: [PATCH 0841/1096] rustup --- src/eval_context.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 2dd6662e63086..6244e9c8d8a68 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -11,7 +11,6 @@ use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::indexed_vec::Idx; -use rustc_data_structures::fx::FxHashSet; use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; @@ -1593,7 +1592,7 @@ pub fn monomorphize_field_ty<'a, 'tcx:'a >(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: &ty:: } pub fn is_inhabited<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.uninhabited_from(&mut FxHashSet::default(), tcx).is_empty() + ty.uninhabited_from(&mut HashMap::default(), tcx).is_empty() } pub trait IntoValTyPair<'tcx> { From 35502fd47daf3a95d01e3a538e429c7ceb9de03b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 24 Feb 2017 10:39:55 +0100 Subject: [PATCH 0842/1096] rustup --- src/eval_context.rs | 19 ++++++++++++++++--- src/lib.rs | 1 - src/step.rs | 2 ++ src/terminator/drop.rs | 2 +- src/terminator/mod.rs | 39 ++++++++------------------------------- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 13fad7b4644af..54aae64c5caaf 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -456,7 +456,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked(); + let discr_val = adt_def.variants[variant].disr_val; let discr_size = discr.size().bytes(); if variants[variant].packed { let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; @@ -529,7 +529,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let n = adt_def.variants[variant].disr_val.to_u128_unchecked(); + let n = adt_def.variants[variant].disr_val; self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); @@ -661,7 +661,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - InlineAsm { .. } => return Err(EvalError::InlineAsm), + Discriminant(ref lvalue) => { + let lval = self.eval_lvalue(lvalue)?; + let ty = self.lvalue_ty(lvalue); + let ptr = self.force_allocation(lval)?.to_ptr(); + let discr_val = self.read_discriminant_value(ptr, ty)?; + if let ty::TyAdt(adt_def, _) = ty.sty { + if adt_def.variants.iter().all(|v| discr_val != v.disr_val) { + return Err(EvalError::InvalidDiscriminant); + } + } else { + bug!("rustc only generates Rvalue::Discriminant for enums"); + } + self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; + }, } if log_enabled!(::log::LogLevel::Trace) { diff --git a/src/lib.rs b/src/lib.rs index 290b478eb648b..d1d8e8cf229f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![feature( btree_range, collections, - field_init_shorthand, i128_type, pub_restricted, rustc_private, diff --git a/src/step.rs b/src/step.rs index 5c957f0cdc08b..c08ac9693a4b1 100644 --- a/src/step.rs +++ b/src/step.rs @@ -119,6 +119,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} + + InlineAsm { .. } => return Err(EvalError::InlineAsm), } self.frame_mut().stmt += 1; diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 71f3d46736605..dd199a4266c14 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -121,7 +121,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::General { .. } => { let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; let ptr = self.force_allocation(lval)?.to_ptr(); - match adt_def.variants.iter().position(|v| discr_val == v.disr_val.to_u128_unchecked()) { + match adt_def.variants.iter().position(|v| discr_val == v.disr_val) { Some(i) => { lval = Lvalue::Ptr { ptr, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4fb3968161343..183a3f54fb639 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -35,22 +35,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), - If { ref cond, targets: (then_target, else_target) } => { - let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; - self.goto_block(if cond_val { then_target } else { else_target }); - } - SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_val = self.eval_and_read_lvalue(discr)?; - let discr_ty = self.lvalue_ty(discr); + let discr_val = self.eval_operand(discr)?; + let discr_ty = self.operand_ty(discr); let discr_prim = self.value_to_primval(discr_val, discr_ty)?; // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, const_val) in values.iter().enumerate() { - let val = self.const_to_value(const_val)?; - let prim = self.value_to_primval(val, discr_ty)?; + for (index, const_int) in values.iter().enumerate() { + let prim = PrimVal::Bytes(const_int.to_u128_unchecked()); if discr_prim.to_bytes()? == prim.to_bytes()? { target_block = targets[index]; break; @@ -60,23 +54,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(target_block); } - Switch { ref discr, ref targets, adt_def } => { - // FIXME(solson) - let lvalue = self.eval_lvalue(discr)?; - let lvalue = self.force_allocation(lvalue)?; - - let adt_ptr = lvalue.to_ptr(); - let adt_ty = self.lvalue_ty(discr); - let discr_val = self.read_discriminant_value(adt_ptr, adt_ty)?; - let matching = adt_def.variants.iter() - .position(|v| discr_val == v.disr_val.to_u128_unchecked()); - - match matching { - Some(i) => self.goto_block(targets[i]), - None => return Err(EvalError::InvalidDiscriminant), - } - } - Call { ref func, ref args, ref destination, .. } => { let destination = match *destination { Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)), @@ -216,12 +193,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("layout({:?}) = {:#?}", dest_ty, dest_layout); match *dest_layout { Layout::Univariant { .. } => { - let disr_val = v.disr_val.to_u128_unchecked(); + let disr_val = v.disr_val; assert_eq!(disr_val, 0); self.assign_fields(lvalue, dest_ty, args)?; }, Layout::General { discr, ref variants, .. } => { - let disr_val = v.disr_val.to_u128_unchecked(); + let disr_val = v.disr_val; let discr_size = discr.size().bytes(); self.assign_discr_and_fields( lvalue, @@ -234,7 +211,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; }, Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let disr_val = v.disr_val.to_u128_unchecked(); + let disr_val = v.disr_val; if nndiscr as u128 == disr_val { self.assign_fields(lvalue, dest_ty, args)?; } else { @@ -325,7 +302,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; trace!("read_discriminant_value {:#?}", adt_layout); From 25c3a4fb0042a8d1975daff0340c7f967a6d4f60 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 15:35:03 +0100 Subject: [PATCH 0843/1096] drop all temporary closure allocations --- src/eval_context.rs | 17 +++++++++-------- src/traits.rs | 5 ++--- tests/run-pass/move_fn_closure.rs | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 tests/run-pass/move_fn_closure.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 54aae64c5caaf..1bf71e149dc34 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -81,8 +81,8 @@ pub struct Frame<'tcx> { /// Temporary allocations introduced to save stackframes /// This is pure interpreter magic and has nothing to do with how rustc does it /// An example is calling an FnMut closure that has been converted to a FnOnce closure - /// The memory will be freed when the stackframe finishes - pub interpreter_temporaries: Vec, + /// The value's destructor will be called and the memory freed when the stackframe finishes + pub interpreter_temporaries: Vec<(Pointer, Ty<'tcx>)>, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -273,7 +273,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, - temporaries: Vec, + temporaries: Vec<(Pointer, Ty<'tcx>)>, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -347,11 +347,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } - // deallocate all temporary allocations - for ptr in frame.interpreter_temporaries { - trace!("deallocating temporary allocation"); - self.memory.dump_alloc(ptr.alloc_id); - self.memory.deallocate(ptr)?; + // drop and deallocate all temporary allocations + for (ptr, ty) in frame.interpreter_temporaries { + trace!("dropping temporary allocation"); + let mut drops = Vec::new(); + self.drop(Lvalue::from_ptr(ptr), ty, &mut drops)?; + self.eval_drop_impls(drops, frame.span)?; } Ok(()) } diff --git a/src/traits.rs b/src/traits.rs index b892efbf569bb..a46d6096dfc95 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -23,7 +23,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { def_id: DefId, substs: &'tcx Substs<'tcx>, args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec)> { + ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec<(Pointer, Ty<'tcx>)>)> { let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); @@ -72,16 +72,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.alloc_ptr(args[0].1)?; let size = self.type_size(args[0].1)?.expect("closures are sized"); self.memory.write_primval(ptr, primval, size)?; - temporaries.push(ptr); ptr }, Value::ByValPair(a, b) => { let ptr = self.alloc_ptr(args[0].1)?; self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - temporaries.push(ptr); ptr }, }; + temporaries.push((ptr, args[0].1)); args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } diff --git a/tests/run-pass/move_fn_closure.rs b/tests/run-pass/move_fn_closure.rs new file mode 100644 index 0000000000000..e3e66c6f80fec --- /dev/null +++ b/tests/run-pass/move_fn_closure.rs @@ -0,0 +1,24 @@ +struct Foo<'a>(&'a mut bool); + +impl<'a> Drop for Foo<'a> { + fn drop(&mut self) { + *self.0 = true; + } +} + +fn f(t: T) { + t() +} + +fn main() { + let mut ran_drop = false; + { + let x = Foo(&mut ran_drop); + // this closure never by val uses its captures + // so it's basically a fn(&self) + // the shim used to not drop the `x` + let x = move || { let _ = x; }; + f(x); + } + assert!(ran_drop); +} From 529efc51e817f18b879bf5f03dea21e36617b05f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 15:35:13 +0100 Subject: [PATCH 0844/1096] detect memory leaks --- src/eval_context.rs | 8 +++++++- src/memory.rs | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 1bf71e149dc34..fb83eff502a2d 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1510,7 +1510,13 @@ pub fn eval_main<'a, 'tcx: 'a>( loop { match ecx.step() { Ok(true) => {} - Ok(false) => return, + Ok(false) => { + let leaks = ecx.memory.leak_report(); + if leaks != 0 { + tcx.sess.err("the evaluated program leaked memory"); + } + return; + } Err(e) => { report(tcx, &ecx, e); return; diff --git a/src/memory.rs b/src/memory.rs index 0c7b4971e1df7..459a9bed4128f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -585,6 +585,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } } + + pub fn leak_report(&self) -> usize { + trace!("### LEAK REPORT ###"); + let leaks: Vec<_> = self.alloc_map + .iter() + .filter_map(|(&key, val)| { + if val.static_kind == StaticKind::NotStatic { + Some(key) + } else { + None + } + }) + .collect(); + let n = leaks.len(); + self.dump_allocs(leaks); + n + } } fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String { From c4c9ff8d3f1eed85ec7bb08e402e64f545d45e42 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 16:55:30 +0100 Subject: [PATCH 0845/1096] remove memory leak from run-pass test --- tests/run-pass/option_box_transmute_ptr.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/run-pass/option_box_transmute_ptr.rs b/tests/run-pass/option_box_transmute_ptr.rs index c81db8e8b2354..0786db1ef895a 100644 --- a/tests/run-pass/option_box_transmute_ptr.rs +++ b/tests/run-pass/option_box_transmute_ptr.rs @@ -3,7 +3,10 @@ fn option_box_deref() -> i32 { let val = Some(Box::new(42)); unsafe { let ptr: *const i32 = std::mem::transmute::>, *const i32>(val); - *ptr + let ret = *ptr; + // unleak memory + std::mem::transmute::<*const i32, Option>>(ptr); + ret } } From 51f209a61f38eaabe6bce54118e53432a7ba5d55 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 16:56:21 +0100 Subject: [PATCH 0846/1096] write_primval used to leak memory if the destination was ByRef instead of duplicating all the code from `write_value`, we forward to it. --- src/eval_context.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index fb83eff502a2d..0ab6e85e389d0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -929,26 +929,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { val: PrimVal, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { - match dest { - Lvalue::Ptr { ptr, extra } => { - assert_eq!(extra, LvalueExtra::None); - let size = self.type_size(dest_ty)?.expect("dest type must be sized"); - self.memory.write_primval(ptr, val, size) - } - Lvalue::Local { frame, local, field } => { - self.stack[frame].set_local(local, field.map(|(i, _)| i), Value::ByVal(val)); - Ok(()) - } - Lvalue::Global(cid) => { - let global_val = self.globals.get_mut(&cid).expect("global not cached"); - if global_val.mutable { - global_val.value = Value::ByVal(val); - Ok(()) - } else { - Err(EvalError::ModifiedConstantMemory) - } - } - } + self.write_value(Value::ByVal(val), dest, dest_ty) } pub(super) fn write_value( From 5829483f4dbee193a1e1c9950c6920dbed076fd6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 09:03:07 +0100 Subject: [PATCH 0847/1096] add a test for the memory leak error --- tests/compile-fail/memleak.rs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/compile-fail/memleak.rs diff --git a/tests/compile-fail/memleak.rs b/tests/compile-fail/memleak.rs new file mode 100644 index 0000000000000..71b4e2f442f31 --- /dev/null +++ b/tests/compile-fail/memleak.rs @@ -0,0 +1,5 @@ +//error-pattern: the evaluated program leaked memory + +fn main() { + std::mem::forget(Box::new(42)); +} From 38d16ccacc761ce31477f2ad3efc87ea53bfab06 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 16:38:27 +0100 Subject: [PATCH 0848/1096] add test with an Rc cycle to create a memleak --- tests/compile-fail/memleak_rc.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/compile-fail/memleak_rc.rs diff --git a/tests/compile-fail/memleak_rc.rs b/tests/compile-fail/memleak_rc.rs new file mode 100644 index 0000000000000..b2bc6722afb04 --- /dev/null +++ b/tests/compile-fail/memleak_rc.rs @@ -0,0 +1,12 @@ +//error-pattern: the evaluated program leaked memory + +use std::rc::Rc; +use std::cell::RefCell; + +struct Dummy(Rc>>); + +fn main() { + let x = Dummy(Rc::new(RefCell::new(None))); + let y = Dummy(x.0.clone()); + *x.0.borrow_mut() = Some(y); +} From 31c81ac3227e8f4aa4a16f88ebf5af6414ee9d0e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 24 Feb 2017 10:42:11 +0100 Subject: [PATCH 0849/1096] Merge `move_fn_closure` run-passtest into `closure-drop` --- tests/run-pass/closure-drop.rs | 15 +++++---------- tests/run-pass/move_fn_closure.rs | 24 ------------------------ 2 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 tests/run-pass/move_fn_closure.rs diff --git a/tests/run-pass/closure-drop.rs b/tests/run-pass/closure-drop.rs index 913df06f15908..f1bdafaeb1354 100644 --- a/tests/run-pass/closure-drop.rs +++ b/tests/run-pass/closure-drop.rs @@ -13,17 +13,12 @@ fn f(t: T) { fn main() { let mut ran_drop = false; { - // FIXME: v is a temporary hack to force the below closure to be a FnOnce-only closure - // (with sig fn(self)). Without it, the closure sig would be fn(&self) which requires a - // shim to call via FnOnce::call_once, and Miri's current shim doesn't correctly call - // destructors. - let v = vec![1]; let x = Foo(&mut ran_drop); - let g = move || { - let _ = x; - drop(v); // Force the closure to be FnOnce-only by using a capture by-value. - }; - f(g); + // this closure never by val uses its captures + // so it's basically a fn(&self) + // the shim used to not drop the `x` + let x = move || { let _ = x; }; + f(x); } assert!(ran_drop); } diff --git a/tests/run-pass/move_fn_closure.rs b/tests/run-pass/move_fn_closure.rs deleted file mode 100644 index e3e66c6f80fec..0000000000000 --- a/tests/run-pass/move_fn_closure.rs +++ /dev/null @@ -1,24 +0,0 @@ -struct Foo<'a>(&'a mut bool); - -impl<'a> Drop for Foo<'a> { - fn drop(&mut self) { - *self.0 = true; - } -} - -fn f(t: T) { - t() -} - -fn main() { - let mut ran_drop = false; - { - let x = Foo(&mut ran_drop); - // this closure never by val uses its captures - // so it's basically a fn(&self) - // the shim used to not drop the `x` - let x = move || { let _ = x; }; - f(x); - } - assert!(ran_drop); -} From 2282e6b58253c5aad75fda48a8e9b15f0c9f62b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 10:59:38 +0100 Subject: [PATCH 0850/1096] represent single field structs as their single field --- src/eval_context.rs | 54 +++++++++++++++++++++++++++++++++++-------- src/lvalue.rs | 8 ++++++- src/memory.rs | 16 +++++++++---- src/terminator/mod.rs | 2 +- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ab6e85e389d0..123d1f5d47f8f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -312,14 +312,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match global_value.value { Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } }, } @@ -369,7 +369,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { discr_val: u128, variant_idx: usize, discr_size: u64, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + where J::IntoIter: ExactSizeIterator, + { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); @@ -392,7 +394,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, operands: J, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + where J::IntoIter: ExactSizeIterator, + { + if self.type_size(dest_ty)? == Some(0) { + // zst assigning is a nop + return Ok(()); + } + if self.ty_to_primval_kind(dest_ty).is_ok() { + let mut iter = operands.into_iter(); + assert_eq!(iter.len(), 1); + let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?; + return self.write_value(value, dest, value_ty); + } for (field_index, operand) in operands.into_iter().enumerate() { let (value, value_ty) = operand.into_val_ty_pair(self)?; let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?; @@ -780,7 +794,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { + pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; @@ -1037,8 +1051,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { a: PrimVal, b: PrimVal, ptr: Pointer, - ty: Ty<'tcx> + mut ty: Ty<'tcx> ) -> EvalResult<'tcx> { + while self.get_field_count(ty)? == 1 { + ty = self.get_field_ty(ty, 0)?; + } assert_eq!(self.get_field_count(ty)?, 2); let field_0 = self.get_field_offset(ty, 0)?.bytes(); let field_1 = self.get_field_offset(ty, 1)?.bytes(); @@ -1094,7 +1111,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr, - ty::TyAdt(..) => { + ty::TyAdt(ref def, substs) => { use rustc::ty::layout::Layout::*; match *self.type_layout(ty)? { CEnum { discr, signed, .. } => { @@ -1117,6 +1134,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + // represent single field structs as their single field + Univariant { .. } => { + // enums with just one variant are no different, but `.struct_variant()` doesn't work for enums + let variant = &def.variants[0]; + // FIXME: also allow structs with only a single non zst field + if variant.fields.len() == 1 { + return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs)); + } else { + return Err(EvalError::TypeNotPrimitive(ty)); + } + } + _ => return Err(EvalError::TypeNotPrimitive(ty)), } } @@ -1305,8 +1334,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty()); } - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); + if self.ty_to_primval_kind(src_ty).is_ok() { + let sty = self.get_field_ty(src_ty, 0)?; + let dty = self.get_field_ty(dest_ty, 0)?; + return self.unsize_into(src, sty, dest, dty); + } // unsizing of generic struct with pointer fields // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr @@ -1323,6 +1355,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("expected pointer, got {:?}", src), }; + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); diff --git a/src/lvalue.rs b/src/lvalue.rs index d4d292cd4e53e..240b7ced220c8 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -223,10 +223,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) }, Value::ByVal(_) => { - assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); return Ok(base); }, Value::ByValPair(_, _) => { + let field_count = self.get_field_count(base_ty)?; + if field_count == 1 { + assert_eq!(field_index, 0, "{:?} has only one field", base_ty); + return Ok(base); + } + assert_eq!(field_count, 2); assert!(field_index < 2); return Ok(Lvalue::Local { frame, diff --git a/src/memory.rs b/src/memory.rs index 459a9bed4128f..86ca0083f27cd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -673,13 +673,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as being the entry point to a static (see `static_alloc` field) pub fn mark_static(&mut self, alloc_id: AllocId) { + trace!("mark_static: {:?}", alloc_id); if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); } } + /// mark an allocation pointed to by a static as static and initialized + pub fn mark_inner_allocation(&mut self, alloc: AllocId, mutable: bool) -> EvalResult<'tcx> { + // relocations into other statics are not "inner allocations" + if !self.static_alloc.contains(&alloc) { + self.mark_static_initalized(alloc, mutable)?; + } + Ok(()) + } + /// mark an allocation as static and initialized, either mutable or not pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { + trace!("mark_static_initialized {:?}, mutable: {:?}", alloc_id, mutable); // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { @@ -699,10 +710,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - // relocations into other statics are not "inner allocations" - if !self.static_alloc.contains(&alloc) { - self.mark_static_initalized(alloc, mutable)?; - } + self.mark_inner_allocation(alloc, mutable)?; } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 183a3f54fb639..c258d56ded421 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -490,7 +490,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((undef, field_ty)); } }, - _ => bug!("rust-call ABI tuple argument was {:?}", last), + _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), } } ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), From 1a697f9bba595020e129bbe2f316df5dea02cdf0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 09:35:43 +0100 Subject: [PATCH 0851/1096] move all code accessing vtable internals into the `trait` module fixes #124 --- src/terminator/drop.rs | 8 +------- src/terminator/intrinsic.rs | 5 +---- src/traits.rs | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index dd199a4266c14..f334e9e7685de 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -178,14 +178,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), _ => bug!("expected an lvalue with a vtable"), }; - let drop_fn = self.memory.read_ptr(vtable)?; - // some values don't need to call a drop impl, so the value is null - if drop_fn != Pointer::from_int(0) { - let real_ty = self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty()?; + if let Some(real_ty) = self.read_drop_type_from_vtable(vtable)? { self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - } else { - // just a sanity check - assert_eq!(drop_fn.offset, 0); } } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 0443206e80dba..ab8539be3154b 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -418,7 +418,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: ty::Ty<'tcx>, value: Value, ) -> EvalResult<'tcx, (u64, u64)> { - let pointer_size = self.memory.pointer_size(); if let Some(size) = self.type_size(ty)? { Ok((size as u64, self.type_align(ty)? as u64)) } else { @@ -481,9 +480,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyDynamic(..) => { let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. - let size = self.memory.read_usize(vtable.offset(pointer_size))?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?; - Ok((size, align)) + self.read_size_and_align_from_vtable(vtable) } ty::TySlice(_) | ty::TyStr => { diff --git a/src/traits.rs b/src/traits.rs index a46d6096dfc95..733095322d002 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -285,6 +285,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } + pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { + let drop_fn = self.memory.read_ptr(vtable)?; + + // just a sanity check + assert_eq!(drop_fn.offset, 0); + + // some values don't need to call a drop impl, so the value is null + if drop_fn == Pointer::from_int(0) { + Ok(None) + } else { + self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty().map(Some) + } + } + + pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { + let pointer_size = self.memory.pointer_size(); + let size = self.memory.read_usize(vtable.offset(pointer_size))?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?; + Ok((size, align)) + } + fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec>> { debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); From de42764b5231bf2a8d9ebaaa5572cc1e46dce7f8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 10:56:02 +0100 Subject: [PATCH 0852/1096] drop zst fields of null pointer optimized structs and enums fixes #25 --- src/terminator/drop.rs | 28 ++++------------------------ tests/run-pass/zst_variant_drop.rs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 tests/run-pass/zst_variant_drop.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index f334e9e7685de..289bae89c3c64 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -132,31 +132,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { None => return Err(EvalError::InvalidDiscriminant), } }, - Layout::StructWrappedNullablePointer { nndiscr, .. } => { + Layout::StructWrappedNullablePointer { .. } | + Layout::RawNullablePointer { .. } => { let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr as u128 { - assert_eq!(discr as usize as u128, discr); - &adt_def.variants[discr as usize].fields - } else { - // FIXME: the zst variant might contain zst types that impl Drop - return Ok(()); // nothing to do, this is zero sized (e.g. `None`) - } - }, - Layout::RawNullablePointer { nndiscr, .. } => { - let discr = self.read_discriminant_value(adt_ptr, ty)?; - if discr == nndiscr as u128 { - assert_eq!(discr as usize as u128, discr); - assert_eq!(adt_def.variants[discr as usize].fields.len(), 1); - let field_ty = &adt_def.variants[discr as usize].fields[0]; - let field_ty = monomorphize_field_ty(self.tcx, field_ty, substs); - // FIXME: once read_discriminant_value works with lvalue, don't force - // alloc in the RawNullablePointer case - self.drop(lval, field_ty, drop)?; - return Ok(()); - } else { - // FIXME: the zst variant might contain zst types that impl Drop - return Ok(()); // nothing to do, this is zero sized (e.g. `None`) - } + assert_eq!(discr as usize as u128, discr); + &adt_def.variants[discr as usize].fields }, Layout::CEnum { .. } => return Ok(()), _ => bug!("{:?} is not an adt layout", layout), diff --git a/tests/run-pass/zst_variant_drop.rs b/tests/run-pass/zst_variant_drop.rs new file mode 100644 index 0000000000000..a76f64ce29df7 --- /dev/null +++ b/tests/run-pass/zst_variant_drop.rs @@ -0,0 +1,23 @@ +struct Foo; +impl Drop for Foo { + fn drop(&mut self) { + unsafe { + FOO = true; + } + } +} + +static mut FOO: bool = false; + +enum Bar { + A(Box), + B(Foo), +} + +fn main() { + assert!(unsafe { !FOO }); + drop(Bar::A(Box::new(42))); + assert!(unsafe { !FOO }); + drop(Bar::B(Foo)); + assert!(unsafe { FOO }); +} From e2c5a6e64edf48a30fd7d593e90b1a97fe6a5aa7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 11:00:29 +0100 Subject: [PATCH 0853/1096] don't allocate for primvals --- src/lvalue.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 240b7ced220c8..2ca1f399af97a 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -219,8 +219,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (ptr, LvalueExtra::None) }, Value::ByVal(PrimVal::Undef) => { - // FIXME: add some logic for when to not allocate - (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + // FIXME: allocate in fewer cases + if self.ty_to_primval_kind(base_ty).is_ok() { + return Ok(base); + } else { + (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + } }, Value::ByVal(_) => { assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); From 8878a4030acf5285a48e7e248e5f96e4d01060ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 28 Feb 2017 12:35:00 +0100 Subject: [PATCH 0854/1096] rustup to rustc 1.17.0-nightly (60a0edc6c 2017-02-26) --- src/eval_context.rs | 17 +- src/memory.rs | 29 ++- src/terminator/mod.rs | 230 +++++++++++------- src/traits.rs | 23 +- .../run-pass/non_capture_closure_to_fn_ptr.rs | 16 ++ tests/run-pass/recursive_static.rs | 2 - 6 files changed, 215 insertions(+), 102 deletions(-) create mode 100644 tests/run-pass/non_capture_closure_to_fn_ptr.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ab6e85e389d0..1a1102c13a1c0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -649,16 +649,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, UnsafeFnPointer => match dest_ty.sty { - ty::TyFnPtr(unsafe_fn_ty) => { + ty::TyFnPtr(_) => { let src = self.eval_operand(operand)?; - let ptr = src.read_ptr(&self.memory)?; - let fn_def = self.memory.get_fn(ptr.alloc_id)?.expect_concrete()?; - let unsafe_fn_ty = self.tcx.erase_regions(&unsafe_fn_ty); - let fn_ptr = self.memory.create_fn_ptr(self.tcx, fn_def.def_id, fn_def.substs, unsafe_fn_ty); - self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; + self.write_value(src, dest, dest_ty)?; }, ref other => bug!("fn to unsafe fn cast on {:?}", other), }, + + ClosureFnPointer => match self.operand_ty(operand).sty { + ty::TyClosure(def_id, substs) => { + let fn_ty = self.tcx.closure_type(def_id, substs); + let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(self.tcx, def_id, substs, fn_ty); + self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; + }, + ref other => bug!("reify fn pointer on {:?}", other), + }, } } diff --git a/src/memory.rs b/src/memory.rs index 459a9bed4128f..fd035b7fcba69 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -130,15 +130,11 @@ pub enum Function<'tcx> { FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>), /// Glue for Closures Closure(FunctionDefinition<'tcx>), + /// Glue for noncapturing closures casted to function pointers + NonCaptureClosureAsFnPtr(FunctionDefinition<'tcx>), } impl<'tcx> Function<'tcx> { - pub fn expect_concrete(self) -> EvalResult<'tcx, FunctionDefinition<'tcx>> { - match self { - Function::Concrete(fn_def) => Ok(fn_def), - other => Err(EvalError::ExpectedConcreteFunction(other)), - } - } pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> { match self { Function::DropGlue(real_ty) => Ok(real_ty), @@ -238,6 +234,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { })) } + pub fn create_fn_ptr_from_noncapture_closure(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { + // FIXME: this is a hack + let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: fn_ty.unsafety, + abi: fn_ty.abi, + sig: fn_ty.sig, + }); + self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition { + def_id, + substs: substs.substs, + abi: Abi::Rust, // adjust abi + // FIXME: why doesn't this compile? + //sig: tcx.erase_late_bound_regions(&fn_ty.sig), + sig: fn_ty.sig.skip_binder(), + })) + } + pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition { def_id, @@ -535,6 +548,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { trace!("{} closure glue for {}", msg, dump_fn_def(fn_def)); continue; }, + (None, Some(&Function::NonCaptureClosureAsFnPtr(fn_def))) => { + trace!("{} non-capture closure as fn ptr glue for {}", msg, dump_fn_def(fn_def)); + continue; + }, (None, None) => { trace!("{} (deallocated)", msg); continue; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 183a3f54fb639..0befb5ba9fc1c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,14 +2,14 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, BareFnTy}; +use rustc::ty::{self, Ty}; use syntax::codemap::Span; use syntax::attr; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, FunctionDefinition}; +use memory::{Pointer, FunctionDefinition, Function}; use value::PrimVal; use value::Value; @@ -61,35 +61,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let func_ty = self.operand_ty(func); - match func_ty.sty { + let fn_def = match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let FunctionDefinition {def_id, substs, abi, sig} = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; + let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); let bare_sig = self.tcx.erase_regions(&bare_sig); - // transmuting function pointers in miri is fine as long as the number of - // arguments and the abi don't change. - // FIXME: also check the size of the arguments' type and the return type - // Didn't get it to work, since that triggers an assertion in rustc which - // checks whether the type has escaping regions - if abi != bare_fn_ty.abi || - sig.variadic != bare_sig.variadic || - sig.inputs().len() != bare_sig.inputs().len() { - return Err(EvalError::FunctionPointerTyMismatch(abi, sig, bare_fn_ty)); + match fn_def { + Function::Concrete(fn_def) => { + // transmuting function pointers in miri is fine as long as the number of + // arguments and the abi don't change. + // FIXME: also check the size of the arguments' type and the return type + // Didn't get it to work, since that triggers an assertion in rustc which + // checks whether the type has escaping regions + if fn_def.abi != bare_fn_ty.abi || + fn_def.sig.variadic != bare_sig.variadic || + fn_def.sig.inputs().len() != bare_sig.inputs().len() { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + } + }, + Function::NonCaptureClosureAsFnPtr(fn_def) => { + if fn_def.abi != bare_fn_ty.abi || + fn_def.sig.variadic != bare_sig.variadic || + fn_def.sig.inputs().len() != 1 { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + } + if let ty::TyTuple(fields, _) = fn_def.sig.inputs()[0].sty { + if fields.len() != bare_sig.inputs().len() { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + } + } + }, + other => return Err(EvalError::ExpectedConcreteFunction(other)), } - self.eval_fn_call(def_id, substs, bare_fn_ty, destination, args, - terminator.source_info.span)? + self.memory.get_fn(fn_ptr.alloc_id)? }, - ty::TyFnDef(def_id, substs, fn_ty) => { - self.eval_fn_call(def_id, substs, fn_ty, destination, args, - terminator.source_info.span)? - } + ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition { + def_id, + substs, + abi: fn_ty.abi, + sig: fn_ty.sig.skip_binder(), + }), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); } - } + }; + self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?; } Drop { ref location, target, .. } => { @@ -138,17 +157,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_fn_call( &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - fn_ty: &'tcx BareFnTy, + fn_def: Function<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx> { use syntax::abi::Abi; - match fn_ty.abi { - Abi::RustIntrinsic => { - let ty = fn_ty.sig.0.output(); + match fn_def { + // Intrinsics can only be addressed directly + Function::Concrete(FunctionDefinition { def_id, substs, abi: Abi::RustIntrinsic, sig }) => { + let ty = sig.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { Some(dest) if is_inhabited(self.tcx, ty) => dest, @@ -157,18 +175,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); Ok(()) - } - - Abi::C => { - let ty = fn_ty.sig.0.output(); + }, + // C functions can only be addressed directly + Function::Concrete(FunctionDefinition { def_id, abi: Abi::C, sig, ..}) => { + let ty = sig.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.dump_local(ret); self.goto_block(target); Ok(()) - } - - Abi::Rust | Abi::RustCall => { + }, + Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), + Function::Concrete(FunctionDefinition { def_id, abi: Abi::RustCall, sig, substs }) | + Function::Concrete(FunctionDefinition { def_id, abi: Abi::Rust, sig, substs }) => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; @@ -185,7 +204,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = fn_ty.sig.skip_binder().output().ty_adt_def() { + if let Some(adt_def) = sig.output().ty_adt_def() { if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); @@ -240,66 +259,105 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); } } - - let mir = match self.load_mir(resolved_def_id) { - Ok(mir) => mir, - Err(EvalError::NoMirFor(path)) => { - match &path[..] { - // let's just ignore all output for now - "std::io::_print" => { - self.goto_block(destination.unwrap().1); - return Ok(()); - }, - "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), - "std::panicking::rust_panic_with_hook" | - "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), - "std::panicking::panicking" | - "std::rt::panicking" => { - let (lval, block) = destination.expect("std::rt::panicking does not diverge"); - // we abort on panic -> `std::rt::panicking` always returns false - let bool = self.tcx.types.bool; - self.write_primval(lval, PrimVal::from_bool(false), bool)?; - self.goto_block(block); - return Ok(()); - } - _ => {}, - } - return Err(EvalError::NoMirFor(path)); - }, - Err(other) => return Err(other), - }; - let (return_lvalue, return_to_block) = match destination { - Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), - None => { - // FIXME(solson) - let lvalue = Lvalue::from_ptr(Pointer::never_ptr()); - (lvalue, StackPopCleanup::None) - } - }; - - self.push_stack_frame( + self.eval_fn_call_inner( resolved_def_id, - span, - mir, resolved_substs, - return_lvalue, - return_to_block, + destination, + args, temporaries, - )?; - - let arg_locals = self.frame().mir.args_iter(); - assert_eq!(self.frame().mir.arg_count, args.len()); - for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg_val, dest, arg_ty)?; + span, + ) + }, + Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, abi: Abi::Rust, substs, sig }) => { + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); } + args.insert(0, ( + Value::ByVal(PrimVal::Undef), + sig.inputs()[0], + )); + self.eval_fn_call_inner( + def_id, + substs, + destination, + args, + Vec::new(), + span, + ) + } + other => Err(EvalError::Unimplemented(format!("can't call function kind {:?}", other))), + } + } - Ok(()) + fn eval_fn_call_inner( + &mut self, + resolved_def_id: DefId, + resolved_substs: &'tcx Substs, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + args: Vec<(Value, Ty<'tcx>)>, + temporaries: Vec<(Pointer, Ty<'tcx>)>, + span: Span, + ) -> EvalResult<'tcx> { + trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", args, temporaries, destination); + + let mir = match self.load_mir(resolved_def_id) { + Ok(mir) => mir, + Err(EvalError::NoMirFor(path)) => { + match &path[..] { + // let's just ignore all output for now + "std::io::_print" => { + self.goto_block(destination.unwrap().1); + return Ok(()); + }, + "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + "std::panicking::panicking" | + "std::rt::panicking" => { + let (lval, block) = destination.expect("std::rt::panicking does not diverge"); + // we abort on panic -> `std::rt::panicking` always returns false + let bool = self.tcx.types.bool; + self.write_primval(lval, PrimVal::from_bool(false), bool)?; + self.goto_block(block); + return Ok(()); + } + _ => {}, + } + return Err(EvalError::NoMirFor(path)); + }, + Err(other) => return Err(other), + }; + let (return_lvalue, return_to_block) = match destination { + Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), + None => { + // FIXME(solson) + let lvalue = Lvalue::from_ptr(Pointer::never_ptr()); + (lvalue, StackPopCleanup::None) } + }; - abi => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + self.push_stack_frame( + resolved_def_id, + span, + mir, + resolved_substs, + return_lvalue, + return_to_block, + temporaries, + )?; + + let arg_locals = self.frame().mir.args_iter(); + assert_eq!(self.frame().mir.arg_count, args.len()); + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; } + + Ok(()) } pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { diff --git a/src/traits.rs b/src/traits.rs index 733095322d002..36293dde5322f 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -130,6 +130,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ); Ok((fn_def.def_id, fn_def.substs, Vec::new())) }, + Function::NonCaptureClosureAsFnPtr(fn_def) => { + args.insert(0, ( + Value::ByVal(PrimVal::Undef), + fn_def.sig.inputs()[0], + )); + Ok((fn_def.def_id, fn_def.substs, Vec::new())) + } Function::Closure(fn_def) => { self.unpack_fn_args(args)?; Ok((fn_def.def_id, fn_def.substs, Vec::new())) @@ -140,8 +147,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.remove(0); self.unpack_fn_args(args)?; let fn_ptr = self.memory.read_ptr(self_ptr)?; - let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?.expect_concrete()?; - assert_eq!(sig, fn_def.sig); + let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { + Function::Concrete(fn_def) => { + assert_eq!(sig, fn_def.sig); + fn_def + }, + Function::NonCaptureClosureAsFnPtr(fn_def) => { + args.insert(0, ( + Value::ByVal(PrimVal::Undef), + fn_def.sig.inputs()[0], + )); + fn_def + }, + other => bug!("FnPtrAsTraitObject for {:?}", other), + }; Ok((fn_def.def_id, fn_def.substs, Vec::new())) } } diff --git a/tests/run-pass/non_capture_closure_to_fn_ptr.rs b/tests/run-pass/non_capture_closure_to_fn_ptr.rs new file mode 100644 index 0000000000000..6f73a3d09dda7 --- /dev/null +++ b/tests/run-pass/non_capture_closure_to_fn_ptr.rs @@ -0,0 +1,16 @@ +#![feature(closure_to_fn_coercion)] + +// allow(const_err) to work around a bug in warnings +#[allow(const_err)] +static FOO: fn() = || { assert_ne!(42, 43) }; +#[allow(const_err)] +static BAR: fn(i32, i32) = |a, b| { assert_ne!(a, b) }; + +fn main() { + FOO(); + BAR(44, 45); + let bar: unsafe fn(i32, i32) = BAR; + unsafe { bar(46, 47) }; + let boo: &Fn(i32, i32) = &BAR; + boo(48, 49); +} diff --git a/tests/run-pass/recursive_static.rs b/tests/run-pass/recursive_static.rs index 5b27324964b47..77f2902917a1c 100644 --- a/tests/run-pass/recursive_static.rs +++ b/tests/run-pass/recursive_static.rs @@ -1,5 +1,3 @@ -#![feature(static_recursion)] - struct S(&'static S); static S1: S = S(&S2); static S2: S = S(&S1); From 8405770b5196914dd397877c0447756a6b41fb2b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 2 Mar 2017 13:11:33 +0100 Subject: [PATCH 0855/1096] Rustup to rustc 1.17.0-nightly (be760566c 2017-02-28) --- src/error.rs | 9 +++-- src/eval_context.rs | 25 +++++++------ src/memory.rs | 60 ++++++++----------------------- src/terminator/drop.rs | 4 +-- src/terminator/mod.rs | 55 ++++++++++++++-------------- src/traits.rs | 20 +++++------ tests/compile-fail/cast_fn_ptr.rs | 2 +- 7 files changed, 71 insertions(+), 104 deletions(-) diff --git a/src/error.rs b/src/error.rs index 72eedba5e3e18..34b672dda91e1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,14 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::{BareFnTy, Ty, FnSig, layout}; -use syntax::abi::Abi; +use rustc::ty::{PolyFnSig, Ty, layout}; use memory::{Pointer, Function}; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { - FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>), + FunctionPointerTyMismatch(PolyFnSig<'tcx>, PolyFnSig<'tcx>), NoMirFor(String), UnterminatedCString(Pointer), DanglingPointerDeref, @@ -151,8 +150,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) }, EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), - EvalError::FunctionPointerTyMismatch(abi, sig, got) => - write!(f, "tried to call a function with abi {:?} and sig {:?} through a function pointer of type {:?}", abi, sig, got), + EvalError::FunctionPointerTyMismatch(sig, got) => + write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig.skip_binder(), got.skip_binder()), EvalError::ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => diff --git a/src/eval_context.rs b/src/eval_context.rs index 1a1102c13a1c0..9b23b849fc9b7 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -181,8 +181,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Float(ConstFloat::F32(f)) => PrimVal::from_f32(f), Float(ConstFloat::F64(f)) => PrimVal::from_f64(f), - Float(ConstFloat::FInfer { .. }) => - bug!("uninferred constants only exist before typeck"), Bool(b) => PrimVal::from_bool(b), Char(c) => PrimVal::from_char(c), @@ -196,7 +194,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), - Function(_) => unimplemented!(), + Function(_, _) => unimplemented!(), Array(_) => unimplemented!(), Repeat(_, _) => unimplemented!(), }; @@ -457,7 +455,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { General { discr, ref variants, .. } => { if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let discr_val = adt_def.variants[variant].disr_val; + let discr_val = adt_def.discriminants(self.tcx) + .nth(variant) + .expect("broken mir: Adt variant id invalid") + .to_u128_unchecked(); let discr_size = discr.size().bytes(); if variants[variant].packed { let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; @@ -530,7 +531,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { .. } => { assert_eq!(operands.len(), 0); if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { - let n = adt_def.variants[variant].disr_val; + let n = adt_def.discriminants(self.tcx) + .nth(variant) + .expect("broken mir: Adt variant index invalid") + .to_u128_unchecked(); self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?; } else { bug!("tried to assign {:?} to Layout::CEnum", kind); @@ -640,9 +644,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ReifyFnPointer => match self.operand_ty(operand).sty { - ty::TyFnDef(def_id, substs, fn_ty) => { - let fn_ty = self.tcx.erase_regions(&fn_ty); - let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty); + ty::TyFnDef(def_id, substs, sig) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs, sig); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -658,8 +661,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ClosureFnPointer => match self.operand_ty(operand).sty { ty::TyClosure(def_id, substs) => { - let fn_ty = self.tcx.closure_type(def_id, substs); - let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(self.tcx, def_id, substs, fn_ty); + let fn_ty = self.tcx.closure_type(def_id); + let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(def_id, substs, fn_ty); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -673,7 +676,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.force_allocation(lval)?.to_ptr(); let discr_val = self.read_discriminant_value(ptr, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { - if adt_def.variants.iter().all(|v| discr_val != v.disr_val) { + if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) { return Err(EvalError::InvalidDiscriminant); } } else { diff --git a/src/memory.rs b/src/memory.rs index fd035b7fcba69..44959ea672860 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,12 +3,10 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet use std::{fmt, iter, ptr, mem, io}; use rustc::hir::def_id::DefId; -use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt}; +use rustc::ty::{self, PolyFnSig, ClosureSubsts}; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; -use syntax::abi::Abi; - use error::{EvalError, EvalResult}; use value::PrimVal; @@ -109,8 +107,7 @@ impl Pointer { pub struct FunctionDefinition<'tcx> { pub def_id: DefId, pub substs: &'tcx Substs<'tcx>, - pub abi: Abi, - pub sig: &'tcx ty::FnSig<'tcx>, + pub sig: PolyFnSig<'tcx>, } /// Either a concrete function, or a glue function @@ -127,7 +124,7 @@ pub enum Function<'tcx> { DropGlue(ty::Ty<'tcx>), /// Glue required to treat the ptr part of a fat pointer /// as a function pointer - FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>), + FnPtrAsTraitObject(PolyFnSig<'tcx>), /// Glue for Closures Closure(FunctionDefinition<'tcx>), /// Glue for noncapturing closures casted to function pointers @@ -217,67 +214,43 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_closure_ptr(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { - // FIXME: this is a hack - let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { - unsafety: fn_ty.unsafety, - abi: fn_ty.abi, - sig: fn_ty.sig, - }); + pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { self.create_fn_alloc(Function::Closure(FunctionDefinition { def_id, substs: substs.substs, - abi: fn_ty.abi, - // FIXME: why doesn't this compile? - //sig: tcx.erase_late_bound_regions(&fn_ty.sig), - sig: fn_ty.sig.skip_binder(), + sig, })) } - pub fn create_fn_ptr_from_noncapture_closure(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer { - // FIXME: this is a hack - let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { - unsafety: fn_ty.unsafety, - abi: fn_ty.abi, - sig: fn_ty.sig, - }); + pub fn create_fn_ptr_from_noncapture_closure(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs: substs.substs, - abi: Abi::Rust, // adjust abi - // FIXME: why doesn't this compile? - //sig: tcx.erase_late_bound_regions(&fn_ty.sig), - sig: fn_ty.sig.skip_binder(), + sig, })) } - pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + pub fn create_fn_as_trait_glue(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition { def_id, substs, - abi: fn_ty.abi, - // FIXME: why doesn't this compile? - //sig: tcx.erase_late_bound_regions(&fn_ty.sig), - sig: fn_ty.sig.skip_binder(), + sig, })) } - pub fn create_fn_ptr_as_trait_glue(&mut self, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { - self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder())) + pub fn create_fn_ptr_as_trait_glue(&mut self, sig: PolyFnSig<'tcx>) -> Pointer { + self.create_fn_alloc(Function::FnPtrAsTraitObject(sig)) } pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer { self.create_fn_alloc(Function::DropGlue(ty)) } - pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer { + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { self.create_fn_alloc(Function::Concrete(FunctionDefinition { def_id, substs, - abi: fn_ty.abi, - // FIXME: why doesn't this compile? - //sig: tcx.erase_late_bound_regions(&fn_ty.sig), - sig: fn_ty.sig.skip_binder(), + sig, })) } @@ -623,12 +596,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String { let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); - let abi = if fn_def.abi == Abi::Rust { - format!("") - } else { - format!("extern {} ", fn_def.abi) - }; - format!("function pointer: {}: {}{}", name, abi, fn_def.sig) + format!("function pointer: {}: {}", name, fn_def.sig.skip_binder()) } /// Byte accessors diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 289bae89c3c64..e8bd2164b84a2 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (adt_ptr, extra) = lval.to_ptr_and_extra(); // run drop impl before the fields' drop impls - if let Some(drop_def_id) = adt_def.destructor() { + if let Some(drop_def_id) = adt_def.destructor(self.tcx) { let trait_ref = ty::Binder(ty::TraitRef { def_id: self.tcx.lang_items.drop_trait().unwrap(), substs: self.tcx.mk_substs_trait(ty, &[]), @@ -121,7 +121,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::General { .. } => { let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; let ptr = self.force_allocation(lval)?.to_ptr(); - match adt_def.variants.iter().position(|v| discr_val == v.disr_val) { + match adt_def.discriminants(self.tcx).position(|v| discr_val == v.to_u128_unchecked()) { Some(i) => { lval = Lvalue::Ptr { ptr, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 0befb5ba9fc1c..14392eda32623 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -3,8 +3,10 @@ use rustc::mir; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; +use rustc_const_math::ConstInt; use syntax::codemap::Span; use syntax::attr; +use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; @@ -62,11 +64,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); let fn_def = match func_ty.sty { - ty::TyFnPtr(bare_fn_ty) => { + ty::TyFnPtr(bare_sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; - let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig); - let bare_sig = self.tcx.erase_regions(&bare_sig); match fn_def { Function::Concrete(fn_def) => { // transmuting function pointers in miri is fine as long as the number of @@ -74,21 +74,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: also check the size of the arguments' type and the return type // Didn't get it to work, since that triggers an assertion in rustc which // checks whether the type has escaping regions - if fn_def.abi != bare_fn_ty.abi || - fn_def.sig.variadic != bare_sig.variadic || - fn_def.sig.inputs().len() != bare_sig.inputs().len() { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + if fn_def.sig.abi() != bare_sig.abi() || + fn_def.sig.variadic() != bare_sig.variadic() || + fn_def.sig.inputs().skip_binder().len() != bare_sig.inputs().skip_binder().len() { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); } }, Function::NonCaptureClosureAsFnPtr(fn_def) => { - if fn_def.abi != bare_fn_ty.abi || - fn_def.sig.variadic != bare_sig.variadic || - fn_def.sig.inputs().len() != 1 { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + assert_eq!(fn_def.sig.abi(), Abi::RustCall); + if fn_def.sig.variadic() != bare_sig.variadic() || + fn_def.sig.inputs().skip_binder().len() != 1 { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); } - if let ty::TyTuple(fields, _) = fn_def.sig.inputs()[0].sty { - if fields.len() != bare_sig.inputs().len() { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty)); + if let ty::TyTuple(fields, _) = fn_def.sig.inputs().skip_binder()[0].sty { + if fields.len() != bare_sig.inputs().skip_binder().len() { + return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); } } }, @@ -99,8 +99,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition { def_id, substs, - abi: fn_ty.abi, - sig: fn_ty.sig.skip_binder(), + sig: fn_ty, }), _ => { @@ -165,8 +164,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use syntax::abi::Abi; match fn_def { // Intrinsics can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, substs, abi: Abi::RustIntrinsic, sig }) => { - let ty = sig.output(); + Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { + let ty = *sig.output().skip_binder(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { Some(dest) if is_inhabited(self.tcx, ty) => dest, @@ -177,8 +176,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) }, // C functions can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, abi: Abi::C, sig, ..}) => { - let ty = sig.output(); + Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => { + let ty = *sig.output().skip_binder(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.dump_local(ret); @@ -186,8 +185,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) }, Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(FunctionDefinition { def_id, abi: Abi::RustCall, sig, substs }) | - Function::Concrete(FunctionDefinition { def_id, abi: Abi::Rust, sig, substs }) => { + Function::Concrete(FunctionDefinition { def_id, sig, substs }) if sig.abi() == Abi::Rust || sig.abi() == Abi::RustCall => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; @@ -204,20 +202,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = sig.output().ty_adt_def() { - if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) { + if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { + let dids = adt_def.variants.iter().map(|v| v.did); + let discrs = adt_def.discriminants(self.tcx).map(ConstInt::to_u128_unchecked); + if let Some((_, disr_val)) = dids.zip(discrs).find(|&(did, _)| resolved_def_id == did) { let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); let dest_ty = self.tcx.item_type(adt_def.did); let dest_layout = self.type_layout(dest_ty)?; trace!("layout({:?}) = {:#?}", dest_ty, dest_layout); match *dest_layout { Layout::Univariant { .. } => { - let disr_val = v.disr_val; assert_eq!(disr_val, 0); self.assign_fields(lvalue, dest_ty, args)?; }, Layout::General { discr, ref variants, .. } => { - let disr_val = v.disr_val; let discr_size = discr.size().bytes(); self.assign_discr_and_fields( lvalue, @@ -230,7 +228,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; }, Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let disr_val = v.disr_val; if nndiscr as u128 == disr_val { self.assign_fields(lvalue, dest_ty, args)?; } else { @@ -268,7 +265,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, - Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, abi: Abi::Rust, substs, sig }) => { + Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; @@ -277,7 +274,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } args.insert(0, ( Value::ByVal(PrimVal::Undef), - sig.inputs()[0], + sig.inputs().skip_binder()[0], )); self.eval_fn_call_inner( def_id, diff --git a/src/traits.rs b/src/traits.rs index 36293dde5322f..986efc987de35 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.memory.get_fn(fn_ptr.alloc_id)? { Function::FnDefAsTraitObject(fn_def) => { trace!("sig: {:#?}", fn_def.sig); - assert!(fn_def.abi != abi::Abi::RustCall); + assert!(fn_def.sig.abi() != abi::Abi::RustCall); assert_eq!(args.len(), 2); // a function item turned into a closure trait object // the first arg is just there to give use the vtable @@ -126,14 +126,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("sig: {:#?}", fn_def.sig); args[0] = ( Value::ByVal(PrimVal::Ptr(self_ptr)), - fn_def.sig.inputs()[0], + fn_def.sig.inputs().skip_binder()[0], ); Ok((fn_def.def_id, fn_def.substs, Vec::new())) }, Function::NonCaptureClosureAsFnPtr(fn_def) => { args.insert(0, ( Value::ByVal(PrimVal::Undef), - fn_def.sig.inputs()[0], + fn_def.sig.inputs().skip_binder()[0], )); Ok((fn_def.def_id, fn_def.substs, Vec::new())) } @@ -155,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Function::NonCaptureClosureAsFnPtr(fn_def) => { args.insert(0, ( Value::ByVal(PrimVal::Undef), - fn_def.sig.inputs()[0], + fn_def.sig.inputs().skip_binder()[0], )); fn_def }, @@ -220,7 +220,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("bad function type: {}", fn_ty), }; let fn_ty = self.tcx.erase_regions(&fn_ty); - self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty) + self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) })) .collect::>() .into_iter() @@ -233,15 +233,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .. } ) => { - let closure_type = self.tcx.closure_type(closure_def_id, substs); - vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter() + let closure_type = self.tcx.closure_type(closure_def_id); + vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() } // turn a function definition into a Fn trait object traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { match fn_ty.sty { ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_as_trait_glue(self.tcx, did, substs, bare_fn_ty))].into_iter() + vec![Some(self.memory.create_fn_as_trait_glue(did, substs, bare_fn_ty))].into_iter() }, ty::TyFnPtr(bare_fn_ty) => { vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter() @@ -275,13 +275,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // in case there is no drop function to be called, this still needs to be initialized self.memory.write_usize(vtable, 0)?; if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if let Some(drop_def_id) = adt_def.destructor() { + if let Some(drop_def_id) = adt_def.destructor(self.tcx) { let fn_ty = match self.tcx.item_type(drop_def_id).sty { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; // The real type is taken from the self argument in `fn drop(&mut self)` - let real_ty = match fn_ty.sig.skip_binder().inputs()[0].sty { + let real_ty = match fn_ty.inputs().skip_binder()[0].sty { ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), _ => bug!("first argument of Drop::drop must be &mut T"), }; diff --git a/tests/compile-fail/cast_fn_ptr.rs b/tests/compile-fail/cast_fn_ptr.rs index c8070913f1cb5..7509ae6ed77cb 100644 --- a/tests/compile-fail/cast_fn_ptr.rs +++ b/tests/compile-fail/cast_fn_ptr.rs @@ -5,5 +5,5 @@ fn main() { std::mem::transmute::(f) }; - g(42) //~ ERROR tried to call a function with abi Rust and sig + g(42) //~ ERROR tried to call a function with sig fn() through a function pointer of type fn(i32) } From 41d59b117aa87c3c54c298a0fd49aaf3d00242bc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 7 Mar 2017 12:49:56 +0100 Subject: [PATCH 0856/1096] Rustup to rustc 1.17.0-nightly (b1e31766d 2017-03-03) --- src/terminator/drop.rs | 4 ++-- src/traits.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index e8bd2164b84a2..efa415679328c 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (adt_ptr, extra) = lval.to_ptr_and_extra(); // run drop impl before the fields' drop impls - if let Some(drop_def_id) = adt_def.destructor(self.tcx) { + if let Some(destructor) = adt_def.destructor(self.tcx) { let trait_ref = ty::Binder(ty::TraitRef { def_id: self.tcx.lang_items.drop_trait().unwrap(), substs: self.tcx.mk_substs_trait(ty, &[]), @@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { LvalueExtra::Length(n) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::from_u128(n as u128)), LvalueExtra::Vtable(vtable) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::Ptr(vtable)), }; - drop.push((drop_def_id, val, vtable.substs)); + drop.push((destructor.did, val, vtable.substs)); } let layout = self.type_layout(ty)?; diff --git a/src/traits.rs b/src/traits.rs index 986efc987de35..e4f9e4a24aa58 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -275,8 +275,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // in case there is no drop function to be called, this still needs to be initialized self.memory.write_usize(vtable, 0)?; if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if let Some(drop_def_id) = adt_def.destructor(self.tcx) { - let fn_ty = match self.tcx.item_type(drop_def_id).sty { + if let Some(destructor) = adt_def.destructor(self.tcx) { + let fn_ty = match self.tcx.item_type(destructor.did).sty { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; From 4cb1f639b7efeb30c8ca3e2fa97a85355ecdb7ac Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 13 Mar 2017 11:28:45 +0100 Subject: [PATCH 0857/1096] Rustup to rustc 1.17.0-nightly (824c9ebbd 2017-03-12) --- src/lvalue.rs | 4 ++-- src/step.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index d4d292cd4e53e..8971f050a8998 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -137,9 +137,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, - Static(def_id) => { + Static(ref statik) => { let substs = self.tcx.intern_substs(&[]); - Lvalue::Global(GlobalId { def_id, substs, promoted: None }) + Lvalue::Global(GlobalId { def_id: statik.def_id, substs, promoted: None }) } Projection(ref proj) => return self.eval_lvalue_projection(proj), diff --git a/src/step.rs b/src/step.rs index c08ac9693a4b1..ced709b3a9340 100644 --- a/src/step.rs +++ b/src/step.rs @@ -242,7 +242,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { location: mir::Location ) { self.super_lvalue(lvalue, context, location); - if let mir::Lvalue::Static(def_id) = *lvalue { + if let mir::Lvalue::Static(ref statik) = *lvalue { + let def_id = statik.def_id; let substs = self.ecx.tcx.intern_substs(&[]); let span = self.span; if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) { From 2f3440d213af7d46e11d143fc3811b0b0d770abe Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 19:28:29 -0400 Subject: [PATCH 0858/1096] implement write_bytes intrinsic --- src/terminator/intrinsic.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index ab8539be3154b..9c7489d145c35 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -402,6 +402,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + "write_bytes" => { + let u8 = self.tcx.types.u8; + let ty = substs.type_at(0); + let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); + let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()?; + let mut pattern: u128 = 0; + for ii in 0..size { + pattern |= val_byte << (8 * ii); + } + let val_full = Value::ByVal(PrimVal::from_u128(pattern)); + let mut ptr = arg_vals[0].read_ptr(&self.memory)?; + + let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; + for _ in 0..count { + self.write_value_to_ptr(val_full, ptr, ty)?; + ptr = ptr.offset(size); + } + } + name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), } From aa2f9988a41144bf29d3538239b40bb12571f36c Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 19:40:48 -0400 Subject: [PATCH 0859/1096] add write-bytes test --- tests/run-pass/write-bytes.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/run-pass/write-bytes.rs diff --git a/tests/run-pass/write-bytes.rs b/tests/run-pass/write-bytes.rs new file mode 100644 index 0000000000000..1df23ab1feac4 --- /dev/null +++ b/tests/run-pass/write-bytes.rs @@ -0,0 +1,17 @@ +fn main() { + const LENGTH: usize = 10; + let mut v: [u64; LENGTH] = [0; LENGTH]; + + for idx in 0..LENGTH { + assert_eq!(v[idx], 0); + } + + unsafe { + let p = v.as_mut_ptr(); + ::std::ptr::write_bytes(p, 0xab, LENGTH); + } + + for idx in 0..LENGTH { + assert_eq!(v[idx], 0xabababababababab); + } +} From e79ee140ee0809fc23e4d6f148fac228ca669e98 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 20:32:43 -0400 Subject: [PATCH 0860/1096] write_bytes(): fix handling of types that are larger than u128 --- src/terminator/intrinsic.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 9c7489d145c35..56d097289bcb5 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -406,18 +406,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let u8 = self.tcx.types.u8; let ty = substs.type_at(0); let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()?; - let mut pattern: u128 = 0; - for ii in 0..size { - pattern |= val_byte << (8 * ii); - } - let val_full = Value::ByVal(PrimVal::from_u128(pattern)); let mut ptr = arg_vals[0].read_ptr(&self.memory)?; - let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - for _ in 0..count { - self.write_value_to_ptr(val_full, ptr, ty)?; - ptr = ptr.offset(size); + for _ in 0..(count * size) { + self.write_value_to_ptr(arg_vals[1], ptr, u8)?; + ptr = ptr.offset(1); } } From 4cae50cccbd5b9cf349baaa93178edb8b2ff3b1d Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 20:33:23 -0400 Subject: [PATCH 0861/1096] add write_bytes() test for a larger-than-u128 struct --- tests/run-pass/write-bytes.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/run-pass/write-bytes.rs b/tests/run-pass/write-bytes.rs index 1df23ab1feac4..7c9a38fca696d 100644 --- a/tests/run-pass/write-bytes.rs +++ b/tests/run-pass/write-bytes.rs @@ -1,3 +1,11 @@ +#[repr(C)] +#[derive(Copy, Clone)] +struct Foo { + a: u64, + b: u64, + c: u64, +} + fn main() { const LENGTH: usize = 10; let mut v: [u64; LENGTH] = [0; LENGTH]; @@ -14,4 +22,24 @@ fn main() { for idx in 0..LENGTH { assert_eq!(v[idx], 0xabababababababab); } + + // ----- + + let mut w: [Foo; LENGTH] = [Foo { a: 0, b: 0, c: 0 }; LENGTH]; + for idx in 0..LENGTH { + assert_eq!(w[idx].a, 0); + assert_eq!(w[idx].b, 0); + assert_eq!(w[idx].c, 0); + } + + unsafe { + let p = w.as_mut_ptr(); + ::std::ptr::write_bytes(p, 0xcd, LENGTH); + } + + for idx in 0..LENGTH { + assert_eq!(w[idx].a, 0xcdcdcdcdcdcdcdcd); + assert_eq!(w[idx].b, 0xcdcdcdcdcdcdcdcd); + assert_eq!(w[idx].c, 0xcdcdcdcdcdcdcdcd); + } } From 41c2aa677a0ad0e6ff7e77e738b410d7ed3d2980 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 20:47:08 -0400 Subject: [PATCH 0862/1096] simplify write_bytes() by using memory.write_repeat() --- src/terminator/intrinsic.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 56d097289bcb5..51404e7d13198 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -405,13 +405,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write_bytes" => { let u8 = self.tcx.types.u8; let ty = substs.type_at(0); + let ty_align = self.type_align(ty)?; + let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let mut ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - for _ in 0..(count * size) { - self.write_value_to_ptr(arg_vals[1], ptr, u8)?; - ptr = ptr.offset(1); - } + self.memory.check_align(ptr, size * count, ty_align)?; + self.memory.write_repeat(ptr, val_byte, size * count)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), From f0e2247f4d17aa9ed94dda2e5ca1cc805465645f Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Mon, 13 Mar 2017 21:07:25 -0400 Subject: [PATCH 0863/1096] fix argument order on check_align --- src/terminator/intrinsic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 51404e7d13198..91bd046277957 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -410,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - self.memory.check_align(ptr, size * count, ty_align)?; + self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } From 64d196a9dc14b5cacf7ef12bf8dd691355d0ffc9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 09:23:15 +0100 Subject: [PATCH 0864/1096] Use deterministic keyword renaming (append `_`) --- src/lvalue.rs | 4 ++-- src/step.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 8971f050a8998..afdb5b392d6c2 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -137,9 +137,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, - Static(ref statik) => { + Static(ref static_) => { let substs = self.tcx.intern_substs(&[]); - Lvalue::Global(GlobalId { def_id: statik.def_id, substs, promoted: None }) + Lvalue::Global(GlobalId { def_id: static_.def_id, substs, promoted: None }) } Projection(ref proj) => return self.eval_lvalue_projection(proj), diff --git a/src/step.rs b/src/step.rs index ced709b3a9340..23f0142a7f53f 100644 --- a/src/step.rs +++ b/src/step.rs @@ -242,8 +242,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { location: mir::Location ) { self.super_lvalue(lvalue, context, location); - if let mir::Lvalue::Static(ref statik) = *lvalue { - let def_id = statik.def_id; + if let mir::Lvalue::Static(ref static_) = *lvalue { + let def_id = static_.def_id; let substs = self.ecx.tcx.intern_substs(&[]); let span = self.span; if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) { From da6f1369972ad972c60270e073361f727b68d8a6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 10:49:22 +0100 Subject: [PATCH 0865/1096] I say we take off and nuke the lifetimes from orbit --- src/error.rs | 6 +-- src/terminator/mod.rs | 48 ++++++++++++++--------- src/traits.rs | 24 +++++++++--- tests/run-pass/rfc1623.rs | 81 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 27 deletions(-) create mode 100644 tests/run-pass/rfc1623.rs diff --git a/src/error.rs b/src/error.rs index 34b672dda91e1..370d59e5a3a39 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,14 @@ use std::error::Error; use std::fmt; use rustc::mir; -use rustc::ty::{PolyFnSig, Ty, layout}; +use rustc::ty::{FnSig, Ty, layout}; use memory::{Pointer, Function}; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { - FunctionPointerTyMismatch(PolyFnSig<'tcx>, PolyFnSig<'tcx>), + FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), UnterminatedCString(Pointer), DanglingPointerDeref, @@ -151,7 +151,7 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { }, EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), EvalError::FunctionPointerTyMismatch(sig, got) => - write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig.skip_binder(), got.skip_binder()), + write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig, got), EvalError::ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 14392eda32623..b0e7b76ae106b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,31 +65,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); let fn_def = match func_ty.sty { ty::TyFnPtr(bare_sig) => { + let bare_sig = self.tcx.erase_late_bound_regions(&bare_sig); + let bare_sig = self.tcx.erase_regions(&bare_sig); let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; match fn_def { Function::Concrete(fn_def) => { // transmuting function pointers in miri is fine as long as the number of // arguments and the abi don't change. - // FIXME: also check the size of the arguments' type and the return type - // Didn't get it to work, since that triggers an assertion in rustc which - // checks whether the type has escaping regions - if fn_def.sig.abi() != bare_sig.abi() || - fn_def.sig.variadic() != bare_sig.variadic() || - fn_def.sig.inputs().skip_binder().len() != bare_sig.inputs().skip_binder().len() { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); + let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let sig = self.tcx.erase_regions(&sig); + if sig.abi != bare_sig.abi || + sig.variadic != bare_sig.variadic || + sig.inputs_and_output != bare_sig.inputs_and_output { + return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); } }, Function::NonCaptureClosureAsFnPtr(fn_def) => { - assert_eq!(fn_def.sig.abi(), Abi::RustCall); - if fn_def.sig.variadic() != bare_sig.variadic() || - fn_def.sig.inputs().skip_binder().len() != 1 { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); + let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let sig = self.tcx.erase_regions(&sig); + assert_eq!(sig.abi, Abi::RustCall); + if sig.variadic != bare_sig.variadic || + sig.inputs().len() != 1 { + return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); } - if let ty::TyTuple(fields, _) = fn_def.sig.inputs().skip_binder()[0].sty { - if fields.len() != bare_sig.inputs().skip_binder().len() { - return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig)); + if let ty::TyTuple(fields, _) = sig.inputs()[0].sty { + if **fields != *bare_sig.inputs() { + return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); } + } else { + return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); } }, other => return Err(EvalError::ExpectedConcreteFunction(other)), @@ -165,7 +170,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match fn_def { // Intrinsics can only be addressed directly Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { - let ty = *sig.output().skip_binder(); + let sig = self.tcx.erase_late_bound_regions(&sig); + let sig = self.tcx.erase_regions(&sig); + let ty = sig.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { Some(dest) if is_inhabited(self.tcx, ty) => dest, @@ -177,7 +184,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, // C functions can only be addressed directly Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => { - let ty = *sig.output().skip_binder(); + let sig = self.tcx.erase_late_bound_regions(&sig); + let sig = self.tcx.erase_regions(&sig); + let ty = sig.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; self.dump_local(ret); @@ -266,6 +275,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) }, Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { + let sig = self.tcx.erase_late_bound_regions(&sig); + let sig = self.tcx.erase_regions(&sig); let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; @@ -274,7 +285,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } args.insert(0, ( Value::ByVal(PrimVal::Undef), - sig.inputs().skip_binder()[0], + sig.inputs()[0], )); self.eval_fn_call_inner( def_id, @@ -285,7 +296,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) } - other => Err(EvalError::Unimplemented(format!("can't call function kind {:?}", other))), + Function::Concrete(fn_def) => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", fn_def.sig.abi()))), + other => Err(EvalError::Unimplemented(format!("can't call function kind {:#?}", other))), } } diff --git a/src/traits.rs b/src/traits.rs index e4f9e4a24aa58..19a4f6652d918 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -123,17 +123,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), Function::Concrete(fn_def) => { - trace!("sig: {:#?}", fn_def.sig); + let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let sig = self.tcx.erase_regions(&sig); + trace!("sig: {:#?}", sig); args[0] = ( Value::ByVal(PrimVal::Ptr(self_ptr)), - fn_def.sig.inputs().skip_binder()[0], + sig.inputs()[0], ); Ok((fn_def.def_id, fn_def.substs, Vec::new())) }, Function::NonCaptureClosureAsFnPtr(fn_def) => { + let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let sig = self.tcx.erase_regions(&sig); args.insert(0, ( Value::ByVal(PrimVal::Undef), - fn_def.sig.inputs().skip_binder()[0], + sig.inputs()[0], )); Ok((fn_def.def_id, fn_def.substs, Vec::new())) } @@ -142,6 +146,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((fn_def.def_id, fn_def.substs, Vec::new())) } Function::FnPtrAsTraitObject(sig) => { + let sig = self.tcx.erase_late_bound_regions(&sig); + let sig = self.tcx.erase_regions(&sig); trace!("sig: {:#?}", sig); // the first argument was the fat ptr args.remove(0); @@ -149,13 +155,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.read_ptr(self_ptr)?; let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { Function::Concrete(fn_def) => { - assert_eq!(sig, fn_def.sig); + let fn_def_sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let fn_def_sig = self.tcx.erase_regions(&fn_def_sig); + assert_eq!(sig, fn_def_sig); fn_def }, Function::NonCaptureClosureAsFnPtr(fn_def) => { + let fn_def_sig = self.tcx.erase_late_bound_regions(&fn_def.sig); + let fn_def_sig = self.tcx.erase_regions(&fn_def_sig); args.insert(0, ( Value::ByVal(PrimVal::Undef), - fn_def.sig.inputs().skip_binder()[0], + fn_def_sig.inputs()[0], )); fn_def }, @@ -280,8 +290,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; + let fn_ty = self.tcx.erase_late_bound_regions(&fn_ty); + let fn_ty = self.tcx.erase_regions(&fn_ty); // The real type is taken from the self argument in `fn drop(&mut self)` - let real_ty = match fn_ty.inputs().skip_binder()[0].sty { + let real_ty = match fn_ty.inputs()[0].sty { ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), _ => bug!("first argument of Drop::drop must be &mut T"), }; diff --git a/tests/run-pass/rfc1623.rs b/tests/run-pass/rfc1623.rs new file mode 100644 index 0000000000000..17453933c8abc --- /dev/null +++ b/tests/run-pass/rfc1623.rs @@ -0,0 +1,81 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +// very simple test for a 'static static with default lifetime +static STATIC_STR: &str = "&'static str"; +const CONST_STR: &str = "&'static str"; + +// this should be the same as without default: +static EXPLICIT_STATIC_STR: &'static str = "&'static str"; +const EXPLICIT_CONST_STR: &'static str = "&'static str"; + +// a function that elides to an unbound lifetime for both in- and output +fn id_u8_slice(arg: &[u8]) -> &[u8] { + arg +} + +// one with a function, argument elided +static STATIC_SIMPLE_FN: &fn(&[u8]) -> &[u8] = &(id_u8_slice as fn(&[u8]) -> &[u8]); +const CONST_SIMPLE_FN: &fn(&[u8]) -> &[u8] = &(id_u8_slice as fn(&[u8]) -> &[u8]); + +// this should be the same as without elision +static STATIC_NON_ELIDED_fN: &for<'a> fn(&'a [u8]) -> &'a [u8] = + &(id_u8_slice as for<'a> fn(&'a [u8]) -> &'a [u8]); +const CONST_NON_ELIDED_fN: &for<'a> fn(&'a [u8]) -> &'a [u8] = + &(id_u8_slice as for<'a> fn(&'a [u8]) -> &'a [u8]); + +// another function that elides, each to a different unbound lifetime +fn multi_args(a: &u8, b: &u8, c: &u8) {} + +static STATIC_MULTI_FN: &fn(&u8, &u8, &u8) = &(multi_args as fn(&u8, &u8, &u8)); +const CONST_MULTI_FN: &fn(&u8, &u8, &u8) = &(multi_args as fn(&u8, &u8, &u8)); + +struct Foo<'a> { + bools: &'a [bool], +} + +static STATIC_FOO: Foo = Foo { bools: &[true, false] }; +const CONST_FOO: Foo = Foo { bools: &[true, false] }; + +type Bar<'a> = Foo<'a>; + +static STATIC_BAR: Bar = Bar { bools: &[true, false] }; +const CONST_BAR: Bar = Bar { bools: &[true, false] }; + +type Baz<'a> = fn(&'a [u8]) -> Option; + +fn baz(e: &[u8]) -> Option { + e.first().map(|x| *x) +} + +static STATIC_BAZ: &Baz = &(baz as Baz); +const CONST_BAZ: &Baz = &(baz as Baz); + +static BYTES: &[u8] = &[1, 2, 3]; + +fn main() { + // make sure that the lifetime is actually elided (and not defaulted) + let x = &[1u8, 2, 3]; + STATIC_SIMPLE_FN(x); + CONST_SIMPLE_FN(x); + + STATIC_BAZ(BYTES); // neees static lifetime + CONST_BAZ(BYTES); + + // make sure this works with different lifetimes + let a = &1; + { + let b = &2; + let c = &3; + CONST_MULTI_FN(a, b, c); + } +} From adb3fbb285181bcbf1453efd42a0fc970424ed3e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 11:12:59 +0100 Subject: [PATCH 0866/1096] Add a method that hides the lifetime erasing boilerplate --- src/eval_context.rs | 9 ++++++++- src/terminator/mod.rs | 18 ++++++------------ src/traits.rs | 18 ++++++------------ 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9b23b849fc9b7..549530265ee62 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -9,7 +9,7 @@ use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; @@ -225,6 +225,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.tcx.normalize_associated_type(&substituted) } + pub fn erase_lifetimes(&self, value: &Binder) -> T + where T : TypeFoldable<'tcx> + { + let value = self.tcx.erase_late_bound_regions(value); + self.tcx.erase_regions(&value) + } + pub(super) fn type_size(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { self.type_size_with_substs(ty, self.substs()) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index b0e7b76ae106b..79682723118cb 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,16 +65,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); let fn_def = match func_ty.sty { ty::TyFnPtr(bare_sig) => { - let bare_sig = self.tcx.erase_late_bound_regions(&bare_sig); - let bare_sig = self.tcx.erase_regions(&bare_sig); + let bare_sig = self.erase_lifetimes(&bare_sig); let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; match fn_def { Function::Concrete(fn_def) => { // transmuting function pointers in miri is fine as long as the number of // arguments and the abi don't change. - let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&fn_def.sig); if sig.abi != bare_sig.abi || sig.variadic != bare_sig.variadic || sig.inputs_and_output != bare_sig.inputs_and_output { @@ -82,8 +80,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&fn_def.sig); assert_eq!(sig.abi, Abi::RustCall); if sig.variadic != bare_sig.variadic || sig.inputs().len() != 1 { @@ -170,8 +167,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match fn_def { // Intrinsics can only be addressed directly Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { - let sig = self.tcx.erase_late_bound_regions(&sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { @@ -184,8 +180,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, // C functions can only be addressed directly Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => { - let sig = self.tcx.erase_late_bound_regions(&sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let (ret, target) = destination.unwrap(); self.call_c_abi(def_id, arg_operands, ret, ty)?; @@ -275,8 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) }, Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { - let sig = self.tcx.erase_late_bound_regions(&sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&sig); let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; diff --git a/src/traits.rs b/src/traits.rs index 19a4f6652d918..72de17801d5eb 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -123,8 +123,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), Function::Concrete(fn_def) => { - let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&fn_def.sig); trace!("sig: {:#?}", sig); args[0] = ( Value::ByVal(PrimVal::Ptr(self_ptr)), @@ -133,8 +132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((fn_def.def_id, fn_def.substs, Vec::new())) }, Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&fn_def.sig); args.insert(0, ( Value::ByVal(PrimVal::Undef), sig.inputs()[0], @@ -146,8 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((fn_def.def_id, fn_def.substs, Vec::new())) } Function::FnPtrAsTraitObject(sig) => { - let sig = self.tcx.erase_late_bound_regions(&sig); - let sig = self.tcx.erase_regions(&sig); + let sig = self.erase_lifetimes(&sig); trace!("sig: {:#?}", sig); // the first argument was the fat ptr args.remove(0); @@ -155,14 +152,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.read_ptr(self_ptr)?; let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { Function::Concrete(fn_def) => { - let fn_def_sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let fn_def_sig = self.tcx.erase_regions(&fn_def_sig); + let fn_def_sig = self.erase_lifetimes(&fn_def.sig); assert_eq!(sig, fn_def_sig); fn_def }, Function::NonCaptureClosureAsFnPtr(fn_def) => { - let fn_def_sig = self.tcx.erase_late_bound_regions(&fn_def.sig); - let fn_def_sig = self.tcx.erase_regions(&fn_def_sig); + let fn_def_sig = self.erase_lifetimes(&fn_def.sig); args.insert(0, ( Value::ByVal(PrimVal::Undef), fn_def_sig.inputs()[0], @@ -290,8 +285,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), _ => bug!("drop method is not a TyFnDef"), }; - let fn_ty = self.tcx.erase_late_bound_regions(&fn_ty); - let fn_ty = self.tcx.erase_regions(&fn_ty); + let fn_ty = self.erase_lifetimes(&fn_ty); // The real type is taken from the self argument in `fn drop(&mut self)` let real_ty = match fn_ty.inputs()[0].sty { ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), From 80be25e705b0c9b3e8cb43bc0fb4fae3108172bf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 11:14:53 +0100 Subject: [PATCH 0867/1096] Fix warnings in unit test --- tests/run-pass/rfc1623.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run-pass/rfc1623.rs b/tests/run-pass/rfc1623.rs index 17453933c8abc..0ee523a5be00a 100644 --- a/tests/run-pass/rfc1623.rs +++ b/tests/run-pass/rfc1623.rs @@ -28,13 +28,13 @@ static STATIC_SIMPLE_FN: &fn(&[u8]) -> &[u8] = &(id_u8_slice as fn(&[u8]) -> &[u const CONST_SIMPLE_FN: &fn(&[u8]) -> &[u8] = &(id_u8_slice as fn(&[u8]) -> &[u8]); // this should be the same as without elision -static STATIC_NON_ELIDED_fN: &for<'a> fn(&'a [u8]) -> &'a [u8] = +static STATIC_NON_ELIDED_FN: &for<'a> fn(&'a [u8]) -> &'a [u8] = &(id_u8_slice as for<'a> fn(&'a [u8]) -> &'a [u8]); -const CONST_NON_ELIDED_fN: &for<'a> fn(&'a [u8]) -> &'a [u8] = +const CONST_NON_ELIDED_FN: &for<'a> fn(&'a [u8]) -> &'a [u8] = &(id_u8_slice as for<'a> fn(&'a [u8]) -> &'a [u8]); // another function that elides, each to a different unbound lifetime -fn multi_args(a: &u8, b: &u8, c: &u8) {} +fn multi_args(_a: &u8, _b: &u8, _c: &u8) {} static STATIC_MULTI_FN: &fn(&u8, &u8, &u8) = &(multi_args as fn(&u8, &u8, &u8)); const CONST_MULTI_FN: &fn(&u8, &u8, &u8) = &(multi_args as fn(&u8, &u8, &u8)); From adddde7cba89ff9a80647acd336bbf9325e52b3c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 12:35:38 +0100 Subject: [PATCH 0868/1096] Implement more float intrinsics --- src/terminator/intrinsic.rs | 80 +++++++++++++++++++++++++------ tests/run-pass/intrinsics-math.rs | 67 ++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 tests/run-pass/intrinsics-math.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index ab8539be3154b..2ee4d2440537e 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -207,14 +207,50 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return self.eval_drop_impls(drops, span); } - "fabsf32" => { + "sinf32" | "fabsf32" | "cosf32" | + "sqrtf32" | "expf32" | "exp2f32" | + "logf32" | "log10f32" | "log2f32" | + "floorf32" | "ceilf32" | "truncf32" => { let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; - self.write_primval(dest, PrimVal::from_f32(f.abs()), dest_ty)?; + let f = match intrinsic_name { + "sinf32" => f.sin(), + "fabsf32" => f.abs(), + "cosf32" => f.cos(), + "sqrtf32" => f.sqrt(), + "expf32" => f.exp(), + "exp2f32" => f.exp2(), + "logf32" => f.ln(), + "log10f32" => f.log10(), + "log2f32" => f.log2(), + "floorf32" => f.floor(), + "ceilf32" => f.ceil(), + "truncf32" => f.trunc(), + _ => bug!(), + }; + self.write_primval(dest, PrimVal::from_f32(f), dest_ty)?; } - "fabsf64" => { + "sinf64" | "fabsf64" | "cosf64" | + "sqrtf64" | "expf64" | "exp2f64" | + "logf64" | "log10f64" | "log2f64" | + "floorf64" | "ceilf64" | "truncf64" => { let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; - self.write_primval(dest, PrimVal::from_f64(f.abs()), dest_ty)?; + let f = match intrinsic_name { + "sinf64" => f.sin(), + "fabsf64" => f.abs(), + "cosf64" => f.cos(), + "sqrtf64" => f.sqrt(), + "expf64" => f.exp(), + "exp2f64" => f.exp2(), + "logf64" => f.ln(), + "log10f64" => f.log10(), + "log2f64" => f.log2(), + "floorf64" => f.floor(), + "ceilf64" => f.ceil(), + "truncf64" => f.trunc(), + _ => bug!(), + }; + self.write_primval(dest, PrimVal::from_f64(f), dest_ty)?; } "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { @@ -320,26 +356,42 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.intrinsic_overflowing(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?; } - "powif32" => { + "powf32" => { let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; - let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; - self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?; + let f2 = self.value_to_primval(arg_vals[1], f32)?.to_f32()?; + self.write_primval(dest, PrimVal::from_f32(f.powf(f2)), dest_ty)?; } - "powif64" => { + "powf64" => { let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; - let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; - self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?; + let f2 = self.value_to_primval(arg_vals[1], f64)?.to_f64()?; + self.write_primval(dest, PrimVal::from_f64(f.powf(f2)), dest_ty)?; } - "sqrtf32" => { + "fmaf32" => { + let a = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; + let b = self.value_to_primval(arg_vals[1], f32)?.to_f32()?; + let c = self.value_to_primval(arg_vals[2], f32)?.to_f32()?; + self.write_primval(dest, PrimVal::from_f32(a * b + c), dest_ty)?; + } + + "fmaf64" => { + let a = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; + let b = self.value_to_primval(arg_vals[1], f64)?.to_f64()?; + let c = self.value_to_primval(arg_vals[2], f64)?.to_f64()?; + self.write_primval(dest, PrimVal::from_f64(a * b + c), dest_ty)?; + } + + "powif32" => { let f = self.value_to_primval(arg_vals[0], f32)?.to_f32()?; - self.write_primval(dest, PrimVal::from_f32(f.sqrt()), dest_ty)?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; + self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)), dest_ty)?; } - "sqrtf64" => { + "powif64" => { let f = self.value_to_primval(arg_vals[0], f64)?.to_f64()?; - self.write_primval(dest, PrimVal::from_f64(f.sqrt()), dest_ty)?; + let i = self.value_to_primval(arg_vals[1], i32)?.to_i128()?; + self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)), dest_ty)?; } "size_of" => { diff --git a/tests/run-pass/intrinsics-math.rs b/tests/run-pass/intrinsics-math.rs new file mode 100644 index 0000000000000..a2c55634749cb --- /dev/null +++ b/tests/run-pass/intrinsics-math.rs @@ -0,0 +1,67 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => ({ + let (a, b) = (&$a, &$b); + assert!((*a - *b).abs() < 1.0e-6, + "{} is not approximately equal to {}", *a, *b); + }) +} + +pub fn main() { + use std::f32; + use std::f64; + + assert_approx_eq!(64f32.sqrt(), 8f32); + assert_approx_eq!(64f64.sqrt(), 8f64); + + assert_approx_eq!(25f32.powi(-2), 0.0016f32); + assert_approx_eq!(23.2f64.powi(2), 538.24f64); + + assert_approx_eq!(0f32.sin(), 0f32); + assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64); + + assert_approx_eq!(0f32.cos(), 1f32); + assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64); + + assert_approx_eq!(25f32.powf(-2f32), 0.0016f32); + assert_approx_eq!(400f64.powf(0.5f64), 20f64); + + assert_approx_eq!((1f32.exp() - f32::consts::E).abs(), 0f32); + assert_approx_eq!(1f64.exp(), f64::consts::E); + + assert_approx_eq!(10f32.exp2(), 1024f32); + assert_approx_eq!(50f64.exp2(), 1125899906842624f64); + + assert_approx_eq!((f32::consts::E.ln() - 1f32).abs(), 0f32); + assert_approx_eq!(1f64.ln(), 0f64); + + assert_approx_eq!(10f32.log10(), 1f32); + assert_approx_eq!(f64::consts::E.log10(), f64::consts::LOG10_E); + + assert_approx_eq!(8f32.log2(), 3f32); + assert_approx_eq!(f64::consts::E.log2(), f64::consts::LOG2_E); + + assert_approx_eq!(1.0f32.mul_add(2.0f32, 5.0f32), 7.0f32); + assert_approx_eq!(0.0f64.mul_add(-2.0f64, f64::consts::E), f64::consts::E); + + assert_approx_eq!((-1.0f32).abs(), 1.0f32); + assert_approx_eq!(34.2f64.abs(), 34.2f64); + + assert_approx_eq!(3.8f32.floor(), 3.0f32); + assert_approx_eq!((-1.1f64).floor(), -2.0f64); + + assert_approx_eq!((-2.3f32).ceil(), -2.0f32); + assert_approx_eq!(3.8f64.ceil(), 4.0f64); + + assert_approx_eq!(0.1f32.trunc(), 0.0f32); + assert_approx_eq!((-0.1f64).trunc(), 0.0f64); +} From bb33830c60f9b815bce976567c53a0b7e3f13d9c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 13:05:51 +0100 Subject: [PATCH 0869/1096] Implement more atomic intrinsics --- src/terminator/intrinsic.rs | 39 ++++++++++------------- tests/run-pass/atomic-access-bool.rs | 30 +++++++++++++++++ tests/run-pass/atomic-compare_exchange.rs | 36 +++++++++++++++++++++ 3 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 tests/run-pass/atomic-access-bool.rs create mode 100644 tests/run-pass/atomic-compare_exchange.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 2ee4d2440537e..2776857a88160 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // we are inherently singlethreaded and singlecored, this is a nop } - "atomic_xchg" => { + _ if intrinsic_name.starts_with("atomic_xchg") => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; @@ -92,8 +92,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } - "atomic_cxchg_relaxed" | - "atomic_cxchg" => { + _ if intrinsic_name.starts_with("atomic_cxchg") => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; @@ -111,8 +110,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } - "atomic_xadd" | - "atomic_xadd_relaxed" => { + "atomic_or" | "atomic_or_acq" | "atomic_or_rel" | "atomic_or_acqrel" | "atomic_or_relaxed" | + "atomic_xor" | "atomic_xor_acq" | "atomic_xor_rel" | "atomic_xor_acqrel" | "atomic_xor_relaxed" | + "atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" | + "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | + "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; @@ -124,27 +126,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.write_primval(dest, old, ty)?; let kind = self.ty_to_primval_kind(ty)?; - // FIXME: what do atomics do on overflow? - let (val, _) = operator::binary_op(mir::BinOp::Add, old, kind, change, kind)?; - self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; - }, - - "atomic_xsub_rel" => { - let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; - let change = self.value_to_primval(arg_vals[1], ty)?; - let old = self.read_value(ptr, ty)?; - let old = match old { - Value::ByVal(val) => val, - Value::ByRef(_) => bug!("just read the value, can't be byref"), - Value::ByValPair(..) => bug!("atomic_xsub_rel doesn't work with nonprimitives"), + let op = match intrinsic_name.split('_').nth(1).unwrap() { + "or" => mir::BinOp::BitOr, + "xor" => mir::BinOp::BitXor, + "and" => mir::BinOp::BitAnd, + "xadd" => mir::BinOp::Add, + "xsub" => mir::BinOp::Sub, + _ => bug!(), }; - self.write_primval(dest, old, ty)?; - let kind = self.ty_to_primval_kind(ty)?; // FIXME: what do atomics do on overflow? - let (val, _) = operator::binary_op(mir::BinOp::Sub, old, kind, change, kind)?; + let (val, _) = operator::binary_op(op, old, kind, change, kind)?; self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; - } + }, "breakpoint" => unimplemented!(), // halt miri diff --git a/tests/run-pass/atomic-access-bool.rs b/tests/run-pass/atomic-access-bool.rs new file mode 100644 index 0000000000000..ada584705401f --- /dev/null +++ b/tests/run-pass/atomic-access-bool.rs @@ -0,0 +1,30 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; +use std::sync::atomic::Ordering::*; + +static mut ATOMIC: AtomicBool = ATOMIC_BOOL_INIT; + +fn main() { + unsafe { + assert_eq!(*ATOMIC.get_mut(), false); + ATOMIC.store(true, SeqCst); + assert_eq!(*ATOMIC.get_mut(), true); + ATOMIC.fetch_or(false, SeqCst); + assert_eq!(*ATOMIC.get_mut(), true); + ATOMIC.fetch_and(false, SeqCst); + assert_eq!(*ATOMIC.get_mut(), false); + ATOMIC.fetch_nand(true, SeqCst); + assert_eq!(*ATOMIC.get_mut(), true); + ATOMIC.fetch_xor(true, SeqCst); + assert_eq!(*ATOMIC.get_mut(), false); + } +} diff --git a/tests/run-pass/atomic-compare_exchange.rs b/tests/run-pass/atomic-compare_exchange.rs new file mode 100644 index 0000000000000..61e9a96588966 --- /dev/null +++ b/tests/run-pass/atomic-compare_exchange.rs @@ -0,0 +1,36 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::atomic::{AtomicIsize, ATOMIC_ISIZE_INIT}; +use std::sync::atomic::Ordering::*; + +static ATOMIC: AtomicIsize = ATOMIC_ISIZE_INIT; + +fn main() { + // Make sure trans can emit all the intrinsics correctly + ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Acquire, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, AcqRel, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok(); + ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Acquire, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok(); +} From 257ac5803f0baf0dd0b79b6560391bf588ca17d8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Mar 2017 14:24:16 +0100 Subject: [PATCH 0870/1096] Don't unconditionally mask bitshift rhs --- src/operator.rs | 20 ++------------------ tests/compile-fail/overflowing-rsh-6.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 tests/compile-fail/overflowing-rsh-6.rs diff --git a/src/operator.rs b/src/operator.rs index 2823e3edbea9e..155d5574daa04 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -178,25 +178,9 @@ pub fn binary_op<'tcx>( // These ops can have an RHS with a different numeric type. if bin_op == Shl || bin_op == Shr { - // These are the maximum values a bitshift RHS could possibly have. For example, u16 - // can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in - // that range. - let type_bits: u32 = match left_kind { - I8 | U8 => 8, - I16 | U16 => 16, - I32 | U32 => 32, - I64 | U64 => 64, - I128 | U128 => 128, - _ => bug!("bad MIR: bitshift lhs is not integral"), - }; - - // Cast to `u32` because `overflowing_sh{l,r}` only take `u32`, then apply the bitmask - // to ensure it's within the valid shift value range. - let masked_shift_width = (r as u32) & (type_bits - 1); - return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, masked_shift_width), - Shr => int_shift!(left_kind, overflowing_shr, l, masked_shift_width), + Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), + Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), _ => bug!("it has already been checked that this is a shift op"), }; } diff --git a/tests/compile-fail/overflowing-rsh-6.rs b/tests/compile-fail/overflowing-rsh-6.rs new file mode 100644 index 0000000000000..a7ac9d1d50398 --- /dev/null +++ b/tests/compile-fail/overflowing-rsh-6.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(exceeding_bitshifts)] + +fn main() { + let _n = 1i64 >> 64; //~ Overflow(Shr) +} From c6a18cead8885b229f5c7bc5d11cf6ed42d4dc6c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Mar 2017 09:21:51 +0100 Subject: [PATCH 0871/1096] Rustup to rustc 1.17.0-nightly (134c4a0f0 2017-03-20) --- src/bin/miri.rs | 6 +++--- src/lib.rs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f1a97d0a44b5c..98ad4e9f9b36d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -82,7 +82,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> { fn visit_item(&mut self, i: &'hir hir::Item) { if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { - if i.attrs.iter().any(|attr| attr.value.name == "test") { + if i.attrs.iter().any(|attr| attr.name().map_or(true, |name| name == "test")) { let did = self.1.hir.body_owner_def_id(body_id); println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); miri::eval_main(self.1, did, self.0); @@ -117,8 +117,8 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits } }; - for attr in krate.attrs.iter().filter(|a| a.name() == "miri") { - if let MetaItemKind::List(ref items) = attr.value.node { + for attr in krate.attrs.iter().filter(|a| a.name().map_or(true, |n| n == "miri")) { + if let Some(ref items) = attr.meta_item_list() { for item in items { if let NestedMetaItemKind::MetaItem(ref inner) = item.node { if let MetaItemKind::NameValue(ref value) = inner.node { diff --git a/src/lib.rs b/src/lib.rs index d1d8e8cf229f6..b7303566384b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,4 @@ #![feature( - btree_range, - collections, i128_type, pub_restricted, rustc_private, From dc1b0fb436da6d7e391e14b3a98cd5f96b2726bb Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Mar 2017 13:53:55 +0100 Subject: [PATCH 0872/1096] Compiles again --- src/error.rs | 11 +- src/eval_context.rs | 168 ++++++++++++++--------- src/lvalue.rs | 18 +-- src/memory.rs | 124 ++--------------- src/step.rs | 30 ++--- src/terminator/drop.rs | 224 ------------------------------ src/terminator/intrinsic.rs | 79 ++++------- src/terminator/mod.rs | 219 ++++-------------------------- src/traits.rs | 262 ++++-------------------------------- 9 files changed, 212 insertions(+), 923 deletions(-) delete mode 100644 src/terminator/drop.rs diff --git a/src/error.rs b/src/error.rs index 370d59e5a3a39..fd692ef8b64a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt; use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; -use memory::{Pointer, Function}; +use memory::Pointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -52,9 +52,6 @@ pub enum EvalError<'tcx> { DeallocatedStaticMemory, Layout(layout::LayoutError<'tcx>), Unreachable, - ExpectedConcreteFunction(Function<'tcx>), - ExpectedDropGlue(Function<'tcx>), - ManuallyCalledDropGlue, Panic, } @@ -128,12 +125,6 @@ impl<'tcx> Error for EvalError<'tcx> { "attempted to get length of a null terminated string, but no null found before end of allocation", EvalError::Unreachable => "entered unreachable code", - EvalError::ExpectedConcreteFunction(_) => - "tried to use glue function as function", - EvalError::ExpectedDropGlue(_) => - "tried to use non-drop-glue function as drop glue", - EvalError::ManuallyCalledDropGlue => - "tried to manually invoke drop glue", EvalError::Panic => "the evaluated program panicked", } diff --git a/src/eval_context.rs b/src/eval_context.rs index 011a25774aca6..9b2e5e1ceaba6 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -8,7 +8,7 @@ use rustc::middle::const_val::ConstVal; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; -use rustc::ty::subst::{self, Subst, Substs}; +use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP}; @@ -52,11 +52,8 @@ pub struct Frame<'tcx> { /// The MIR for the function called on this frame. pub mir: MirRef<'tcx>, - /// The def_id of the current function. - pub def_id: DefId, - - /// type substitutions for the current function invocation. - pub substs: &'tcx Substs<'tcx>, + /// The def_id and substs of the current function + pub instance: ty::Instance<'tcx>, /// The span of the call site. pub span: codemap::Span, @@ -78,12 +75,6 @@ pub struct Frame<'tcx> { /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`. pub locals: Vec, - /// Temporary allocations introduced to save stackframes - /// This is pure interpreter magic and has nothing to do with how rustc does it - /// An example is calling an FnMut closure that has been converted to a FnOnce closure - /// The value's destructor will be called and the memory freed when the stackframe finishes - pub interpreter_temporaries: Vec<(Pointer, Ty<'tcx>)>, - //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// @@ -208,12 +199,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - pub fn load_mir(&self, def_id: DefId) -> EvalResult<'tcx, MirRef<'tcx>> { - trace!("load mir {:?}", def_id); - if def_id.is_local() || self.tcx.sess.cstore.is_item_mir_available(def_id) { - Ok(self.tcx.item_mir(def_id)) - } else { - Err(EvalError::NoMirFor(self.tcx.item_path_str(def_id))) + pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, MirRef<'tcx>> { + trace!("load mir {:?}", instance); + match instance { + ty::InstanceDef::Item(def_id) => self.tcx.maybe_item_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))), + _ => Ok(self.tcx.instance_mir(instance)), } } @@ -272,13 +262,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn push_stack_frame( &mut self, - def_id: DefId, + instance: ty::Instance<'tcx>, span: codemap::Span, mir: MirRef<'tcx>, - substs: &'tcx Substs<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, - temporaries: Vec<(Pointer, Ty<'tcx>)>, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -293,10 +281,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, return_lvalue, locals, - interpreter_temporaries: temporaries, span, - def_id, - substs, + instance, stmt: 0, }); @@ -352,13 +338,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } } - // drop and deallocate all temporary allocations - for (ptr, ty) in frame.interpreter_temporaries { - trace!("dropping temporary allocation"); - let mut drops = Vec::new(); - self.drop(Lvalue::from_ptr(ptr), ty, &mut drops)?; - self.eval_drop_impls(drops, frame.span)?; - } + Ok(()) } @@ -665,8 +645,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ReifyFnPointer => match self.operand_ty(operand).sty { - ty::TyFnDef(def_id, substs, sig) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs, sig); + ty::TyFnDef(def_id, substs, _) => { + let fn_ptr = self.memory.create_fn_ptr(def_id, substs); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -682,8 +662,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ClosureFnPointer => match self.operand_ty(operand).sty { ty::TyClosure(def_id, substs) => { - let fn_ty = self.tcx.closure_type(def_id); - let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(def_id, substs, fn_ty); + let instance = resolve_closure(self.tcx, def_id, substs, ty::ClosureKind::FnOnce); + let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), @@ -845,16 +825,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // function items are zero sized Value::ByRef(self.memory.allocate(0, 0)?) } else { - let (def_id, substs) = self.resolve_associated_const(def_id, substs); - let cid = GlobalId { def_id, substs, promoted: None }; + let instance = self.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; self.globals.get(&cid).expect("static/const not cached").value } } Literal::Promoted { index } => { let cid = GlobalId { - def_id: self.frame().def_id, - substs: self.substs(), + instance: self.frame().instance, promoted: Some(index), }; self.globals.get(&cid).expect("promoted not cached").value @@ -891,8 +870,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, val => { let ty = self.stack[frame].mir.local_decls[local].ty; - let ty = self.monomorphize(ty, self.stack[frame].substs); - let substs = self.stack[frame].substs; + let ty = self.monomorphize(ty, self.stack[frame].instance.substs); + let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.write_value_to_ptr(val, ptr, ty)?; @@ -911,7 +890,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match global_val.value { Value::ByRef(ptr) => Lvalue::from_ptr(ptr), _ => { - let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.substs)?; + let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; // see comment on `initialized` field @@ -1289,7 +1268,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn substs(&self) -> &'tcx Substs<'tcx> { - self.frame().substs + self.frame().instance.substs } fn unsize_into_ptr( @@ -1320,7 +1299,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (_, &ty::TyDynamic(ref data, _)) => { let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); - let vtable = self.get_vtable(trait_ref)?; + let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; let ptr = PrimVal::Ptr(ptr); let extra = PrimVal::Ptr(vtable); @@ -1519,7 +1498,8 @@ pub fn eval_main<'a, 'tcx: 'a>( limits: ResourceLimits, ) { let mut ecx = EvalContext::new(tcx, limits); - let mir = ecx.load_mir(def_id).expect("main function's MIR not found"); + let instance = ty::Instance::mono(tcx, def_id); + let mir = ecx.load_mir(instance.def).expect("main function's MIR not found"); if !mir.return_ty.is_nil() || mir.arg_count != 0 { let msg = "miri does not support main functions without `fn()` type signatures"; @@ -1528,13 +1508,11 @@ pub fn eval_main<'a, 'tcx: 'a>( } ecx.push_stack_frame( - def_id, + instance, DUMMY_SP, mir, - tcx.intern_substs(&[]), Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, - Vec::new(), ).expect("could not allocate first stack frame"); loop { @@ -1564,23 +1542,12 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { block.terminator().source_info.span }; let mut err = tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { - if tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr { + for &Frame { instance, span, .. } in ecx.stack().iter().rev() { + if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { err.span_note(span, "inside call to closure"); continue; } - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); - impl<'tcx> ::std::panic::UnwindSafe for Instance<'tcx> {} - impl<'tcx> ::std::panic::RefUnwindSafe for Instance<'tcx> {} - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, &[]) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + err.span_note(span, &format!("inside call to {}", instance)); } err.emit(); } @@ -1657,3 +1624,80 @@ impl<'b, 'tcx: 'b> IntoValTyPair<'tcx> for &'b mir::Operand<'tcx> { Ok((value, value_ty)) } } + + +/// FIXME: expose trans::monomorphize::resolve_closure +pub fn resolve_closure<'a, 'tcx> ( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: ty::ClosureSubsts<'tcx>, + requested_kind: ty::ClosureKind, +) -> ty::Instance<'tcx> { + let actual_kind = tcx.closure_kind(def_id); + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => fn_once_adapter_instance(tcx, def_id, substs), + _ => ty::Instance::new(def_id, substs.substs) + } +} + +fn fn_once_adapter_instance<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + closure_did: DefId, + substs: ty::ClosureSubsts<'tcx>, +) -> ty::Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", + closure_did, + substs); + let fn_once = tcx.lang_items.fn_once_trait().unwrap(); + let call_once = tcx.associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure_from_closure_substs( + closure_did, substs); + + let sig = tcx.closure_type(closure_did).subst(tcx, substs.substs); + let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs([ + Kind::from(self_ty), + Kind::from(sig.inputs()[0]), + ].iter().cloned()); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + ty::Instance { def, substs } +} + +fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind) + -> Result +{ + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | + (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at trans time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | + (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at trans time. + Ok(true) + } + _ => Err(()), + } +} diff --git a/src/lvalue.rs b/src/lvalue.rs index 35fe958f7567f..62855c0450579 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -1,7 +1,5 @@ -use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::layout::{Size, Align}; -use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; @@ -42,15 +40,9 @@ pub enum LvalueExtra { /// Uniquely identifies a specific constant or static. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalId<'tcx> { - /// For a constant or static, the `DefId` of the item itself. - /// For a promoted global, the `DefId` of the function they belong to. - pub(super) def_id: DefId, - - /// For statics and constants this is `Substs::empty()`, so only promoteds and associated - /// constants actually have something useful here. We could special case statics and constants, - /// but that would only require more branching when working with constants, and not bring any - /// real benefits. - pub(super) substs: &'tcx Substs<'tcx>, + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub(super) instance: ty::Instance<'tcx>, /// The index for promoted globals within their function's `Mir`. pub(super) promoted: Option, @@ -138,8 +130,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(ref static_) => { - let substs = self.tcx.intern_substs(&[]); - Lvalue::Global(GlobalId { def_id: static_.def_id, substs, promoted: None }) + let instance = ty::Instance::mono(self.tcx, static_.def_id); + Lvalue::Global(GlobalId { instance, promoted: None }) } Projection(ref proj) => return self.eval_lvalue_projection(proj), diff --git a/src/memory.rs b/src/memory.rs index d6563c4c703cd..058f8b478cac4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -3,7 +3,7 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet use std::{fmt, iter, ptr, mem, io}; use rustc::hir::def_id::DefId; -use rustc::ty::{self, PolyFnSig, ClosureSubsts}; +use rustc::ty; use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; @@ -102,44 +102,6 @@ impl Pointer { } } -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] -/// Identifies a specific monomorphized function -pub struct FunctionDefinition<'tcx> { - pub def_id: DefId, - pub substs: &'tcx Substs<'tcx>, - pub sig: PolyFnSig<'tcx>, -} - -/// Either a concrete function, or a glue function -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] -pub enum Function<'tcx> { - /// A function or method created by compiling code - Concrete(FunctionDefinition<'tcx>), - /// Glue required to call a regular function through a Fn(Mut|Once) trait object - FnDefAsTraitObject(FunctionDefinition<'tcx>), - /// A drop glue function only needs to know the real type, and then miri can extract - /// that type from a vtable's drop pointer. - /// Instead of storing some drop function, we act as if there are no trait objects, by - /// mapping trait objects to their real types before acting on them. - DropGlue(ty::Ty<'tcx>), - /// Glue required to treat the ptr part of a fat pointer - /// as a function pointer - FnPtrAsTraitObject(PolyFnSig<'tcx>), - /// Glue for Closures - Closure(FunctionDefinition<'tcx>), - /// Glue for noncapturing closures casted to function pointers - NonCaptureClosureAsFnPtr(FunctionDefinition<'tcx>), -} - -impl<'tcx> Function<'tcx> { - pub fn expect_drop_glue_real_ty(self) -> EvalResult<'tcx, ty::Ty<'tcx>> { - match self { - Function::DropGlue(real_ty) => Ok(real_ty), - other => Err(EvalError::ExpectedDropGlue(other)), - } - } -} - //////////////////////////////////////////////////////////////////////////////// // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// @@ -165,10 +127,10 @@ pub struct Memory<'a, 'tcx> { /// Function "allocations". They exist solely so pointers have something to point to, and /// we can figure out what they point to. - functions: HashMap>, + functions: HashMap>, /// Inverse map of `functions` so we don't allocate a new pointer every time we need one - function_alloc_cache: HashMap, AllocId>, + function_alloc_cache: HashMap, AllocId>, /// Target machine data layout to emulate. pub layout: &'a TargetDataLayout, @@ -214,55 +176,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::Closure(FunctionDefinition { - def_id, - substs: substs.substs, - sig, - })) - } - - pub fn create_fn_ptr_from_noncapture_closure(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition { - def_id, - substs: substs.substs, - sig, - })) - } - - pub fn create_fn_as_trait_glue(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition { - def_id, - substs, - sig, - })) - } - - pub fn create_fn_ptr_as_trait_glue(&mut self, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::FnPtrAsTraitObject(sig)) - } - - pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer { - self.create_fn_alloc(Function::DropGlue(ty)) - } - - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer { - self.create_fn_alloc(Function::Concrete(FunctionDefinition { - def_id, - substs, - sig, - })) + pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { + let instance = ty::Instance::new(def_id, substs); + self.create_fn_alloc(instance) } - fn create_fn_alloc(&mut self, def: Function<'tcx>) -> Pointer { - if let Some(&alloc_id) = self.function_alloc_cache.get(&def) { + pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer { + if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { return Pointer::new(alloc_id, 0); } let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; - self.functions.insert(id, def); - self.function_alloc_cache.insert(def, id); + self.functions.insert(id, instance); + self.function_alloc_cache.insert(instance, id); Pointer::new(id, 0) } @@ -469,7 +396,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, Function<'tcx>> { + pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, ty::Instance<'tcx>> { debug!("reading fn ptr: {}", id); match self.functions.get(&id) { Some(&fndef) => Ok(fndef), @@ -501,28 +428,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) { (Some(a), None) => a, - (None, Some(&Function::Concrete(fn_def))) => { - trace!("{} {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::DropGlue(real_ty))) => { - trace!("{} drop glue for {}", msg, real_ty); - continue; - }, - (None, Some(&Function::FnDefAsTraitObject(fn_def))) => { - trace!("{} fn as Fn glue for {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::FnPtrAsTraitObject(fn_def))) => { - trace!("{} fn ptr as Fn glue (signature: {:?})", msg, fn_def); - continue; - }, - (None, Some(&Function::Closure(fn_def))) => { - trace!("{} closure glue for {}", msg, dump_fn_def(fn_def)); - continue; - }, - (None, Some(&Function::NonCaptureClosureAsFnPtr(fn_def))) => { - trace!("{} non-capture closure as fn ptr glue for {}", msg, dump_fn_def(fn_def)); + (None, Some(instance)) => { + trace!("{} {}", msg, instance); continue; }, (None, None) => { @@ -594,11 +501,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } -fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String { - let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id)); - format!("function pointer: {}: {}", name, fn_def.sig.skip_binder()) -} - /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { diff --git a/src/step.rs b/src/step.rs index 23f0142a7f53f..8a0cbc4a8f1c9 100644 --- a/src/step.rs +++ b/src/step.rs @@ -45,8 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut new = Ok(0); ConstantExtractor { span: stmt.source_info.span, - substs: self.substs(), - def_id: self.frame().def_id, + instance: self.frame().instance, ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, @@ -63,8 +62,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut new = Ok(0); ConstantExtractor { span: terminator.source_info.span, - substs: self.substs(), - def_id: self.frame().def_id, + instance: self.frame().instance, ecx: self, mir: Ref::clone(&mir), new_constants: &mut new, @@ -145,8 +143,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, ecx: &'a mut EvalContext<'b, 'tcx>, mir: MirRef<'tcx>, - def_id: DefId, - substs: &'tcx subst::Substs<'tcx>, + instance: ty::Instance<'tcx>, new_constants: &'a mut EvalResult<'tcx, u64>, } @@ -158,26 +155,24 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { span: Span, shared: bool, ) { - let (def_id, substs) = self.ecx.resolve_associated_const(def_id, substs); - let cid = GlobalId { def_id, substs, promoted: None }; + let instance = self.ecx.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; if self.ecx.globals.contains_key(&cid) { return; } self.try(|this| { - let mir = this.ecx.load_mir(def_id)?; + let mir = this.ecx.load_mir(instance.def)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe(); let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); trace!("pushing stack frame for global: {}", name); this.ecx.push_stack_frame( - def_id, + instance, span, mir, - substs, Lvalue::Global(cid), cleanup, - Vec::new(), ) }); } @@ -210,8 +205,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { }, mir::Literal::Promoted { index } => { let cid = GlobalId { - def_id: self.def_id, - substs: self.substs, + instance: self.instance, promoted: Some(index), }; if self.ecx.globals.contains_key(&cid) { @@ -220,16 +214,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { let mir = Ref::clone(&self.mir); let mir = Ref::map(mir, |mir| &mir.promoted[index]); self.try(|this| { - let ty = this.ecx.monomorphize(mir.return_ty, this.substs); + let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs); this.ecx.globals.insert(cid, Global::uninitialized(ty)); trace!("pushing stack frame for {:?}", index); - this.ecx.push_stack_frame(this.def_id, + this.ecx.push_stack_frame(this.instance, constant.span, mir, - this.substs, Lvalue::Global(cid), - StackPopCleanup::MarkStatic(false), - Vec::new()) + StackPopCleanup::MarkStatic(false)) }); } } diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs deleted file mode 100644 index efa415679328c..0000000000000 --- a/src/terminator/drop.rs +++ /dev/null @@ -1,224 +0,0 @@ -use rustc::hir::def_id::DefId; -use rustc::traits; -use rustc::ty::layout::Layout; -use rustc::ty::subst::{Substs, Kind}; -use rustc::ty::{self, Ty}; -use rustc::mir; -use syntax::codemap::Span; - -use error::{EvalError, EvalResult}; -use eval_context::{EvalContext, monomorphize_field_ty, StackPopCleanup}; -use lvalue::{Lvalue, LvalueExtra}; -use memory::Pointer; -use value::PrimVal; -use value::Value; - -impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Creates stack frames for all drop impls. See `drop` for the actual content. - pub fn eval_drop_impls(&mut self, drops: Vec<(DefId, Value, &'tcx Substs<'tcx>)>, span: Span) -> EvalResult<'tcx> { - // add them to the stack in reverse order, because the impl that needs to run the last - // is the one that needs to be at the bottom of the stack - for (drop_def_id, self_arg, substs) in drops.into_iter().rev() { - let mir = self.load_mir(drop_def_id)?; - trace!("substs for drop glue: {:?}", substs); - self.push_stack_frame( - drop_def_id, - span, - mir, - substs, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - Vec::new(), - )?; - let mut arg_locals = self.frame().mir.args_iter(); - let first = arg_locals.next().expect("drop impl has self arg"); - assert!(arg_locals.next().is_none(), "drop impl should have only one arg"); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first))?; - let ty = self.frame().mir.local_decls[first].ty; - self.write_value(self_arg, dest, ty)?; - } - Ok(()) - } - - /// push DefIds of drop impls and their argument on the given vector - pub fn drop( - &mut self, - lval: Lvalue<'tcx>, - ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> { - if !self.type_needs_drop(ty) { - debug!("no need to drop {:?}", ty); - return Ok(()); - } - trace!("need to drop {:?} at {:?}", ty, lval); - - match ty.sty { - // special case `Box` to deallocate the inner allocation - ty::TyAdt(ref def, _) if def.is_box() => { - let contents_ty = ty.boxed_ty(); - let val = self.read_lvalue(lval); - // we are going through the read_value path, because that already does all the - // checks for the trait object types. We'd only be repeating ourselves here. - let val = self.follow_by_ref_value(val, ty)?; - trace!("box dealloc on {:?}", val); - match val { - Value::ByRef(_) => bug!("follow_by_ref_value can't result in ByRef"), - Value::ByVal(ptr) => { - assert!(self.type_is_sized(contents_ty)); - let contents_ptr = ptr.to_ptr()?; - self.drop(Lvalue::from_ptr(contents_ptr), contents_ty, drop)?; - }, - Value::ByValPair(prim_ptr, extra) => { - let ptr = prim_ptr.to_ptr()?; - let extra = match self.tcx.struct_tail(contents_ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), - _ => bug!("invalid fat pointer type: {}", ty), - }; - self.drop(Lvalue::Ptr { ptr, extra }, contents_ty, drop)?; - }, - } - // We cannot use Box's destructor, because it is a no-op and only exists to reduce - // the number of hacks required in the compiler around the Box type. - let box_free_fn = self.tcx.lang_items.box_free_fn().expect("no box_free lang item"); - let substs = self.tcx.intern_substs(&[Kind::from(contents_ty)]); - // this is somewhat hacky, but hey, there's no representation difference between - // pointers, `Box`es and references, so - // #[lang = "box_free"] unsafe fn box_free(ptr: *mut T) - // is the same as - // fn drop(&mut self) if Self is Box - drop.push((box_free_fn, val, substs)); - } - - ty::TyAdt(adt_def, substs) => { - // FIXME: some structs are represented as ByValPair - let mut lval = self.force_allocation(lval)?; - let (adt_ptr, extra) = lval.to_ptr_and_extra(); - - // run drop impl before the fields' drop impls - if let Some(destructor) = adt_def.destructor(self.tcx) { - let trait_ref = ty::Binder(ty::TraitRef { - def_id: self.tcx.lang_items.drop_trait().unwrap(), - substs: self.tcx.mk_substs_trait(ty, &[]), - }); - let vtable = match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(data) => data, - _ => bug!("dtor for {:?} is not an impl???", ty) - }; - let val = match extra { - LvalueExtra::None => Value::ByVal(PrimVal::Ptr(adt_ptr)), - LvalueExtra::DowncastVariant(_) => bug!("downcast variant in drop"), - LvalueExtra::Length(n) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::from_u128(n as u128)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(PrimVal::Ptr(adt_ptr), PrimVal::Ptr(vtable)), - }; - drop.push((destructor.did, val, vtable.substs)); - } - - let layout = self.type_layout(ty)?; - let fields = match *layout { - Layout::Univariant { .. } => &adt_def.struct_variant().fields, - Layout::General { .. } => { - let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128; - let ptr = self.force_allocation(lval)?.to_ptr(); - match adt_def.discriminants(self.tcx).position(|v| discr_val == v.to_u128_unchecked()) { - Some(i) => { - lval = Lvalue::Ptr { - ptr, - extra: LvalueExtra::DowncastVariant(i), - }; - &adt_def.variants[i].fields - }, - None => return Err(EvalError::InvalidDiscriminant), - } - }, - Layout::StructWrappedNullablePointer { .. } | - Layout::RawNullablePointer { .. } => { - let discr = self.read_discriminant_value(adt_ptr, ty)?; - assert_eq!(discr as usize as u128, discr); - &adt_def.variants[discr as usize].fields - }, - Layout::CEnum { .. } => return Ok(()), - _ => bug!("{:?} is not an adt layout", layout), - }; - let tcx = self.tcx; - self.drop_fields( - fields.iter().map(|field| monomorphize_field_ty(tcx, field, substs)), - lval, - ty, - drop, - )?; - } - - ty::TyTuple(fields, _) => - self.drop_fields(fields.into_iter().cloned(), lval, ty, drop)?, - - ty::TyDynamic(..) => { - let (ptr, vtable) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => (ptr, vtable), - _ => bug!("expected an lvalue with a vtable"), - }; - if let Some(real_ty) = self.read_drop_type_from_vtable(vtable)? { - self.drop(Lvalue::from_ptr(ptr), real_ty, drop)?; - } - } - - ty::TySlice(elem_ty) => { - let (ptr, len) = match lval { - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len), - _ => bug!("expected an lvalue with a length"), - }; - let size = self.type_size(elem_ty)?.expect("slice element must be sized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..len { - self.drop(Lvalue::from_ptr(ptr.offset(i * size)), elem_ty, drop)?; - } - } - - ty::TyArray(elem_ty, len) => { - let lval = self.force_allocation(lval)?; - let (ptr, extra) = match lval { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - _ => bug!("expected an lvalue with optional extra data"), - }; - let size = self.type_size(elem_ty)?.expect("array element cannot be unsized"); - // FIXME: this creates a lot of stack frames if the element type has - // a drop impl - for i in 0..(len as u64) { - self.drop(Lvalue::Ptr { ptr: ptr.offset(i * size), extra }, elem_ty, drop)?; - } - } - - ty::TyClosure(def_id, substs) => { - let fields = substs.upvar_tys(def_id, self.tcx); - self.drop_fields(fields, lval, ty, drop)?; - } - - _ => bug!(), - } - - Ok(()) - } - - fn drop_fields( - &mut self, - fields: I, - lval: Lvalue<'tcx>, - ty: Ty<'tcx>, - drop: &mut Vec<(DefId, Value, &'tcx Substs<'tcx>)>, - ) -> EvalResult<'tcx> - where I: Iterator>, - { - trace!("drop_fields: {:?} of type {}", lval, ty); - for (i, field_ty) in fields.enumerate() { - let field_lval = self.lvalue_field(lval, i, ty, field_ty)?; - self.drop(field_lval, field_ty, drop)?; - } - Ok(()) - } - - fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()) - } -} diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2e1202ab5d1f..a84b87f49a736 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -1,4 +1,3 @@ -use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::subst::Substs; @@ -13,8 +12,7 @@ use value::{PrimVal, PrimValKind, Value}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( &mut self, - def_id: DefId, - substs: &'tcx Substs<'tcx>, + instance: ty::Instance<'tcx>, args: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, @@ -31,7 +29,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let f32 = self.tcx.types.f32; let f64 = self.tcx.types.f64; - let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..]; + let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_ty)?, @@ -60,7 +58,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -69,7 +67,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_relaxed" | "atomic_store_rel" | "volatile_store" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -79,7 +77,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_xchg") => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -93,7 +91,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_cxchg") => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; @@ -115,7 +113,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" | "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -144,7 +142,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy" | "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs - let elem_ty = substs.type_at(0); + let elem_ty = instance.substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; @@ -157,7 +155,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "cttz" | "ctlz" | "bswap" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let num = self.value_to_primval(arg_vals[0], ty)?; let kind = self.ty_to_primval_kind(ty)?; let num = numeric_intrinsic(intrinsic_name, num, kind)?; @@ -165,41 +163,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } - "drop_in_place" => { - let ty = substs.type_at(0); - trace!("drop in place on {}", ty); - let ptr_ty = self.tcx.mk_mut_ptr(ty); - let lvalue = match self.follow_by_ref_value(arg_vals[0], ptr_ty)? { - Value::ByRef(_) => bug!("follow_by_ref_value returned ByRef"), - Value::ByVal(value) => Lvalue::from_ptr(value.to_ptr()?), - Value::ByValPair(ptr, extra) => Lvalue::Ptr { - ptr: ptr.to_ptr()?, - extra: match self.tcx.struct_tail(ty).sty { - ty::TyDynamic(..) => LvalueExtra::Vtable(extra.to_ptr()?), - ty::TyStr | ty::TySlice(_) => LvalueExtra::Length(extra.to_u64()?), - _ => bug!("invalid fat pointer type: {}", ptr_ty), - }, - }, - }; - let mut drops = Vec::new(); - self.drop(lvalue, ty, &mut drops)?; - let span = { - let frame = self.frame(); - frame.mir[frame.block].terminator().source_info.span - }; - // need to change the block before pushing the drop impl stack frames - // we could do this for all intrinsics before evaluating the intrinsics, but if - // the evaluation fails, we should not have moved forward - self.goto_block(target); - return self.eval_drop_impls(drops, span); - } - "sinf32" | "fabsf32" | "cosf32" | "sqrtf32" | "expf32" | "exp2f32" | "logf32" | "log10f32" | "log2f32" | @@ -247,7 +216,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[1], ty)?; @@ -279,7 +248,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { - let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; + let ptr = this.alloc_ptr_with_substs(dest_ty, instance.substs)?; this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) } @@ -299,14 +268,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "min_align_of" => { - let elem_ty = substs.type_at(0); + let elem_ty = instance.substs.type_at(0); let elem_align = self.type_align(elem_ty)?; let align_val = PrimVal::from_u128(elem_align as u128); self.write_primval(dest, align_val, dest_ty)?; } "pref_align_of" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); let align_val = PrimVal::from_u128(align as u128); @@ -314,20 +283,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "move_val_init" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } "needs_drop" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } "offset" => { - let pointee_ty = substs.type_at(0); + let pointee_ty = instance.substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; @@ -388,7 +357,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") @@ -398,32 +367,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of_val" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; } "type_name" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ty_name = ty.to_string(); let s = self.str_to_value(&ty_name)?; self.write_value(s, dest, dest_ty)?; } "type_id" => { - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let n = self.tcx.type_id_hash(ty); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } "transmute" => { - let dest_ty = substs.type_at(1); + let dest_ty = instance.substs.type_at(1); self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -449,7 +418,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write_bytes" => { let u8 = self.tcx.types.u8; - let ty = substs.type_at(0); + let ty = instance.substs.type_at(0); let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 659f8e3152044..8e0623dcba176 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,9 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::layout::Layout; -use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use rustc_const_math::ConstInt; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -11,12 +8,11 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, FunctionDefinition, Function}; +use memory::Pointer; use value::PrimVal; use value::Value; mod intrinsic; -mod drop; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn goto_block(&mut self, target: mir::BasicBlock) { @@ -64,46 +60,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let func_ty = self.operand_ty(func); let fn_def = match func_ty.sty { - ty::TyFnPtr(bare_sig) => { - let bare_sig = self.erase_lifetimes(&bare_sig); + ty::TyFnPtr(_) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?; - match fn_def { - Function::Concrete(fn_def) => { - // transmuting function pointers in miri is fine as long as the number of - // arguments and the abi don't change. - let sig = self.erase_lifetimes(&fn_def.sig); - if sig.abi != bare_sig.abi || - sig.variadic != bare_sig.variadic || - sig.inputs_and_output != bare_sig.inputs_and_output { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - assert_eq!(sig.abi, Abi::RustCall); - if sig.variadic != bare_sig.variadic || - sig.inputs().len() != 1 { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - if let ty::TyTuple(fields, _) = sig.inputs()[0].sty { - if **fields != *bare_sig.inputs() { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - } else { - return Err(EvalError::FunctionPointerTyMismatch(sig, bare_sig)); - } - }, - other => return Err(EvalError::ExpectedConcreteFunction(other)), - } self.memory.get_fn(fn_ptr.alloc_id)? }, - ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition { - def_id, - substs, - sig: fn_ty, - }), - + ty::TyFnDef(def_id, substs, _) => ty::Instance::new(def_id, substs), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); @@ -112,19 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?; } - Drop { ref location, target, .. } => { - let lval = self.eval_lvalue(location)?; - - let ty = self.lvalue_ty(location); - - // we can't generate the drop stack frames on the fly, - // because that would change our call stack - // and very much confuse the further processing of the drop glue - let mut drops = Vec::new(); - self.drop(lval, ty, &mut drops)?; - self.goto_block(target); - self.eval_drop_impls(drops, terminator.source_info.span)?; - } + Drop { .. } => unreachable!(), Assert { ref cond, expected, ref msg, target, .. } => { let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; @@ -158,15 +107,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_fn_call( &mut self, - fn_def: Function<'tcx>, + instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, ) -> EvalResult<'tcx> { - use syntax::abi::Abi; - match fn_def { - // Intrinsics can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => { + let sig = match instance.def.def_ty(self.tcx).sty { + ty::TyFnPtr(bare_sig) => bare_sig, + ty::TyFnDef(_, _, fn_ty) => fn_ty, + ref other => bug!("expected function or pointer, got {:?}", other), + }; + match sig.abi() { + Abi::RustIntrinsic => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let layout = self.type_layout(ty)?; @@ -174,139 +126,51 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Some(dest) if is_inhabited(self.tcx, ty) => dest, _ => return Err(EvalError::Unreachable), }; - self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout, target)?; + self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); Ok(()) }, - // C functions can only be addressed directly - Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => { + Abi::C => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let (ret, target) = destination.unwrap(); - self.call_c_abi(def_id, arg_operands, ret, ty)?; + match instance.def { + ty::InstanceDef::Item(_) => {}, + _ => bug!("C abi function must be InstanceDef::Item"), + } + self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; self.dump_local(ret); self.goto_block(target); Ok(()) }, - Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(FunctionDefinition { def_id, sig, substs }) if sig.abi() == Abi::Rust || sig.abi() == Abi::RustCall => { + Abi::Rust | Abi::RustCall => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - - // Only trait methods can have a Self parameter. - let (resolved_def_id, resolved_substs, temporaries) = - if let Some(trait_id) = self.tcx.trait_of_item(def_id) { - self.trait_method(trait_id, def_id, substs, &mut args)? - } else { - (def_id, substs, Vec::new()) - }; - - // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { - let dids = adt_def.variants.iter().map(|v| v.did); - let discrs = adt_def.discriminants(self.tcx).map(ConstInt::to_u128_unchecked); - if let Some((_, disr_val)) = dids.zip(discrs).find(|&(did, _)| resolved_def_id == did) { - let (lvalue, target) = destination.expect("tuple struct constructors can't diverge"); - let dest_ty = self.tcx.item_type(adt_def.did); - let dest_layout = self.type_layout(dest_ty)?; - trace!("layout({:?}) = {:#?}", dest_ty, dest_layout); - match *dest_layout { - Layout::Univariant { .. } => { - assert_eq!(disr_val, 0); - self.assign_fields(lvalue, dest_ty, args)?; - }, - Layout::General { discr, ref variants, .. } => { - let discr_size = discr.size().bytes(); - self.assign_discr_and_fields( - lvalue, - dest_ty, - variants[disr_val as usize].offsets[0].bytes(), - args, - disr_val, - disr_val as usize, - discr_size, - )?; - }, - Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - if nndiscr as u128 == disr_val { - self.assign_fields(lvalue, dest_ty, args)?; - } else { - for (_, ty) in args { - assert_eq!(self.type_size(ty)?, Some(0)); - } - let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; - - // FIXME(solson) - let dest = self.force_allocation(lvalue)?.to_ptr(); - - let dest = dest.offset(offset.bytes()); - let dest_size = self.type_size(ty)? - .expect("bad StructWrappedNullablePointer discrfield"); - self.memory.write_int(dest, 0, dest_size)?; - } - }, - Layout::RawNullablePointer { .. } => { - assert_eq!(args.len(), 1); - let (val, ty) = args.pop().unwrap(); - self.write_value(val, lvalue, ty)?; - }, - _ => bug!("bad layout for tuple struct constructor: {:?}", dest_layout), - } - self.goto_block(target); - return Ok(()); - } - } self.eval_fn_call_inner( - resolved_def_id, - resolved_substs, + instance, destination, args, - temporaries, span, ) }, - Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => { - let sig = self.erase_lifetimes(&sig); - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - sig.inputs()[0], - )); - self.eval_fn_call_inner( - def_id, - substs, - destination, - args, - Vec::new(), - span, - ) - } - Function::Concrete(fn_def) => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", fn_def.sig.abi()))), - other => Err(EvalError::Unimplemented(format!("can't call function kind {:#?}", other))), + other => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", other))), } } fn eval_fn_call_inner( &mut self, - resolved_def_id: DefId, - resolved_substs: &'tcx Substs, + instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, args: Vec<(Value, Ty<'tcx>)>, - temporaries: Vec<(Pointer, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", args, temporaries, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}", args, destination); - let mir = match self.load_mir(resolved_def_id) { + let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { match &path[..] { @@ -344,13 +208,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.push_stack_frame( - resolved_def_id, + instance, span, mir, - resolved_substs, return_lvalue, return_to_block, - temporaries, )?; let arg_locals = self.frame().mir.args_iter(); @@ -530,33 +392,4 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // current frame. Ok(()) } - - pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { - if let Some((last, last_ty)) = args.pop() { - let last_layout = self.type_layout(last_ty)?; - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields, _), - &Layout::Univariant { ref variant, .. }) => { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - match last { - Value::ByRef(last_ptr) => { - for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset)); - args.push((arg, ty)); - } - }, - // propagate undefs - undef @ Value::ByVal(PrimVal::Undef) => { - for field_ty in fields { - args.push((undef, field_ty)); - } - }, - _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - Ok(()) - } } diff --git a/src/traits.rs b/src/traits.rs index 72de17801d5eb..74cb6966c067b 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -8,171 +8,11 @@ use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use syntax::codemap::DUMMY_SP; -use syntax::{ast, abi}; +use syntax::ast; -use error::{EvalError, EvalResult}; -use memory::Function; -use value::PrimVal; -use value::Value; +use error::EvalResult; impl<'a, 'tcx> EvalContext<'a, 'tcx> { - /// Trait method, which has to be resolved to an impl method. - pub(crate) fn trait_method( - &mut self, - trait_id: DefId, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - args: &mut Vec<(Value, Ty<'tcx>)>, - ) -> EvalResult<'tcx, (DefId, &'tcx Substs<'tcx>, Vec<(Pointer, Ty<'tcx>)>)> { - let trait_ref = ty::TraitRef::from_method(self.tcx, trait_id, substs); - let trait_ref = self.tcx.normalize_associated_type(&ty::Binder(trait_ref)); - - match self.fulfill_obligation(trait_ref) { - traits::VtableImpl(vtable_impl) => { - let impl_did = vtable_impl.impl_def_id; - let mname = self.tcx.item_name(def_id); - // Create a concatenated set of substitutions which includes those from the impl - // and those from the method: - let (did, substs) = find_method(self.tcx, substs, impl_did, vtable_impl.substs, mname); - - Ok((did, substs, Vec::new())) - } - - traits::VtableClosure(vtable_closure) => { - let trait_closure_kind = self.tcx - .lang_items - .fn_trait_kind(trait_id) - .expect("The substitutions should have no type parameters remaining after passing through fulfill_obligation"); - let closure_kind = self.tcx.closure_kind(vtable_closure.closure_def_id); - trace!("closures {:?}, {:?}", closure_kind, trait_closure_kind); - self.unpack_fn_args(args)?; - let mut temporaries = Vec::new(); - match (closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) | - (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {} // No adapter needed. - - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) | - (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn is a `fn(&self, ...)` or `fn(&mut self, ...)`. - // We want a `fn(self, ...)`. - // We can produce this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at trans time. - - // Interpreter magic: insert an intermediate pointer, so we can skip the - // intermediate function call. - let ptr = match args[0].0 { - Value::ByRef(ptr) => ptr, - Value::ByVal(primval) => { - let ptr = self.alloc_ptr(args[0].1)?; - let size = self.type_size(args[0].1)?.expect("closures are sized"); - self.memory.write_primval(ptr, primval, size)?; - ptr - }, - Value::ByValPair(a, b) => { - let ptr = self.alloc_ptr(args[0].1)?; - self.write_pair_to_ptr(a, b, ptr, args[0].1)?; - ptr - }, - }; - temporaries.push((ptr, args[0].1)); - args[0].0 = Value::ByVal(PrimVal::Ptr(ptr)); - args[0].1 = self.tcx.mk_mut_ptr(args[0].1); - } - - _ => bug!("cannot convert {:?} to {:?}", closure_kind, trait_closure_kind), - } - Ok((vtable_closure.closure_def_id, vtable_closure.substs.substs, temporaries)) - } - - traits::VtableFnPointer(vtable_fn_ptr) => { - if let ty::TyFnDef(did, substs, _) = vtable_fn_ptr.fn_ty.sty { - args.remove(0); - self.unpack_fn_args(args)?; - Ok((did, substs, Vec::new())) - } else { - bug!("VtableFnPointer did not contain a concrete function: {:?}", vtable_fn_ptr) - } - } - - traits::VtableObject(ref data) => { - let idx = self.tcx.get_vtable_index_of_object_method(data, def_id) as u64; - if args.is_empty() { - return Err(EvalError::VtableForArgumentlessMethod); - } - let (self_ptr, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; - let idx = idx + 3; - let offset = idx * self.memory.pointer_size(); - let fn_ptr = self.memory.read_ptr(vtable.offset(offset))?; - trace!("args: {:#?}", args); - match self.memory.get_fn(fn_ptr.alloc_id)? { - Function::FnDefAsTraitObject(fn_def) => { - trace!("sig: {:#?}", fn_def.sig); - assert!(fn_def.sig.abi() != abi::Abi::RustCall); - assert_eq!(args.len(), 2); - // a function item turned into a closure trait object - // the first arg is just there to give use the vtable - args.remove(0); - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue), - Function::Concrete(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - trace!("sig: {:#?}", sig); - args[0] = ( - Value::ByVal(PrimVal::Ptr(self_ptr)), - sig.inputs()[0], - ); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let sig = self.erase_lifetimes(&fn_def.sig); - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - sig.inputs()[0], - )); - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - Function::Closure(fn_def) => { - self.unpack_fn_args(args)?; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - Function::FnPtrAsTraitObject(sig) => { - let sig = self.erase_lifetimes(&sig); - trace!("sig: {:#?}", sig); - // the first argument was the fat ptr - args.remove(0); - self.unpack_fn_args(args)?; - let fn_ptr = self.memory.read_ptr(self_ptr)?; - let fn_def = match self.memory.get_fn(fn_ptr.alloc_id)? { - Function::Concrete(fn_def) => { - let fn_def_sig = self.erase_lifetimes(&fn_def.sig); - assert_eq!(sig, fn_def_sig); - fn_def - }, - Function::NonCaptureClosureAsFnPtr(fn_def) => { - let fn_def_sig = self.erase_lifetimes(&fn_def.sig); - args.insert(0, ( - Value::ByVal(PrimVal::Undef), - fn_def_sig.inputs()[0], - )); - fn_def - }, - other => bug!("FnPtrAsTraitObject for {:?}", other), - }; - Ok((fn_def.def_id, fn_def.substs, Vec::new())) - } - } - }, - vtable => bug!("resolved vtable bad vtable {:?} in trans", vtable), - } - } pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are @@ -202,7 +42,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// The `trait_ref` encodes the erased self type. Hence if we are /// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. - pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { + pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { let tcx = self.tcx; debug!("get_vtable(trait_ref={:?})", trait_ref); @@ -219,13 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.get_vtable_methods(id, substs) .into_iter() .map(|opt_mth| opt_mth.map(|mth| { - let fn_ty = self.tcx.item_type(mth.method.def_id); - let fn_ty = match fn_ty.sty { - ty::TyFnDef(_, _, fn_ty) => fn_ty, - _ => bug!("bad function type: {}", fn_ty), - }; - let fn_ty = self.tcx.erase_regions(&fn_ty); - self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty) + self.memory.create_fn_ptr(mth.method.def_id, mth.substs) })) .collect::>() .into_iter() @@ -238,18 +72,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .. } ) => { - let closure_type = self.tcx.closure_type(closure_def_id); - vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter() + let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce); + vec![Some(self.memory.create_fn_alloc(instance))].into_iter() } // turn a function definition into a Fn trait object traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { match fn_ty.sty { - ty::TyFnDef(did, substs, bare_fn_ty) => { - vec![Some(self.memory.create_fn_as_trait_glue(did, substs, bare_fn_ty))].into_iter() + ty::TyFnDef(did, substs, _) => { + let instance = ty::Instance { + def: ty::InstanceDef::FnPtrShim(did, fn_ty), + substs, + }; + vec![Some(self.memory.create_fn_alloc(instance))].into_iter() }, - ty::TyFnPtr(bare_fn_ty) => { - vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter() + ty::TyFnPtr(_) => { + unimplemented!(); }, _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), } @@ -279,19 +117,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // in case there is no drop function to be called, this still needs to be initialized self.memory.write_usize(vtable, 0)?; + let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if let Some(destructor) = adt_def.destructor(self.tcx) { - let fn_ty = match self.tcx.item_type(destructor.did).sty { - ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty), - _ => bug!("drop method is not a TyFnDef"), - }; - let fn_ty = self.erase_lifetimes(&fn_ty); - // The real type is taken from the self argument in `fn drop(&mut self)` - let real_ty = match fn_ty.inputs()[0].sty { - ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs), - _ => bug!("first argument of Drop::drop must be &mut T"), + if adt_def.has_dtor(self.tcx) { + let env = self.tcx.empty_parameter_environment(); + let def = if self.tcx.type_needs_drop_given_env(ty, &env) { + ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) + } else { + ty::InstanceDef::DropGlue(drop_in_place, None) }; - let fn_ptr = self.memory.create_drop_glue(real_ty); + let instance = ty::Instance { substs, def }; + let fn_ptr = self.memory.create_fn_alloc(instance); self.memory.write_ptr(vtable, fn_ptr)?; } } @@ -310,20 +146,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } - pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { - let drop_fn = self.memory.read_ptr(vtable)?; - - // just a sanity check - assert_eq!(drop_fn.offset, 0); - - // some values don't need to call a drop impl, so the value is null - if drop_fn == Pointer::from_int(0) { - Ok(None) - } else { - self.memory.get_fn(drop_fn.alloc_id)?.expect_drop_glue_real_ty().map(Some) - } - } - pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_usize(vtable.offset(pointer_size))?; @@ -423,7 +245,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self, def_id: DefId, substs: &'tcx Substs<'tcx>, - ) -> (DefId, &'tcx Substs<'tcx>) { + ) -> ty::Instance<'tcx> { if let Some(trait_id) = self.tcx.trait_of_item(def_id) { let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs)); let vtable = self.fulfill_obligation(trait_ref); @@ -432,11 +254,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let assoc_const_opt = self.tcx.associated_items(vtable_impl.impl_def_id) .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); if let Some(assoc_const) = assoc_const_opt { - return (assoc_const.def_id, vtable_impl.substs); + return ty::Instance::new(assoc_const.def_id, vtable_impl.substs); } } } - (def_id, substs) + ty::Instance::new(def_id, substs) } } @@ -483,35 +305,3 @@ pub(super) fn get_impl_method<'a, 'tcx>( } } } - -/// Locates the applicable definition of a method, given its name. -pub fn find_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name) - -> (DefId, &'tcx Substs<'tcx>) -{ - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("find_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - (node_item.item.def_id, substs) - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} From 3612d826e74752e39688eb4fcd6dd7bd806fc9d4 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 21 Mar 2017 11:04:49 -0400 Subject: [PATCH 0873/1096] ast::Attribute no longer has a 'value' field --- src/bin/miri.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index f1a97d0a44b5c..04b11ff60783d 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -82,7 +82,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> { fn visit_item(&mut self, i: &'hir hir::Item) { if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { - if i.attrs.iter().any(|attr| attr.value.name == "test") { + if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) { let did = self.1.hir.body_owner_def_id(body_id); println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); miri::eval_main(self.1, did, self.0); @@ -117,8 +117,8 @@ fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits } }; - for attr in krate.attrs.iter().filter(|a| a.name() == "miri") { - if let MetaItemKind::List(ref items) = attr.value.node { + for attr in krate.attrs.iter().filter(|a| a.name().map_or(false, |n| n == "miri")) { + if let Some(items) = attr.meta_item_list() { for item in items { if let NestedMetaItemKind::MetaItem(ref inner) = item.node { if let MetaItemKind::NameValue(ref value) = inner.node { From 26c3335dbfd510a95284bd953e869cf31d5127ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 13:13:52 +0100 Subject: [PATCH 0874/1096] Closures work again --- src/eval_context.rs | 259 +++++++++++++++++++++++++++++++++++++++++- src/terminator/mod.rs | 114 +++++++++++++++---- 2 files changed, 352 insertions(+), 21 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9b2e5e1ceaba6..1ec690e620493 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -10,8 +10,11 @@ use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{Subst, Substs, Kind}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; +use rustc::traits; use rustc_data_structures::indexed_vec::Idx; -use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::{self, DUMMY_SP, Span}; +use syntax::ast; +use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -1701,3 +1704,257 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, _ => Err(()), } } + +/// The point where linking happens. Resolve a (def_id, substs) +/// pair to an instance. +pub fn resolve<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx> +) -> ty::Instance<'tcx> { + debug!("resolve(def_id={:?}, substs={:?})", + def_id, substs); + let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + debug!(" => associated item, attempting to find impl"); + let item = tcx.associated_item(def_id); + resolve_associated_item(tcx, &item, trait_def_id, substs) + } else { + let item_type = def_ty(tcx, def_id, substs); + let def = match item_type.sty { + ty::TyFnDef(_, _, f) if + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic => + { + debug!(" => intrinsic"); + ty::InstanceDef::Intrinsic(def_id) + } + _ => { + if Some(def_id) == tcx.lang_items.drop_in_place_fn() { + let ty = substs.type_at(0); + if needs_drop_glue(tcx, ty) { + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) + } else { + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) + } + } else { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } + } + }; + ty::Instance { def, substs } + }; + debug!("resolve(def_id={:?}, substs={:?}) = {}", + def_id, substs, result); + result +} + +pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bool { + assert!(t.is_normalized_for_trans()); + + let t = tcx.erase_regions(&t); + + // FIXME (#22815): note that type_needs_drop conservatively + // approximates in some cases and may say a type expression + // requires drop glue when it actually does not. + // + // (In this case it is not clear whether any harm is done, i.e. + // erroneously returning `true` in some cases where we could have + // returned `false` does not appear unsound. The impact on + // code quality is unknown at this time.) + + let env = tcx.empty_parameter_environment(); + if !tcx.type_needs_drop_given_env(t, &env) { + return false; + } + match t.sty { + ty::TyAdt(def, _) if def.is_box() => { + let typ = t.boxed_ty(); + if !tcx.type_needs_drop_given_env(typ, &env) && type_is_sized(tcx, typ) { + tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { + let layout = t.layout(&infcx).unwrap(); + if layout.size(&tcx.data_layout).bytes() == 0 { + // `Box` does not allocate. + false + } else { + true + } + }) + } else { + true + } + } + _ => true + } +} + +fn resolve_associated_item<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + trait_item: &ty::AssociatedItem, + trait_id: DefId, + rcvr_substs: &'tcx Substs<'tcx> +) -> ty::Instance<'tcx> { + let def_id = trait_item.def_id; + debug!("resolve_associated_item(trait_item={:?}, \ + trait_id={:?}, \ + rcvr_substs={:?})", + def_id, trait_id, rcvr_substs); + + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); + let vtbl = fulfill_obligation(tcx, DUMMY_SP, ty::Binder(trait_ref)); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + ::rustc::traits::VtableImpl(impl_data) => { + let (def_id, substs) = ::rustc::traits::find_associated_item( + tcx, trait_item, rcvr_substs, &impl_data); + let substs = tcx.erase_regions(&substs); + ty::Instance::new(def_id, substs) + } + ::rustc::traits::VtableClosure(closure_data) => { + let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap(); + resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs, + trait_closure_kind) + } + ::rustc::traits::VtableFnPointer(ref data) => { + ty::Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs + } + } + ::rustc::traits::VtableObject(ref data) => { + let index = tcx.get_vtable_index_of_object_method(data, def_id); + ty::Instance { + def: ty::InstanceDef::Virtual(def_id, index), + substs: rcvr_substs + } + } + _ => { + bug!("static call to invalid vtable: {:?}", vtbl) + } + } +} + +pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> Ty<'tcx> +{ + let ty = tcx.item_type(def_id); + apply_param_substs(tcx, substs, &ty) +} + +/// Monomorphizes a type from the AST by first applying the in-scope +/// substitutions and then normalizing any associated types. +pub fn apply_param_substs<'a, 'tcx, T>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + param_substs: &Substs<'tcx>, + value: &T) + -> T + where T: ::rustc::infer::TransNormalize<'tcx> +{ + debug!("apply_param_substs(param_substs={:?}, value={:?})", param_substs, value); + let substituted = value.subst(tcx, param_substs); + let substituted = tcx.erase_regions(&substituted); + AssociatedTypeNormalizer{ tcx }.fold(&substituted) +} + + +struct AssociatedTypeNormalizer<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> AssociatedTypeNormalizer<'a, 'tcx> { + fn fold>(&mut self, value: &T) -> T { + if !value.has_projection_types() { + value.clone() + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNormalizer<'a, 'tcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_projection_types() { + ty + } else { + self.tcx.normalize_associated_type(&ty) + } + } +} + +fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + // generics are weird, don't run this function on a generic + assert!(!ty.needs_subst()); + ty.is_sized(tcx, &tcx.empty_parameter_environment(), DUMMY_SP) +} + +/// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we +/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should +/// guarantee to us that all nested obligations *could be* resolved if we wanted to. +fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + span: Span, + trait_ref: ty::PolyTraitRef<'tcx>) + -> traits::Vtable<'tcx, ()> +{ + // Remove any references to regions; this helps improve caching. + let trait_ref = tcx.erase_regions(&trait_ref); + + debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", + trait_ref, trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + let mut selcx = traits::SelectionContext::new(&infcx); + + let obligation_cause = traits::ObligationCause::misc(span, + ast::DUMMY_NODE_ID); + let obligation = traits::Obligation::new(obligation_cause, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + debug!("Encountered ambiguity selecting `{:?}` during trans, \ + presuming due to overflow", + trait_ref); + tcx.sess.span_fatal(span, + "reached the recursion limit during monomorphization \ + (selection ambiguity)"); + } + Err(e) => { + span_bug!(span, "Encountered error `{:?}` selecting `{:?}` during trans", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = traits::FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8e0623dcba176..8b5b88322e2e1 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; +use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -59,21 +60,44 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let func_ty = self.operand_ty(func); - let fn_def = match func_ty.sty { - ty::TyFnPtr(_) => { + let (fn_def, abi) = match func_ty.sty { + ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - self.memory.get_fn(fn_ptr.alloc_id)? + (self.memory.get_fn(fn_ptr.alloc_id)?, sig.abi()) }, - ty::TyFnDef(def_id, substs, _) => ty::Instance::new(def_id, substs), + ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig.abi()), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); } }; - self.eval_fn_call(fn_def, destination, args, terminator.source_info.span)?; + self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, abi)?; } - Drop { .. } => unreachable!(), + Drop { ref location, target, .. } => { + let lval = self.eval_lvalue(location)?; + + let ty = self.lvalue_ty(location); + + self.goto_block(target); + let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); + let env = self.tcx.empty_parameter_environment(); + let def = if self.tcx.type_needs_drop_given_env(ty, &env) { + ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) + } else { + ty::InstanceDef::DropGlue(drop_in_place, None) + }; + let substs = self.substs(); + let instance = ty::Instance { substs, def }; + let mir = self.load_mir(instance.def)?; + self.push_stack_frame( + instance, + terminator.source_info.span, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + } Assert { ref cond, expected, ref msg, target, .. } => { let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?; @@ -111,15 +135,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, + abi: Abi, ) -> EvalResult<'tcx> { - let sig = match instance.def.def_ty(self.tcx).sty { - ty::TyFnPtr(bare_sig) => bare_sig, - ty::TyFnDef(_, _, fn_ty) => fn_ty, - ref other => bug!("expected function or pointer, got {:?}", other), - }; - match sig.abi() { - Abi::RustIntrinsic => { - let sig = self.erase_lifetimes(&sig); + trace!("eval_fn_call: {:#?}", instance); + match instance.def { + ty::InstanceDef::Intrinsic(..) => { + unimplemented!(); + /*let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let layout = self.type_layout(ty)?; let (ret, target) = match destination { @@ -128,9 +150,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); - Ok(()) + Ok(())*/ }, - Abi::C => { + /*Abi::C => { let sig = self.erase_lifetimes(&sig); let ty = sig.output(); let (ret, target) = destination.unwrap(); @@ -142,14 +164,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.dump_local(ret); self.goto_block(target); Ok(()) - }, - Abi::Rust | Abi::RustCall => { + },*/ + ty::InstanceDef::ClosureOnceShim{..} => { let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } + assert_eq!(abi, Abi::RustCall); + self.eval_fn_call_inner( + instance, + destination, + args, + span, + ) + } + ty::InstanceDef::Item(_) => { + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + match abi { + Abi::C => unimplemented!(), + Abi::Rust => {}, + Abi::RustCall => self.unpack_fn_args(&mut args)?, + _ => unimplemented!(), + } self.eval_fn_call_inner( instance, destination, @@ -157,7 +200,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, - other => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", other))), + _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), } } @@ -168,7 +211,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args: Vec<(Value, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}", args, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", instance, args, destination); + + // Only trait methods can have a Self parameter. let mir = match self.load_mir(instance.def) { Ok(mir) => mir, @@ -392,4 +437,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // current frame. Ok(()) } + + pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { + if let Some((last, last_ty)) = args.pop() { + let last_layout = self.type_layout(last_ty)?; + match (&last_ty.sty, last_layout) { + (&ty::TyTuple(fields, _), + &Layout::Univariant { ref variant, .. }) => { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + match last { + Value::ByRef(last_ptr) => { + for (offset, ty) in offsets.zip(fields) { + let arg = Value::ByRef(last_ptr.offset(offset)); + args.push((arg, ty)); + } + }, + // propagate undefs + undef @ Value::ByVal(PrimVal::Undef) => { + for field_ty in fields { + args.push((undef, field_ty)); + } + }, + _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), + } + } + ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), + } + } + Ok(()) + } } From 030f00a8a1929a0f140f0f4ec14937efb720d6dd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 14:19:29 +0100 Subject: [PATCH 0875/1096] Fix drop terminator --- src/terminator/mod.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8b5b88322e2e1..53c22926438e4 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -60,22 +60,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let func_ty = self.operand_ty(func); - let (fn_def, abi) = match func_ty.sty { + let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - (self.memory.get_fn(fn_ptr.alloc_id)?, sig.abi()) + (self.memory.get_fn(fn_ptr.alloc_id)?, sig) }, - ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig.abi()), + ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); } }; - self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, abi)?; + let sig = self.erase_lifetimes(&sig); + self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, sig)?; } Drop { ref location, target, .. } => { let lval = self.eval_lvalue(location)?; + let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); @@ -97,6 +99,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, )?; + + let mut arg_locals = self.frame().mir.args_iter(); + assert_eq!(self.frame().mir.arg_count, 1); + let arg_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let arg_ty = self.tcx.mk_mut_ptr(ty); + self.write_value(Value::ByVal(PrimVal::Ptr(src_ptr)), dest, arg_ty)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -135,22 +144,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, arg_operands: &[mir::Operand<'tcx>], span: Span, - abi: Abi, + sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx> { trace!("eval_fn_call: {:#?}", instance); match instance.def { ty::InstanceDef::Intrinsic(..) => { - unimplemented!(); - /*let sig = self.erase_lifetimes(&sig); - let ty = sig.output(); - let layout = self.type_layout(ty)?; let (ret, target) = match destination { - Some(dest) if is_inhabited(self.tcx, ty) => dest, + Some(dest) => dest, _ => return Err(EvalError::Unreachable), }; + let ty = sig.output(); + if !is_inhabited(self.tcx, ty) { + return Err(EvalError::Unreachable); + } + let layout = self.type_layout(ty)?; self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?; self.dump_local(ret); - Ok(())*/ + Ok(()) }, /*Abi::C => { let sig = self.erase_lifetimes(&sig); @@ -172,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - assert_eq!(abi, Abi::RustCall); + assert_eq!(sig.abi, Abi::RustCall); self.eval_fn_call_inner( instance, destination, @@ -187,7 +197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - match abi { + match sig.abi { Abi::C => unimplemented!(), Abi::Rust => {}, Abi::RustCall => self.unpack_fn_args(&mut args)?, @@ -200,7 +210,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, - _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), + _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } } From 7c12ebc78dbe86896c66e8cfb4e6923184f89e24 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 16:16:23 +0100 Subject: [PATCH 0876/1096] Roll our own MIR for dropping arrays. --- src/eval_context.rs | 185 ++++++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 44 +++++----- 2 files changed, 206 insertions(+), 23 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 1ec690e620493..5fe66ee50c74c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -5,6 +5,7 @@ use std::fmt::Write; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; use rustc::middle::const_val::ConstVal; +use rustc_const_math::{ConstInt, ConstUsize}; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; @@ -44,6 +45,9 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: u64, + + /// Drop glue for arrays and slices + pub(crate) seq_drop_glue: MirRef<'tcx>, } /// A stack frame. @@ -124,6 +128,176 @@ impl Default for ResourceLimits { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { + let source_info = mir::SourceInfo { + span: DUMMY_SP, + scope: mir::ARGUMENT_VISIBILITY_SCOPE + }; + // i = 0; len = Len(*a0); goto head; + let start_block = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(2)), + mir::Rvalue::Use(mir::Operand::Constant(mir::Constant { + span: DUMMY_SP, + ty: tcx.types.usize, + literal: mir::Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + }, + })) + ) + }, + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(3)), + mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection { + base: mir::Lvalue::Local(mir::Local::new(1)), + elem: mir::ProjectionElem::Deref, + }))), + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, + }), + is_cleanup: false + }; + // head: done = i == len; switch done { 1 => ret, 0 => loop } + let head = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(4)), + mir::Rvalue::BinaryOp( + mir::BinOp::Eq, + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))), + ) + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::SwitchInt { + targets: vec![ + mir::BasicBlock::new(2), + mir::BasicBlock::new(4), + ], + discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))), + switch_ty: tcx.types.bool, + values: vec![ConstInt::U8(0)].into(), + }, + }), + is_cleanup: false + }; + // loop: drop (*a0)[i]; goto inc; + let loop_ = mir::BasicBlockData { + statements: Vec::new(), + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Drop { + target: mir::BasicBlock::new(3), + unwind: None, + location: mir::Lvalue::Projection(Box::new( + mir::LvalueProjection { + base: mir::Lvalue::Projection(Box::new( + mir::LvalueProjection { + base: mir::Lvalue::Local(mir::Local::new(1)), + elem: mir::ProjectionElem::Deref, + } + )), + elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))), + } + )), + }, + }), + is_cleanup: false + }; + // inc: i++; goto head; + let inc = mir::BasicBlockData { + statements: vec![ + mir::Statement { + source_info, + kind: mir::StatementKind::Assign( + mir::Lvalue::Local(mir::Local::new(2)), + mir::Rvalue::BinaryOp( + mir::BinOp::Add, + mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), + mir::Operand::Constant(mir::Constant { + span: DUMMY_SP, + ty: tcx.types.usize, + literal: mir::Literal::Value { + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + }, + }), + ) + ) + }, + ], + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, + }), + is_cleanup: false + }; + // ret: return; + let ret = mir::BasicBlockData { + statements: Vec::new(), + terminator: Some(mir::Terminator { + source_info: source_info, + kind: mir::TerminatorKind::Return, + }), + is_cleanup: false + }; + let locals = vec![ + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.mk_nil(), + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.mk_mut_ptr(tcx.mk_self_type()), + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.usize, + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.usize, + name: None, + source_info: None, + }, + mir::LocalDecl { + mutability: mir::Mutability::Mut, + ty: tcx.types.bool, + name: None, + source_info: None, + }, + ]; + let seq_drop_glue = mir::Mir::new( + vec![start_block, head, loop_, inc, ret].into_iter().collect(), + Vec::new().into_iter().collect(), // vis scopes + Vec::new().into_iter().collect(), // promoted + tcx.mk_nil(), // return type + locals.into_iter().collect(), + 1, // arg_count + Vec::new(), // upvars + DUMMY_SP, + ); + let seq_drop_glue = tcx.alloc_mir(seq_drop_glue); + // Perma-borrow MIR from shims to prevent mutation. + ::std::mem::forget(seq_drop_glue.borrow()); EvalContext { tcx, memory: Memory::new(&tcx.data_layout, limits.memory_size), @@ -131,6 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, + seq_drop_glue: seq_drop_glue.borrow(), } } @@ -1958,3 +2133,13 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, vtable }) } + +pub fn resolve_drop_in_place<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + ty: Ty<'tcx>, +) -> ty::Instance<'tcx> +{ + let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem); + let substs = tcx.intern_substs(&[Kind::from(ty)]); + resolve(tcx, def_id, substs) +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 53c22926438e4..8258395c402de 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -80,18 +80,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); + let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); self.goto_block(target); - let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); - let env = self.tcx.empty_parameter_environment(); - let def = if self.tcx.type_needs_drop_given_env(ty, &env) { - ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) - } else { - ty::InstanceDef::DropGlue(drop_in_place, None) + let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + + if let ty::InstanceDef::DropGlue(_, None) = instance.def { + // we don't actually need to drop anything + return Ok(()); + } + + let mir = match ty.sty { + ty::TyDynamic(..) => unimplemented!(), + ty::TyArray(..) | ty::TySlice(..) => ::eval_context::MirRef::clone(&self.seq_drop_glue), + _ => self.load_mir(instance.def)?, }; - let substs = self.substs(); - let instance = ty::Instance { substs, def }; - let mir = self.load_mir(instance.def)?; + self.push_stack_frame( instance, terminator.source_info.span, @@ -162,19 +166,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.dump_local(ret); Ok(()) }, - /*Abi::C => { - let sig = self.erase_lifetimes(&sig); - let ty = sig.output(); - let (ret, target) = destination.unwrap(); - match instance.def { - ty::InstanceDef::Item(_) => {}, - _ => bug!("C abi function must be InstanceDef::Item"), - } - self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; - self.dump_local(ret); - self.goto_block(target); - Ok(()) - },*/ ty::InstanceDef::ClosureOnceShim{..} => { let mut args = Vec::new(); for arg in arg_operands { @@ -198,7 +189,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((arg_val, arg_ty)); } match sig.abi { - Abi::C => unimplemented!(), + Abi::C => { + let ty = sig.output(); + let (ret, target) = destination.unwrap(); + self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; + self.dump_local(ret); + self.goto_block(target); + return Ok(()); + }, Abi::Rust => {}, Abi::RustCall => self.unpack_fn_args(&mut args)?, _ => unimplemented!(), From 9e4e6cdb5c89f68fce06eb8b716ce0068ed8f817 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:32:20 +0100 Subject: [PATCH 0877/1096] Dropping arrays works again --- src/eval_context.rs | 5 +++-- src/terminator/mod.rs | 27 ++++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 5fe66ee50c74c..bf847051d8ab5 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -16,6 +16,7 @@ use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast; use syntax::abi::Abi; +use syntax::symbol::Symbol; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -231,7 +232,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: DUMMY_SP, ty: tcx.types.usize, literal: mir::Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), + value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())), }, }), ) @@ -262,7 +263,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, mir::LocalDecl { mutability: mir::Mutability::Mut, - ty: tcx.mk_mut_ptr(tcx.mk_self_type()), + ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))), name: None, source_info: None, }, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 8258395c402de..ed178f6de0d78 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,6 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; +use rustc::ty::subst::Kind; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; @@ -76,24 +77,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Drop { ref location, target, .. } => { + trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; + trace!("drop lval: {:#?}", lval); let src_ptr = self.force_allocation(lval)?.to_ptr(); - let ty = self.lvalue_ty(location); + self.goto_block(target); + let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); - self.goto_block(target); - let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let mut instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); if let ty::InstanceDef::DropGlue(_, None) = instance.def { // we don't actually need to drop anything return Ok(()); } - + let arg; let mir = match ty.sty { ty::TyDynamic(..) => unimplemented!(), - ty::TyArray(..) | ty::TySlice(..) => ::eval_context::MirRef::clone(&self.seq_drop_glue), - _ => self.load_mir(instance.def)?, + ty::TyArray(elem, n) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + ty::TySlice(ref elem) => unimplemented!(), + _ => { + arg = Value::ByVal(PrimVal::Ptr(src_ptr)); + self.load_mir(instance.def)? + }, }; self.push_stack_frame( @@ -109,7 +122,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = arg_locals.next().unwrap(); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; let arg_ty = self.tcx.mk_mut_ptr(ty); - self.write_value(Value::ByVal(PrimVal::Ptr(src_ptr)), dest, arg_ty)?; + self.write_value(arg, dest, arg_ty)?; } Assert { ref cond, expected, ref msg, target, .. } => { From c4090794420b789bc8566fc7757711c9019ea096 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:48:16 +0100 Subject: [PATCH 0878/1096] Dropping trait objects works again --- src/terminator/mod.rs | 21 ++++++++++++++++++--- src/traits.rs | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ed178f6de0d78..5b4215dec17e9 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; -use lvalue::Lvalue; +use lvalue::{Lvalue, LvalueExtra}; use memory::Pointer; use value::PrimVal; use value::Value; @@ -80,7 +80,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; trace!("drop lval: {:#?}", lval); - let src_ptr = self.force_allocation(lval)?.to_ptr(); let ty = self.lvalue_ty(location); self.goto_block(target); @@ -94,16 +93,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let arg; let mir = match ty.sty { - ty::TyDynamic(..) => unimplemented!(), + ty::TyDynamic(..) => { + if let Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } = lval { + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)); + match self.read_drop_type_from_vtable(vtable)? { + Some(func) => { + instance = func; + self.load_mir(func.def)? + }, + // no drop fn -> bail out + None => return Ok(()), + } + } else { + panic!("expected fat lvalue, got {:?}", lval); + } + }, ty::TyArray(elem, n) => { instance.substs = self.tcx.mk_substs([ Kind::from(elem), ].iter().cloned()); + let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); ::eval_context::MirRef::clone(&self.seq_drop_glue) }, ty::TySlice(ref elem) => unimplemented!(), _ => { + let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByVal(PrimVal::Ptr(src_ptr)); self.load_mir(instance.def)? }, diff --git a/src/traits.rs b/src/traits.rs index 74cb6966c067b..813b19c3dadc9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -146,6 +146,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } + pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { + let drop_fn = self.memory.read_ptr(vtable)?; + + // just a sanity check + assert_eq!(drop_fn.offset, 0); + + // some values don't need to call a drop impl, so the value is null + if drop_fn == Pointer::from_int(0) { + Ok(None) + } else { + self.memory.get_fn(drop_fn.alloc_id).map(Some) + } + } + pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_usize(vtable.offset(pointer_size))?; From 3ef0b0de2c8018037d2efcba547bab1b84e25419 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 17:51:43 +0100 Subject: [PATCH 0879/1096] Dropping slices works again --- src/terminator/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 5b4215dec17e9..a7dcb614bbb92 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -116,7 +116,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); ::eval_context::MirRef::clone(&self.seq_drop_glue) }, - ty::TySlice(ref elem) => unimplemented!(), + ty::TySlice(elem) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + if let Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } = lval { + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + } else { + panic!("slice without length: {:?}", lval); + } + }, _ => { let src_ptr = self.force_allocation(lval)?.to_ptr(); arg = Value::ByVal(PrimVal::Ptr(src_ptr)); From caed365dbe29f31ec477eb82128c98a4771c6983 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 18:31:41 +0100 Subject: [PATCH 0880/1096] Refactor drop into its own module and fix Vec --- src/terminator/drop.rs | 82 +++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 87 ++++++++++-------------------------------- 2 files changed, 102 insertions(+), 67 deletions(-) create mode 100644 src/terminator/drop.rs diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs new file mode 100644 index 0000000000000..bc37730b71438 --- /dev/null +++ b/src/terminator/drop.rs @@ -0,0 +1,82 @@ +use rustc::mir; +use rustc::ty::{self, Ty}; +use rustc::ty::subst::Kind; +use syntax::codemap::Span; + +use error::EvalResult; +use eval_context::{EvalContext, StackPopCleanup}; +use lvalue::{Lvalue, LvalueExtra}; +use memory::Pointer; +use value::PrimVal; +use value::Value; + +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { + trace!("drop_lvalue: {:#?}", lval); + let val = match self.force_allocation(lval)? { + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(PrimVal::Ptr(ptr)), + _ => bug!("force_allocation broken"), + }; + self.drop(val, instance, ty, span) + } + pub(crate) fn drop(&mut self, mut arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { + trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def); + + if let ty::InstanceDef::DropGlue(_, None) = instance.def { + trace!("nothing to do, aborting"); + // we don't actually need to drop anything + return Ok(()); + } + let mir = match ty.sty { + ty::TyDynamic(..) => { + let vtable = match arg { + Value::ByValPair(_, PrimVal::Ptr(vtable)) => vtable, + _ => bug!("expected fat ptr, got {:?}", arg), + }; + match self.read_drop_type_from_vtable(vtable)? { + Some(func) => { + instance = func; + self.load_mir(func.def)? + }, + // no drop fn -> bail out + None => return Ok(()), + } + }, + ty::TyArray(elem, n) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + let ptr = match arg { + Value::ByVal(PrimVal::Ptr(src_ptr)) => src_ptr, + _ => bug!("expected thin ptr, got {:?}", arg), + }; + arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128)); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + ty::TySlice(elem) => { + instance.substs = self.tcx.mk_substs([ + Kind::from(elem), + ].iter().cloned()); + ::eval_context::MirRef::clone(&self.seq_drop_glue) + }, + _ => self.load_mir(instance.def)?, + }; + + self.push_stack_frame( + instance, + span, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + + let mut arg_locals = self.frame().mir.args_iter(); + assert_eq!(self.frame().mir.arg_count, 1); + let arg_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let arg_ty = self.tcx.mk_mut_ptr(ty); + self.write_value(arg, dest, arg_ty) + } +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a7dcb614bbb92..6a615689ae7ed 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -2,18 +2,18 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::Layout; -use rustc::ty::subst::Kind; use syntax::codemap::Span; use syntax::attr; use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; -use lvalue::{Lvalue, LvalueExtra}; +use lvalue::Lvalue; use memory::Pointer; use value::PrimVal; use value::Value; +mod drop; mod intrinsic; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -79,75 +79,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Drop { ref location, target, .. } => { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); let lval = self.eval_lvalue(location)?; - trace!("drop lval: {:#?}", lval); let ty = self.lvalue_ty(location); self.goto_block(target); - let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty); - let mut instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); - - if let ty::InstanceDef::DropGlue(_, None) = instance.def { - // we don't actually need to drop anything - return Ok(()); - } - let arg; - let mir = match ty.sty { - ty::TyDynamic(..) => { - if let Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } = lval { - arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)); - match self.read_drop_type_from_vtable(vtable)? { - Some(func) => { - instance = func; - self.load_mir(func.def)? - }, - // no drop fn -> bail out - None => return Ok(()), - } - } else { - panic!("expected fat lvalue, got {:?}", lval); - } - }, - ty::TyArray(elem, n) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - let src_ptr = self.force_allocation(lval)?.to_ptr(); - arg = Value::ByValPair(PrimVal::Ptr(src_ptr), PrimVal::Bytes(n as u128)); - ::eval_context::MirRef::clone(&self.seq_drop_glue) - }, - ty::TySlice(elem) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - if let Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } = lval { - arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)); - ::eval_context::MirRef::clone(&self.seq_drop_glue) - } else { - panic!("slice without length: {:?}", lval); - } - }, - _ => { - let src_ptr = self.force_allocation(lval)?.to_ptr(); - arg = Value::ByVal(PrimVal::Ptr(src_ptr)); - self.load_mir(instance.def)? - }, - }; - - self.push_stack_frame( - instance, - terminator.source_info.span, - mir, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - )?; - - let mut arg_locals = self.frame().mir.args_iter(); - assert_eq!(self.frame().mir.arg_count, 1); - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - let arg_ty = self.tcx.mk_mut_ptr(ty); - self.write_value(arg, dest, arg_ty)?; + let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty); + self.drop_lvalue(lval, instance, ty, terminator.source_info.span)?; } Assert { ref cond, expected, ref msg, target, .. } => { @@ -246,6 +183,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span, ) }, + ty::InstanceDef::DropGlue(..) => { + assert_eq!(arg_operands.len(), 1); + assert_eq!(sig.abi, Abi::Rust); + let val = self.eval_operand(&arg_operands[0])?; + let ty = self.operand_ty(&arg_operands[0]); + let (_, target) = destination.expect("diverging drop glue"); + self.goto_block(target); + // FIXME: deduplicate these matches + let pointee_type = match ty.sty { + ty::TyRawPtr(ref tam) | + ty::TyRef(_, ref tam) => tam.ty, + ty::TyAdt(ref def, _) if def.is_box() => ty.boxed_ty(), + _ => bug!("can only deref pointer types"), + }; + self.drop(val, instance, pointee_type, span) + } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } } From f0bca59ad075e0731347365f73c13e7f3f498948 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 22 Mar 2017 18:59:26 -0400 Subject: [PATCH 0881/1096] remove feature opt-ins that are no longer needed --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d1d8e8cf229f6..c55b4538005a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ #![feature( - btree_range, - collections, i128_type, - pub_restricted, rustc_private, )] From 0255a5146874f4e340d470af0f6dd96aa5afe8a8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 10:04:08 +0100 Subject: [PATCH 0882/1096] Fix function pointer calls --- src/terminator/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6a615689ae7ed..157fc12a8ce08 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -198,6 +198,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("can only deref pointer types"), }; self.drop(val, instance, pointee_type, span) + }, + ty::InstanceDef::FnPtrShim(..) => { + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + match sig.abi { + Abi::Rust => { + args.remove(0); + }, + Abi::RustCall => {}, + _ => unimplemented!(), + } + trace!("ABI: {}", sig.abi); + self.eval_fn_call_inner( + instance, + destination, + args, + span, + ) } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } From 70ea218d2be5024f9ff2978facb20f9ba9aad6c9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 13:35:19 +0100 Subject: [PATCH 0883/1096] Reuse more rustc code instead of copying it into miri --- src/traits.rs | 231 +++----------------------------------------------- 1 file changed, 12 insertions(+), 219 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index 813b19c3dadc9..77541a5b70fb4 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,12 +1,11 @@ -use rustc::traits::{self, Reveal, SelectionContext}; +use rustc::traits::{self, Reveal}; use eval_context::EvalContext; use memory::Pointer; use rustc::hir::def_id::DefId; -use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty}; use syntax::codemap::DUMMY_SP; use syntax::ast; @@ -43,101 +42,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { - let tcx = self.tcx; - debug!("get_vtable(trait_ref={:?})", trait_ref); - let methods: Vec<_> = traits::supertraits(tcx, trait_ref).flat_map(|trait_ref| { - match self.fulfill_obligation(trait_ref) { - // Should default trait error here? - traits::VtableDefaultImpl(_) | - traits::VtableBuiltin(_) => { - Vec::new().into_iter() - } - - traits::VtableImpl(traits::VtableImplData { impl_def_id: id, substs, .. }) => { - self.get_vtable_methods(id, substs) - .into_iter() - .map(|opt_mth| opt_mth.map(|mth| { - self.memory.create_fn_ptr(mth.method.def_id, mth.substs) - })) - .collect::>() - .into_iter() - } - - traits::VtableClosure( - traits::VtableClosureData { - closure_def_id, - substs, - .. - } - ) => { - let instance = ::eval_context::resolve_closure(self.tcx, closure_def_id, substs, ty::ClosureKind::FnOnce); - vec![Some(self.memory.create_fn_alloc(instance))].into_iter() - } - - // turn a function definition into a Fn trait object - traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => { - match fn_ty.sty { - ty::TyFnDef(did, substs, _) => { - let instance = ty::Instance { - def: ty::InstanceDef::FnPtrShim(did, fn_ty), - substs, - }; - vec![Some(self.memory.create_fn_alloc(instance))].into_iter() - }, - ty::TyFnPtr(_) => { - unimplemented!(); - }, - _ => bug!("bad VtableFnPointer fn_ty: {:#?}", fn_ty.sty), - } - } - - traits::VtableObject(ref data) => { - // this would imply that the Self type being erased is - // an object type; this cannot happen because we - // cannot cast an unsized type into a trait object - bug!("cannot get vtable for an object type: {:?}", - data); - } - - vtable @ traits::VtableParam(..) => { - bug!("resolved vtable for {:?} to bad vtable {:?} in trans", - trait_ref, - vtable); - } - } - }).collect(); - let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type"); let align = self.type_align(trait_ref.self_ty())?; let ptr_size = self.memory.pointer_size(); - let vtable = self.memory.allocate(ptr_size * (3 + methods.len() as u64), ptr_size)?; + let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref); + let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size)?; - // in case there is no drop function to be called, this still needs to be initialized - self.memory.write_usize(vtable, 0)?; - let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available"); - if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty { - if adt_def.has_dtor(self.tcx) { - let env = self.tcx.empty_parameter_environment(); - let def = if self.tcx.type_needs_drop_given_env(ty, &env) { - ty::InstanceDef::DropGlue(drop_in_place, Some(ty)) - } else { - ty::InstanceDef::DropGlue(drop_in_place, None) - }; - let instance = ty::Instance { substs, def }; - let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable, fn_ptr)?; - } - } + let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty); + let drop = self.memory.create_fn_alloc(drop); + self.memory.write_ptr(vtable, drop)?; self.memory.write_usize(vtable.offset(ptr_size), size)?; self.memory.write_usize(vtable.offset(ptr_size * 2), align)?; - for (i, method) in methods.into_iter().enumerate() { - if let Some(method) = method { - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), method)?; + for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { + if let Some((def_id, substs)) = method { + let instance = ::eval_context::resolve(self.tcx, def_id, substs); + let fn_ptr = self.memory.create_fn_alloc(instance); + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), fn_ptr)?; } } @@ -167,94 +92,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((size, align)) } - fn get_vtable_methods(&mut self, impl_id: DefId, substs: &'tcx Substs<'tcx>) -> Vec>> { - debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); - - let trait_id = match self.tcx.impl_trait_ref(impl_id) { - Some(t_id) => t_id.def_id, - None => bug!("make_impl_vtable: don't know how to \ - make a vtable for a type impl!") - }; - - self.tcx.populate_implementations_for_trait_if_necessary(trait_id); - - self.tcx - .associated_items(trait_id) - // Filter out non-method items. - .filter_map(|trait_method_type| { - if trait_method_type.kind != ty::AssociatedKind::Method { - return None; - } - debug!("get_vtable_methods: trait_method_type={:?}", - trait_method_type); - - let name = trait_method_type.name; - - // Some methods cannot be called on an object; skip those. - if !self.tcx.is_vtable_safe_method(trait_id, &trait_method_type) { - debug!("get_vtable_methods: not vtable safe"); - return Some(None); - } - - debug!("get_vtable_methods: trait_method_type={:?}", - trait_method_type); - - // the method may have some early-bound lifetimes, add - // regions for those - let method_substs = Substs::for_item(self.tcx, trait_method_type.def_id, - |_, _| self.tcx.mk_region(ty::ReErased), - |_, _| self.tcx.types.err); - - // The substitutions we have are on the impl, so we grab - // the method type from the impl to substitute into. - let mth = get_impl_method(self.tcx, method_substs, impl_id, substs, name); - - debug!("get_vtable_methods: mth={:?}", mth); - - // If this is a default method, it's possible that it - // relies on where clauses that do not hold for this - // particular set of type parameters. Note that this - // method could then never be called, so we do not want to - // try and trans it, in that case. Issue #23435. - if mth.is_provided { - let predicates = self.tcx.item_predicates(trait_method_type.def_id).instantiate_own(self.tcx, mth.substs); - if !self.normalize_and_test_predicates(predicates.predicates) { - debug!("get_vtable_methods: predicates do not hold"); - return Some(None); - } - } - - Some(Some(mth)) - }) - .collect() - } - - /// Normalizes the predicates and checks whether they hold. If this - /// returns false, then either normalize encountered an error or one - /// of the predicates did not hold. Used when creating vtables to - /// check for unsatisfiable methods. - fn normalize_and_test_predicates(&mut self, predicates: Vec>) -> bool { - debug!("normalize_and_test_predicates(predicates={:?})", - predicates); - - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let mut fulfill_cx = traits::FulfillmentContext::new(); - let cause = traits::ObligationCause::dummy(); - let traits::Normalized { value: predicates, obligations } = - traits::normalize(&mut selcx, cause.clone(), &predicates); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - for predicate in predicates { - let obligation = traits::Obligation::new(cause.clone(), predicate); - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - - fulfill_cx.select_all_or_error(&infcx).is_ok() - }) - } - pub(crate) fn resolve_associated_const( &self, def_id: DefId, @@ -275,47 +112,3 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::Instance::new(def_id, substs) } } - -#[derive(Debug)] -pub(super) struct ImplMethod<'tcx> { - pub(super) method: ty::AssociatedItem, - pub(super) substs: &'tcx Substs<'tcx>, - pub(super) is_provided: bool, -} - -/// Locates the applicable definition of a method, given its name. -pub(super) fn get_impl_method<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - impl_def_id: DefId, - impl_substs: &'tcx Substs<'tcx>, - name: ast::Name, -) -> ImplMethod<'tcx> { - assert!(!substs.needs_infer()); - - let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); - let trait_def = tcx.lookup_trait_def(trait_def_id); - - match trait_def.ancestors(impl_def_id).defs(tcx, name, ty::AssociatedKind::Method).next() { - Some(node_item) => { - let substs = tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - let substs = substs.rebase_onto(tcx, trait_def_id, impl_substs); - let substs = traits::translate_substs(&infcx, impl_def_id, - substs, node_item.node); - tcx.lift(&substs).unwrap_or_else(|| { - bug!("trans::meth::get_impl_method: translate_substs \ - returned {:?} which contains inference types/regions", - substs); - }) - }); - ImplMethod { - method: node_item.item, - substs, - is_provided: node_item.node.is_from_trait(), - } - } - None => { - bug!("method {:?} not found in {:?}", name, impl_def_id) - } - } -} From d70b79c778843ef115103156ccbeed8d2ff4d91f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 13:36:13 +0100 Subject: [PATCH 0884/1096] Refactor function calls --- src/terminator/mod.rs | 147 +++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 157fc12a8ce08..84c76dd1204f8 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -12,6 +12,7 @@ use lvalue::Lvalue; use memory::Pointer; use value::PrimVal; use value::Value; +use rustc_data_structures::indexed_vec::Idx; mod drop; mod intrinsic; @@ -148,21 +149,38 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - assert_eq!(sig.abi, Abi::RustCall); self.eval_fn_call_inner( instance, destination, - args, span, - ) + )?; + let mut arg_locals = self.frame().mir.args_iter(); + match sig.abi { + // closure as closure once + Abi::RustCall => { + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; + } + }, + // non capture closure as fn ptr + // need to inject zst ptr for closure object (aka do nothing) + // and need to pack arguments + Abi::Rust => { + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); + let local = arg_locals.nth(1).unwrap(); + for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() { + let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?; + self.write_value(arg_val, dest, arg_ty)?; + } + + }, + _ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi), + } + Ok(()) } ty::InstanceDef::Item(_) => { - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } match sig.abi { Abi::C => { let ty = sig.output(); @@ -172,16 +190,59 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(target); return Ok(()); }, - Abi::Rust => {}, - Abi::RustCall => self.unpack_fn_args(&mut args)?, + Abi::Rust | Abi::RustCall => {}, _ => unimplemented!(), } + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + self.eval_fn_call_inner( instance, destination, - args, span, - ) + )?; + + let mut arg_locals = self.frame().mir.args_iter(); + match sig.abi { + Abi::Rust => { + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; + } + } + Abi::RustCall => { + assert_eq!(args.len(), 2); + + { // write first argument + let first_local = arg_locals.next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; + let (arg_val, arg_ty) = args.remove(0); + self.write_value(arg_val, dest, arg_ty)?; + } + + // unpack and write all other args + let (arg_val, arg_ty) = args.remove(0); + let layout = self.type_layout(arg_ty)?; + if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } + } + } else { + bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); + } + } + _ => unimplemented!(), + } + Ok(()) }, ty::InstanceDef::DropGlue(..) => { assert_eq!(arg_operands.len(), 1); @@ -200,26 +261,31 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.drop(val, instance, pointee_type, span) }, ty::InstanceDef::FnPtrShim(..) => { + trace!("ABI: {}", sig.abi); let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } + self.eval_fn_call_inner( + instance, + destination, + span, + )?; + let arg_locals = self.frame().mir.args_iter(); match sig.abi { Abi::Rust => { args.remove(0); }, Abi::RustCall => {}, _ => unimplemented!(), + }; + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; } - trace!("ABI: {}", sig.abi); - self.eval_fn_call_inner( - instance, - destination, - args, - span, - ) + Ok(()) } _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), } @@ -229,10 +295,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, - args: Vec<(Value, Ty<'tcx>)>, span: Span, ) -> EvalResult<'tcx> { - trace!("eval_fn_call_inner: {:#?}, {:#?}, {:#?}", instance, args, destination); + trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); // Only trait methods can have a Self parameter. @@ -281,13 +346,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, )?; - let arg_locals = self.frame().mir.args_iter(); - assert_eq!(self.frame().mir.arg_count, args.len()); - for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg_val, dest, arg_ty)?; - } - Ok(()) } @@ -457,34 +515,5 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // as if the call just completed and it's returning to the // current frame. Ok(()) - } - - pub(crate) fn unpack_fn_args(&self, args: &mut Vec<(Value, Ty<'tcx>)>) -> EvalResult<'tcx> { - if let Some((last, last_ty)) = args.pop() { - let last_layout = self.type_layout(last_ty)?; - match (&last_ty.sty, last_layout) { - (&ty::TyTuple(fields, _), - &Layout::Univariant { ref variant, .. }) => { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - match last { - Value::ByRef(last_ptr) => { - for (offset, ty) in offsets.zip(fields) { - let arg = Value::ByRef(last_ptr.offset(offset)); - args.push((arg, ty)); - } - }, - // propagate undefs - undef @ Value::ByVal(PrimVal::Undef) => { - for field_ty in fields { - args.push((undef, field_ty)); - } - }, - _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), - } - } - ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), - } - } - Ok(()) - } + } } From ad4f6b920a47ab112fa249693c819c32988add32 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 14:24:02 +0100 Subject: [PATCH 0885/1096] Fix virtual function calls --- src/terminator/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 84c76dd1204f8..a6e0e53c2ded9 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -174,7 +174,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?; self.write_value(arg_val, dest, arg_ty)?; } - }, _ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi), } @@ -286,8 +285,60 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(arg_val, dest, arg_ty)?; } Ok(()) - } - _ => Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", sig.abi))), + }, + ty::InstanceDef::Virtual(_, idx) => { + trace!("ABI: {:?}", sig.abi); + let mut args = Vec::new(); + for arg in arg_operands { + let arg_val = self.eval_operand(arg)?; + let arg_ty = self.operand_ty(arg); + args.push((arg_val, arg_ty)); + } + let ptr_size = self.memory.pointer_size(); + let (_, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; + // FIXME: do we need to rewrite args[0] to be a thin ptr? + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; + let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + self.eval_fn_call_inner( + instance, + destination, + span, + )?; + match sig.abi { + Abi::RustCall => { + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); + trace!("args: {:#?}", args); + + assert_eq!(args.len(), 2); + + { // write first argument + let first_local = self.frame().mir.args_iter().next().unwrap(); + let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; + let (arg_val, arg_ty) = args.remove(0); + self.write_value(arg_val, dest, arg_ty)?; + } + + // unpack and write all other args + let (arg_val, arg_ty) = args.remove(0); + let layout = self.type_layout(arg_ty)?; + if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(self.frame().mir.args_iter().skip(1)) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } + } + } else { + bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); + } + }, + _ => unimplemented!(), + } + Ok(()) + }, } } From d71f24c00f64cdf35f7b3cd5fd5635185b8b8a0b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 14:57:11 +0100 Subject: [PATCH 0886/1096] Fix virtual function calls --- src/terminator/mod.rs | 51 +++++-------------------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a6e0e53c2ded9..fa74e57132035 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -287,57 +287,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) }, ty::InstanceDef::Virtual(_, idx) => { - trace!("ABI: {:?}", sig.abi); - let mut args = Vec::new(); - for arg in arg_operands { - let arg_val = self.eval_operand(arg)?; - let arg_ty = self.operand_ty(arg); - args.push((arg_val, arg_ty)); - } let ptr_size = self.memory.pointer_size(); - let (_, vtable) = args[0].0.expect_ptr_vtable_pair(&self.memory)?; - // FIXME: do we need to rewrite args[0] to be a thin ptr? + let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; - self.eval_fn_call_inner( + // recurse with concrete function + self.eval_fn_call( instance, destination, + arg_operands, span, - )?; - match sig.abi { - Abi::RustCall => { - trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); - trace!("arg_operands: {:?}", arg_operands); - trace!("args: {:#?}", args); - - assert_eq!(args.len(), 2); - - { // write first argument - let first_local = self.frame().mir.args_iter().next().unwrap(); - let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?; - let (arg_val, arg_ty) = args.remove(0); - self.write_value(arg_val, dest, arg_ty)?; - } - - // unpack and write all other args - let (arg_val, arg_ty) = args.remove(0); - let layout = self.type_layout(arg_ty)?; - if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(self.frame().mir.args_iter().skip(1)) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; - } - } - } else { - bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); - } - }, - _ => unimplemented!(), - } - Ok(()) + sig, + ) }, } } From 4e83659b1de9dad88a034fdc07e30921701761af Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:07:33 +0100 Subject: [PATCH 0887/1096] Fix manual rust-call impls --- src/terminator/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index fa74e57132035..de3742caf131b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -227,13 +227,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (arg_val, arg_ty) = args.remove(0); let layout = self.type_layout(arg_ty)?; if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { - let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; + if self.frame().mir.args_iter().count() == fields.len() + 1 { + let offsets = variant.offsets.iter().map(|s| s.bytes()); + if let Value::ByRef(ptr) = arg_val { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg, dest, ty)?; + } } + } else { + // called a manual impl of a rust-call function + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; + self.write_value(arg_val, dest, arg_ty)?; } } else { bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); From 1391c5a10abf89b0e5eb92dbc3f0c4f997525e98 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:17:02 +0100 Subject: [PATCH 0888/1096] Reintroduce fn ptr transmute check --- src/terminator/mod.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index de3742caf131b..4ea2c4d958781 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,7 +65,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - (self.memory.get_fn(fn_ptr.alloc_id)?, sig) + let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + match instance.def.def_ty(self.tcx).sty { + ty::TyFnDef(_, _, real_sig) => { + let sig = self.erase_lifetimes(&sig); + let real_sig = self.erase_lifetimes(&real_sig); + if sig.abi != real_sig.abi || + sig.variadic != real_sig.variadic || + sig.inputs_and_output != real_sig.inputs_and_output { + return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + } + }, + ref other => bug!("instance def ty: {:?}", other), + } + (instance, sig) }, ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig), _ => { From 1c9f5ac669ab3e709df43b453aa1e80d488e3416 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:23:53 +0100 Subject: [PATCH 0889/1096] Skip the transmute checks for closure glue --- src/terminator/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4ea2c4d958781..e49c4aa7387ba 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -70,10 +70,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, real_sig) => { let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); - if sig.abi != real_sig.abi || - sig.variadic != real_sig.variadic || - sig.inputs_and_output != real_sig.inputs_and_output { - return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + match instance.def { + // FIXME: this needs checks for weird transmutes + // we need to bail here, because noncapturing closures as fn ptrs fail the checks + ty::InstanceDef::ClosureOnceShim{..} => {} + _ => if sig.abi != real_sig.abi || + sig.variadic != real_sig.variadic || + sig.inputs_and_output != real_sig.inputs_and_output { + return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); + }, } }, ref other => bug!("instance def ty: {:?}", other), From eecc727e877630c7809e08f854c96233991ca89c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:52:02 +0100 Subject: [PATCH 0890/1096] Reduce noisyness --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index bf847051d8ab5..f81f352c8aa09 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -2130,7 +2130,7 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }); let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable); - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + debug!("Cache miss: {:?} => {:?}", trait_ref, vtable); vtable }) } From 197226aa2a5eac9a580c15fd35f1bb98723c3249 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 15:52:11 +0100 Subject: [PATCH 0891/1096] Try to fix travis --- .travis.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05fe244d04ffe..e6450622b2e88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,18 +2,16 @@ language: rust rust: - nightly before_script: -- | - pip install 'travis-cargo<0.2' --user && - export PATH=$HOME/.local/bin:$PATH -- sh ~/rust-installer/rustup.sh --add-target=i686-unknown-linux-gnu --prefix=/home/travis/rust -y --disable-sudo -- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-gnu --prefix=/home/travis/rust -y --disable-sudo -- sh ~/rust-installer/rustup.sh --add-target=i686-pc-windows-msvc --prefix=/home/travis/rust -y --disable-sudo +- export PATH=$HOME/.local/bin:$PATH +- rustup target add i686-unknown-linux-gnu +- rustup target add i686-pc-windows-gnu +- rustup target add i686-pc-windows-msvc script: - | export RUST_SYSROOT=$HOME/rust && - travis-cargo build && - travis-cargo test && - travis-cargo install && + cargo build && + cargo test && + cargo install && cd cargo-miri-test && cargo miri && cargo miri test && From fb7cc3c164f457e8724bb483b19e40aecb31329b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 16:09:36 +0100 Subject: [PATCH 0892/1096] Fix single field by val tuples --- src/terminator/mod.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e49c4aa7387ba..1712a0cc12626 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -224,6 +224,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; let mut arg_locals = self.frame().mir.args_iter(); + trace!("ABI: {:?}", sig.abi); + trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); + trace!("arg_operands: {:?}", arg_operands); match sig.abi { Abi::Rust => { for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { @@ -245,16 +248,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (arg_val, arg_ty) = args.remove(0); let layout = self.type_layout(arg_ty)?; if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) { + trace!("fields: {:?}", fields); if self.frame().mir.args_iter().count() == fields.len() + 1 { let offsets = variant.offsets.iter().map(|s| s.bytes()); - if let Value::ByRef(ptr) = arg_val { - for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)); - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg, dest, ty)?; + match arg_val { + Value::ByRef(ptr) => { + for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { + let arg = Value::ByRef(ptr.offset(offset)); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); + self.write_value(arg, dest, ty)?; + } + }, + Value::ByVal(PrimVal::Undef) => {}, + other => { + assert_eq!(fields.len(), 1); + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; + self.write_value(other, dest, fields[0])?; } } } else { + trace!("manual impl of rust-call ABI"); // called a manual impl of a rust-call function let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?; self.write_value(arg_val, dest, arg_ty)?; From 0927829e14686684f96799dd5944190b817145a5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 16:13:01 +0100 Subject: [PATCH 0893/1096] Add regression test for single field by val tuples --- tests/run-pass/issue-20575.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/run-pass/issue-20575.rs diff --git a/tests/run-pass/issue-20575.rs b/tests/run-pass/issue-20575.rs new file mode 100644 index 0000000000000..7db7e3b28e8e6 --- /dev/null +++ b/tests/run-pass/issue-20575.rs @@ -0,0 +1,19 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that overloaded calls work with zero arity closures + +// pretty-expanded FIXME #23616 + +fn main() { + let functions: [Box Option<()>>; 1] = [Box::new(|| None)]; + + let _: Option> = functions.iter().map(|f| (*f)()).collect(); +} From f4ed482c4d153eb61e451129ae48fa9cc39bbeff Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 17:36:10 +0100 Subject: [PATCH 0894/1096] `print` doesn't add a stack frame, so don't write arguments --- src/terminator/mod.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 1712a0cc12626..14f0652a2b574 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -167,11 +167,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let mut arg_locals = self.frame().mir.args_iter(); match sig.abi { // closure as closure once @@ -217,11 +219,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let mut arg_locals = self.frame().mir.args_iter(); trace!("ABI: {:?}", sig.abi); @@ -305,11 +309,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_ty = self.operand_ty(arg); args.push((arg_val, arg_ty)); } - self.eval_fn_call_inner( + if self.eval_fn_call_inner( instance, destination, span, - )?; + )? { + return Ok(()); + } let arg_locals = self.frame().mir.args_iter(); match sig.abi { Abi::Rust => { @@ -341,12 +347,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + /// Returns Ok(true) when the function was handled completely due to mir not being available fn eval_fn_call_inner( &mut self, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, span: Span, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx, bool> { trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); // Only trait methods can have a Self parameter. @@ -358,7 +365,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // let's just ignore all output for now "std::io::_print" => { self.goto_block(destination.unwrap().1); - return Ok(()); + return Ok(true); }, "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), @@ -371,7 +378,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let bool = self.tcx.types.bool; self.write_primval(lval, PrimVal::from_bool(false), bool)?; self.goto_block(block); - return Ok(()); + return Ok(true); } _ => {}, } @@ -396,7 +403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return_to_block, )?; - Ok(()) + Ok(false) } pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { From cb867d250ac4f1a1306d3835d461275ea037c065 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 17:57:40 +0100 Subject: [PATCH 0895/1096] Fix casting generic functions to concrete function pointers --- src/eval_context.rs | 3 ++- src/memory.rs | 7 ------- src/terminator/mod.rs | 4 +++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index f81f352c8aa09..5cd790797b471 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -825,7 +825,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ReifyFnPointer => match self.operand_ty(operand).sty { ty::TyFnDef(def_id, substs, _) => { - let fn_ptr = self.memory.create_fn_ptr(def_id, substs); + let instance = resolve(self.tcx, def_id, substs); + let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, ref other => bug!("reify fn pointer on {:?}", other), diff --git a/src/memory.rs b/src/memory.rs index 058f8b478cac4..3d46d710d3dd7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,9 +2,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet}; use std::{fmt, iter, ptr, mem, io}; -use rustc::hir::def_id::DefId; use rustc::ty; -use rustc::ty::subst::Substs; use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; @@ -176,11 +174,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> Pointer { - let instance = ty::Instance::new(def_id, substs); - self.create_fn_alloc(instance) - } - pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { return Pointer::new(alloc_id, 0); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 14f0652a2b574..060299983ed45 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -66,7 +66,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; - match instance.def.def_ty(self.tcx).sty { + let instance_ty = instance.def.def_ty(self.tcx); + let instance_ty = self.monomorphize(instance_ty, instance.substs); + match instance_ty.sty { ty::TyFnDef(_, _, real_sig) => { let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); From bbeb7216e0985b21c6288acd4f09de1bb0c250cc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 23 Mar 2017 18:32:57 +0100 Subject: [PATCH 0896/1096] Thinify the fat pointer on virtual function calls --- src/lvalue.rs | 6 ++++++ src/terminator/mod.rs | 9 +++++++- tests/run-pass/issue-3794.rs | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/issue-3794.rs diff --git a/src/lvalue.rs b/src/lvalue.rs index 62855c0450579..b78824ea5c9e7 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -200,6 +200,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Size::from_bytes(field * elem_size), false) } + FatPointer { .. } => { + let bytes = field_index as u64 * self.memory.pointer_size(); + let offset = Size::from_bytes(bytes); + (offset, false) + } + _ => bug!("field access on non-product type: {:?}", base_layout), }; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 060299983ed45..03804ccd0bd51 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -337,11 +337,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + let mut arg_operands = arg_operands.to_vec(); + let ty = self.operand_ty(&arg_operands[0]); + let ty = self.get_field_ty(ty, 0)?; + match arg_operands[0] { + mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty), + _ => bug!("virtual call first arg cannot be a constant"), + } // recurse with concrete function self.eval_fn_call( instance, destination, - arg_operands, + &arg_operands, span, sig, ) diff --git a/tests/run-pass/issue-3794.rs b/tests/run-pass/issue-3794.rs new file mode 100644 index 0000000000000..badb833ee800b --- /dev/null +++ b/tests/run-pass/issue-3794.rs @@ -0,0 +1,41 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax)] + +trait T { + fn print(&self); +} + +#[derive(Debug)] +struct S { + s: isize, +} + +impl T for S { + fn print(&self) { + println!("{:?}", self); + } +} + +fn print_t(t: &T) { + t.print(); +} + +fn print_s(s: &S) { + s.print(); +} + +pub fn main() { + let s: Box = box S { s: 5 }; + print_s(&*s); + let t: Box = s as Box; + print_t(&*t); +} From 6706d8fdec0cc24a9d866eba8d2b1bc60ccf4286 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 24 Mar 2017 09:03:19 +0100 Subject: [PATCH 0897/1096] Add regression test for bad substs --- tests/run-pass/bad_substs.rs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/run-pass/bad_substs.rs diff --git a/tests/run-pass/bad_substs.rs b/tests/run-pass/bad_substs.rs new file mode 100644 index 0000000000000..d8da2de5d6df9 --- /dev/null +++ b/tests/run-pass/bad_substs.rs @@ -0,0 +1,4 @@ +fn main() { + let f: fn(i32) -> Option = Some::; + f(42); +} From 065e9593b4f63889fad8b8cf29a52a4791fddc3f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 27 Mar 2017 10:13:21 +0200 Subject: [PATCH 0898/1096] Rustup to `rustc 1.17.0-nightly (7846dbe0c 2017-03-26)` --- src/eval_context.rs | 18 +++++++----------- src/step.rs | 8 +------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 5cd790797b471..bbee49bae84c8 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -342,7 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) } - pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { + pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::ConstFloat; @@ -364,7 +364,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), - Function(_, _) => unimplemented!(), + // function items are zero sized and thus have no readable value + Function(..) => PrimVal::Undef, Array(_) => unimplemented!(), Repeat(_, _) => unimplemented!(), }; @@ -995,20 +996,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), - Constant(mir::Constant { ref literal, ty, .. }) => { + Constant(mir::Constant { ref literal, .. }) => { use rustc::mir::Literal; let value = match *literal { Literal::Value { ref value } => self.const_to_value(value)?, Literal::Item { def_id, substs } => { - if let ty::TyFnDef(..) = ty.sty { - // function items are zero sized - Value::ByRef(self.memory.allocate(0, 0)?) - } else { - let instance = self.resolve_associated_const(def_id, substs); - let cid = GlobalId { instance, promoted: None }; - self.globals.get(&cid).expect("static/const not cached").value - } + let instance = self.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; + self.globals.get(&cid).expect("static/const not cached").value } Literal::Promoted { index } => { diff --git a/src/step.rs b/src/step.rs index 8a0cbc4a8f1c9..79e940bee533b 100644 --- a/src/step.rs +++ b/src/step.rs @@ -195,13 +195,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - if let ty::TyFnDef(..) = constant.ty.sty { - // No need to do anything here, - // because the type is the actual function, not the signature of the function. - // Thus we can simply create a zero sized allocation in `evaluate_operand` - } else { - self.global_item(def_id, substs, constant.span, true); - } + self.global_item(def_id, substs, constant.span, true); }, mir::Literal::Promoted { index } => { let cid = GlobalId { From fdeee8fb594f05319dafacb90233fc8a03aa7352 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 29 Mar 2017 09:10:05 +0200 Subject: [PATCH 0899/1096] Cleanup the diff --- src/terminator/intrinsic.rs | 45 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a84b87f49a736..bf5623e485bfe 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let f32 = self.tcx.types.f32; let f64 = self.tcx.types.f64; + let substs = instance.substs; let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { @@ -58,7 +59,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_relaxed" | "atomic_load_acq" | "volatile_load" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -67,7 +68,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_relaxed" | "atomic_store_rel" | "volatile_store" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -77,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_xchg") => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -91,7 +92,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ if intrinsic_name.starts_with("atomic_cxchg") => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; @@ -113,7 +114,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_and" | "atomic_and_acq" | "atomic_and_rel" | "atomic_and_acqrel" | "atomic_and_relaxed" | "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; @@ -142,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy" | "copy_nonoverlapping" => { // FIXME: check whether overlapping occurs - let elem_ty = instance.substs.type_at(0); + let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); let elem_align = self.type_align(elem_ty)?; let src = arg_vals[0].read_ptr(&self.memory)?; @@ -155,7 +156,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "cttz" | "ctlz" | "bswap" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let num = self.value_to_primval(arg_vals[0], ty)?; let kind = self.ty_to_primval_kind(ty)?; let num = numeric_intrinsic(intrinsic_name, num, kind)?; @@ -163,7 +164,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "discriminant_value" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; @@ -216,7 +217,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[1], ty)?; @@ -248,7 +249,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { - let ptr = this.alloc_ptr_with_substs(dest_ty, instance.substs)?; + let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; this.memory.write_repeat(ptr, 0, size)?; Value::ByRef(ptr) } @@ -268,14 +269,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "min_align_of" => { - let elem_ty = instance.substs.type_at(0); + let elem_ty = substs.type_at(0); let elem_align = self.type_align(elem_ty)?; let align_val = PrimVal::from_u128(elem_align as u128); self.write_primval(dest, align_val, dest_ty)?; } "pref_align_of" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let layout = self.type_layout(ty)?; let align = layout.align(&self.tcx.data_layout).pref(); let align_val = PrimVal::from_u128(align as u128); @@ -283,20 +284,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "move_val_init" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } "needs_drop" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let env = self.tcx.empty_parameter_environment(); let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } "offset" => { - let pointee_ty = instance.substs.type_at(0); + let pointee_ty = substs.type_at(0); // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; @@ -357,7 +358,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the // `size_of_val` intrinsic, then change this back to // .expect("size_of intrinsic called on unsized value") @@ -367,32 +368,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "size_of_val" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } "min_align_of_val" | "align_of_val" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?; self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?; } "type_name" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ty_name = ty.to_string(); let s = self.str_to_value(&ty_name)?; self.write_value(s, dest, dest_ty)?; } "type_id" => { - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } "transmute" => { - let dest_ty = instance.substs.type_at(1); + let dest_ty = substs.type_at(1); self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -418,7 +419,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write_bytes" => { let u8 = self.tcx.types.u8; - let ty = instance.substs.type_at(0); + let ty = substs.type_at(0); let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); From f95cc529fcc8293e236ec92012acfd5684e99b9d Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 20 Apr 2017 09:46:43 +0300 Subject: [PATCH 0900/1096] Update byteorder commit hash in Cargo.lock. --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e3708fd1bc662..7263e2beeebd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "byteorder" version = "1.0.0" -source = "git+https://github.com/quininer/byteorder.git?branch=i128#ef51df297aa833d0b6639aae328a95597fc07d75" +source = "git+https://github.com/quininer/byteorder.git?branch=i128#9bab6d7783f81da50feb234a120c918d9eabba6e" [[package]] name = "cargo_metadata" From cb3799b44bc50fc5b92555cd853bbbc6ff377efd Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 13:27:09 +0300 Subject: [PATCH 0901/1096] Update for changes in LocalDecl on nightly. --- src/eval_context.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index bbee49bae84c8..3ddecc43373d7 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -259,31 +259,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mutability: mir::Mutability::Mut, ty: tcx.mk_nil(), name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))), name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.usize, name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.usize, name: None, - source_info: None, + source_info, + is_user_variable: false, }, mir::LocalDecl { mutability: mir::Mutability::Mut, ty: tcx.types.bool, name: None, - source_info: None, + source_info, + is_user_variable: false, }, ]; let seq_drop_glue = mir::Mir::new( From 738c7d262afc644fb54c09df5517a845f1dfe726 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 13:56:44 +0300 Subject: [PATCH 0902/1096] Handle Use of ! as Unreachable is not emitted nowadays. --- src/lvalue.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index b78824ea5c9e7..9b6bfd2ddf412 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -108,18 +108,24 @@ impl<'tcx> Global<'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + let ty = self.lvalue_ty(lvalue); let lvalue = self.eval_lvalue(lvalue)?; - Ok(self.read_lvalue(lvalue)) - } - pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> Value { + if ty.is_never() { + return Err(EvalError::Unreachable); + } + match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Value::ByRef(ptr) + Ok(Value::ByRef(ptr)) + } + Lvalue::Local { frame, local, field } => { + Ok(self.stack[frame].get_local(local, field.map(|(i, _)| i))) + } + Lvalue::Global(cid) => { + Ok(self.globals.get(&cid).expect("global not cached").value) } - Lvalue::Local { frame, local, field } => self.stack[frame].get_local(local, field.map(|(i, _)| i)), - Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value, } } From b9bd747b6c8f10b4430b96fba16c0359e7c1b271 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 21 Apr 2017 14:02:12 +0300 Subject: [PATCH 0903/1096] Import EvalError in lvalue. --- src/lvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 9b6bfd2ddf412..45d9501458d7b 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -3,7 +3,7 @@ use rustc::ty::layout::{Size, Align}; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; -use error::EvalResult; +use error::{EvalError, EvalResult}; use eval_context::{EvalContext}; use memory::Pointer; use value::{PrimVal, Value}; From d666bd7e62e4215f16747336aec5e87d0ca3213e Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Sun, 23 Apr 2017 13:45:04 -0400 Subject: [PATCH 0904/1096] update for latest rustc nightly: type_needs_drop_given_env() and type_contents() are gone --- src/eval_context.rs | 4 ++-- src/step.rs | 6 +++++- src/terminator/intrinsic.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 3ddecc43373d7..d8002b604c454 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1944,13 +1944,13 @@ pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bo // code quality is unknown at this time.) let env = tcx.empty_parameter_environment(); - if !tcx.type_needs_drop_given_env(t, &env) { + if !t.needs_drop(tcx, &env) { return false; } match t.sty { ty::TyAdt(def, _) if def.is_box() => { let typ = t.boxed_ty(); - if !tcx.type_needs_drop_given_env(typ, &env) && type_is_sized(tcx, typ) { + if !typ.needs_drop(tcx, &env) && type_is_sized(tcx, typ) { tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { let layout = t.layout(&infcx).unwrap(); if layout.size(&tcx.data_layout).bytes() == 0 { diff --git a/src/step.rs b/src/step.rs index 79e940bee533b..4a830d76b3eea 100644 --- a/src/step.rs +++ b/src/step.rs @@ -163,7 +163,11 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { self.try(|this| { let mir = this.ecx.load_mir(instance.def)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let mutable = !shared || mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe(); + let mutable = !shared || + !mir.return_ty.is_freeze( + this.ecx.tcx, + &this.ecx.tcx.empty_parameter_environment(), + span); let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); trace!("pushing stack frame for global: {}", name); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index bf5623e485bfe..ba51283af4c29 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -292,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "needs_drop" => { let ty = substs.type_at(0); let env = self.tcx.empty_parameter_environment(); - let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env); + let needs_drop = ty.needs_drop(self.tcx, &env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } From 76768479b55a7a5de69d8a525140dd0c44c052df Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 25 Apr 2017 16:51:58 +0200 Subject: [PATCH 0905/1096] Update for latest rustc changes --- src/eval_context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/eval_context.rs b/src/eval_context.rs index d8002b604c454..2ae5fcf5cfe29 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -367,6 +367,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::Ptr(ptr) } + Variant(_) => unimplemented!(), Struct(_) => unimplemented!(), Tuple(_) => unimplemented!(), // function items are zero sized and thus have no readable value From df9440d5ac4ac23f9a2fd2e6e8ac4c6beb473ba0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 25 Apr 2017 17:08:12 +0200 Subject: [PATCH 0906/1096] Also test subdirectories of `rust/src/test/run-pass` --- tests/compiletest.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 100b79401dfbb..6e5890d6ade00 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -92,9 +92,17 @@ fn compile_test() { let mut unsupported = Vec::new(); let mut unimplemented_intrinsic = Vec::new(); let mut limits = Vec::new(); - for file in std::fs::read_dir(path).unwrap() { + let mut files: Vec<_> = std::fs::read_dir(path).unwrap().collect(); + while let Some(file) = files.pop() { let file = file.unwrap(); let path = file.path(); + if file.metadata().unwrap().is_dir() { + if !path.to_str().unwrap().ends_with("auxiliary") { + // add subdirs recursively + files.extend(std::fs::read_dir(path).unwrap()); + } + continue; + } if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") { continue; } From 538c271e0594ce6b1cc8ee420eaba0e70286ea65 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 26 Apr 2017 12:15:42 +0200 Subject: [PATCH 0907/1096] Address clippy lints --- src/eval_context.rs | 12 ++++-------- src/lvalue.rs | 2 +- src/memory.rs | 2 +- src/terminator/mod.rs | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 2ae5fcf5cfe29..28a08a8c20946 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1285,9 +1285,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) if self.type_is_sized(tam.ty) => PrimValKind::Ptr, - ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr, + ty::TyAdt(def, _) if def.is_box() => PrimValKind::Ptr, - ty::TyAdt(ref def, substs) => { + ty::TyAdt(def, substs) => { use rustc::ty::layout::Layout::*; match *self.type_layout(ty)? { CEnum { discr, signed, .. } => { @@ -1954,12 +1954,8 @@ pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bo if !typ.needs_drop(tcx, &env) && type_is_sized(tcx, typ) { tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { let layout = t.layout(&infcx).unwrap(); - if layout.size(&tcx.data_layout).bytes() == 0 { - // `Box` does not allocate. - false - } else { - true - } + // `Box` does not allocate. + layout.size(&tcx.data_layout).bytes() != 0 }) } else { true diff --git a/src/lvalue.rs b/src/lvalue.rs index 45d9501458d7b..0f1ce103b0c44 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -322,7 +322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_type = match base_ty.sty { ty::TyRawPtr(ref tam) | ty::TyRef(_, ref tam) => tam.ty, - ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(), + ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(), _ => bug!("can only deref pointer types"), }; diff --git a/src/memory.rs b/src/memory.rs index 3d46d710d3dd7..f2440d3d72672 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -202,7 +202,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(Pointer::zst_ptr()); } - assert!(align != 0); + assert_ne!(align, 0); if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 03804ccd0bd51..0c0239f592695 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_type = match ty.sty { ty::TyRawPtr(ref tam) | ty::TyRef(_, ref tam) => tam.ty, - ty::TyAdt(ref def, _) if def.is_box() => ty.boxed_ty(), + ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(), _ => bug!("can only deref pointer types"), }; self.drop(val, instance, pointee_type, span) From 488fc53db92ae85cdbcecbf94aa31ce919379245 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 27 Apr 2017 13:48:19 +0200 Subject: [PATCH 0908/1096] Update to the latest rustc version --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 28a08a8c20946..f2e4a3d780da0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -2018,7 +2018,7 @@ pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { - let ty = tcx.item_type(def_id); + let ty = tcx.type_of(def_id); apply_param_substs(tcx, substs, &ty) } From 671ccca8913ea3b16d4caf91cf4eef5c9b3fdff9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 2 May 2017 10:44:35 +0200 Subject: [PATCH 0909/1096] Update to rustc 1.19.0-nightly (777ee2079 2017-05-01) --- src/step.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/step.rs b/src/step.rs index 4a830d76b3eea..11db048f78ab6 100644 --- a/src/step.rs +++ b/src/step.rs @@ -248,7 +248,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { bug!("static def id doesn't point to item"); } } else { - let def = self.ecx.tcx.sess.cstore.describe_def(def_id).expect("static not found"); + let def = self.ecx.tcx.describe_def(def_id).expect("static not found"); if let hir::def::Def::Static(_, mutable) = def { self.global_item(def_id, substs, span, !mutable); } else { From 07229d6305bbfc132b144d0974042eff9fb015ee Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 4 May 2017 17:42:43 +0200 Subject: [PATCH 0910/1096] Rustup to rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03) --- benches/helpers/miri_helper.rs | 3 +-- src/bin/miri.rs | 1 - src/eval_context.rs | 47 ++++++---------------------------- src/lib.rs | 1 - src/step.rs | 13 ++++------ src/terminator/drop.rs | 4 +-- 6 files changed, 16 insertions(+), 53 deletions(-) diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index 3725df24cdca7..441c35f5513e0 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -4,7 +4,7 @@ extern crate rustc; extern crate rustc_driver; extern crate test; -use self::miri::{eval_main, run_mir_passes}; +use self::miri::eval_main; use self::rustc::session::Session; use self::rustc_driver::{driver, CompilerCalls, Compilation}; use std::cell::RefCell; @@ -55,7 +55,6 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { .expect("no main or start function found"); let entry_def_id = tcx.map.local_def_id(entry_node_id); - run_mir_passes(tcx); let memory_size = 100*1024*1024; // 100MB let step_limit = 1000_000; let stack_limit = 100; diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 04b11ff60783d..0c3424bc52b88 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -74,7 +74,6 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { state.session.abort_if_errors(); let tcx = state.tcx.unwrap(); - miri::run_mir_passes(tcx); let limits = resource_limits_from_attributes(state); if std::env::args().any(|arg| arg == "--test") { diff --git a/src/eval_context.rs b/src/eval_context.rs index f2e4a3d780da0..1dfca553c749f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1,4 +1,3 @@ -use std::cell::Ref; use std::collections::HashMap; use std::fmt::Write; @@ -24,8 +23,6 @@ use memory::{Memory, Pointer}; use operator; use value::{PrimVal, PrimValKind, Value}; -pub type MirRef<'tcx> = Ref<'tcx, mir::Mir<'tcx>>; - pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. pub(crate) tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -48,7 +45,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { pub(crate) steps_remaining: u64, /// Drop glue for arrays and slices - pub(crate) seq_drop_glue: MirRef<'tcx>, + pub(crate) seq_drop_glue: &'tcx mir::Mir<'tcx>, } /// A stack frame. @@ -58,7 +55,7 @@ pub struct Frame<'tcx> { //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. - pub mir: MirRef<'tcx>, + pub mir: &'tcx mir::Mir<'tcx>, /// The def_id and substs of the current function pub instance: ty::Instance<'tcx>, @@ -302,8 +299,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { DUMMY_SP, ); let seq_drop_glue = tcx.alloc_mir(seq_drop_glue); - // Perma-borrow MIR from shims to prevent mutation. - ::std::mem::forget(seq_drop_glue.borrow()); EvalContext { tcx, memory: Memory::new(&tcx.data_layout, limits.memory_size), @@ -311,7 +306,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, - seq_drop_glue: seq_drop_glue.borrow(), + seq_drop_glue: seq_drop_glue, } } @@ -385,10 +380,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } - pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, MirRef<'tcx>> { + pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> { trace!("load mir {:?}", instance); match instance { - ty::InstanceDef::Item(def_id) => self.tcx.maybe_item_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))), + ty::InstanceDef::Item(def_id) => self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| EvalError::NoMirFor(self.tcx.item_path_str(def_id))), _ => Ok(self.tcx.instance_mir(instance)), } } @@ -450,7 +445,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, instance: ty::Instance<'tcx>, span: codemap::Span, - mir: MirRef<'tcx>, + mir: &'tcx mir::Mir<'tcx>, return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { @@ -1445,8 +1440,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - pub(super) fn mir(&self) -> MirRef<'tcx> { - Ref::clone(&self.frame().mir) + pub(super) fn mir(&self) -> &'tcx mir::Mir<'tcx> { + self.frame().mir } pub(super) fn substs(&self) -> &'tcx Substs<'tcx> { @@ -1734,32 +1729,6 @@ fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { err.emit(); } -pub fn run_mir_passes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - let mut passes = ::rustc::mir::transform::Passes::new(); - passes.push_hook(Box::new(::rustc_mir::transform::dump_mir::DumpMir)); - passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); - passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("no-landing-pads"))); - - // From here on out, regions are gone. - passes.push_pass(Box::new(::rustc_mir::transform::erase_regions::EraseRegions)); - - passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); - passes.push_pass(Box::new(::rustc_borrowck::ElaborateDrops)); - passes.push_pass(Box::new(::rustc_mir::transform::no_landing_pads::NoLandingPads)); - passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyCfg::new("elaborate-drops"))); - - // No lifetime analysis based on borrowing can be done from here on out. - passes.push_pass(Box::new(::rustc_mir::transform::instcombine::InstCombine::new())); - passes.push_pass(Box::new(::rustc_mir::transform::deaggregator::Deaggregator)); - passes.push_pass(Box::new(::rustc_mir::transform::copy_prop::CopyPropagation)); - - passes.push_pass(Box::new(::rustc_mir::transform::simplify::SimplifyLocals)); - passes.push_pass(Box::new(::rustc_mir::transform::add_call_guards::AddCallGuards)); - passes.push_pass(Box::new(::rustc_mir::transform::dump_mir::Marker("PreMiri"))); - - passes.run_passes(tcx); -} - // TODO(solson): Upstream these methods into rustc::ty::layout. pub(super) trait IntegerExt { diff --git a/src/lib.rs b/src/lib.rs index c55b4538005a3..cdbf8f2cf9241 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,6 @@ pub use eval_context::{ ResourceLimits, StackPopCleanup, eval_main, - run_mir_passes, }; pub use lvalue::{ diff --git a/src/step.rs b/src/step.rs index 11db048f78ab6..4f599f8ba14fb 100644 --- a/src/step.rs +++ b/src/step.rs @@ -2,8 +2,6 @@ //! //! The main entry point is the `step` method. -use std::cell::Ref; - use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; @@ -12,7 +10,7 @@ use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; use error::{EvalResult, EvalError}; -use eval_context::{EvalContext, StackPopCleanup, MirRef}; +use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; use syntax::codemap::Span; @@ -47,7 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: stmt.source_info.span, instance: self.frame().instance, ecx: self, - mir: Ref::clone(&mir), + mir, new_constants: &mut new, }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id }); if new? == 0 { @@ -64,7 +62,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { span: terminator.source_info.span, instance: self.frame().instance, ecx: self, - mir: Ref::clone(&mir), + mir, new_constants: &mut new, }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id }); if new? == 0 { @@ -142,7 +140,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> { span: Span, ecx: &'a mut EvalContext<'b, 'tcx>, - mir: MirRef<'tcx>, + mir: &'tcx mir::Mir<'tcx>, instance: ty::Instance<'tcx>, new_constants: &'a mut EvalResult<'tcx, u64>, } @@ -209,8 +207,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if self.ecx.globals.contains_key(&cid) { return; } - let mir = Ref::clone(&self.mir); - let mir = Ref::map(mir, |mir| &mir.promoted[index]); + let mir = &self.mir.promoted[index]; self.try(|this| { let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs); this.ecx.globals.insert(cid, Global::uninitialized(ty)); diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index bc37730b71438..8ce57ba44c78f 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -53,13 +53,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("expected thin ptr, got {:?}", arg), }; arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128)); - ::eval_context::MirRef::clone(&self.seq_drop_glue) + &self.seq_drop_glue }, ty::TySlice(elem) => { instance.substs = self.tcx.mk_substs([ Kind::from(elem), ].iter().cloned()); - ::eval_context::MirRef::clone(&self.seq_drop_glue) + &self.seq_drop_glue }, _ => self.load_mir(instance.def)?, }; From da3860a2936ff6d324fcf32aae51c907f96a0353 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 5 May 2017 10:34:38 +0200 Subject: [PATCH 0911/1096] Address things complained about by clippy --- src/bin/miri.rs | 14 ++++++-------- src/eval_context.rs | 2 +- src/lvalue.rs | 2 +- src/terminator/drop.rs | 4 ++-- tests/compiletest.rs | 18 ++++++++---------- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 0c3424bc52b88..3f0a6f778b425 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -93,15 +93,13 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {} } state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); - } else { - if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { - let entry_def_id = tcx.hir.local_def_id(entry_node_id); - miri::eval_main(tcx, entry_def_id, limits); + } else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { + let entry_def_id = tcx.hir.local_def_id(entry_node_id); + miri::eval_main(tcx, entry_def_id, limits); - state.session.abort_if_errors(); - } else { - println!("no main function found, assuming auxiliary build"); - } + state.session.abort_if_errors(); + } else { + println!("no main function found, assuming auxiliary build"); } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 1dfca553c749f..b9aa499015a8f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1023,7 +1023,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { - self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) + self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { diff --git a/src/lvalue.rs b/src/lvalue.rs index 0f1ce103b0c44..9660b8f4eec86 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -399,6 +399,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) + self.monomorphize(lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } } diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 8ce57ba44c78f..93dfe408e31a9 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -53,13 +53,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("expected thin ptr, got {:?}", arg), }; arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128)); - &self.seq_drop_glue + self.seq_drop_glue }, ty::TySlice(elem) => { instance.substs = self.tcx.mk_substs([ Kind::from(elem), ].iter().cloned()); - &self.seq_drop_glue + self.seq_drop_glue }, _ => self.load_mir(instance.def)?, }; diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 6e5890d6ade00..78b2a2f3ce252 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -5,7 +5,7 @@ use std::io::Write; fn compile_fail(sysroot: &Path) { let flags = format!("--sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path")); - for_all_targets(&sysroot, |target| { + for_all_targets(sysroot, |target| { let mut config = compiletest::default_config(); config.host_rustcflags = Some(flags.clone()); config.mode = "compile-fail".parse().expect("Invalid mode"); @@ -79,8 +79,8 @@ fn compile_test() { .expect("rustc not found for -vV") .stdout; let host = std::str::from_utf8(&host).expect("sysroot is not utf8"); - let host = host.split("\nhost: ").skip(1).next().expect("no host: part in rustc -vV"); - let host = host.split("\n").next().expect("no \n after host"); + let host = host.split("\nhost: ").nth(1).expect("no host: part in rustc -vV"); + let host = host.split('\n').next().expect("no \n after host"); if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") { let mut mir_not_found = Vec::new(); @@ -148,10 +148,8 @@ fn compile_test() { abi.push(text[abi_s.len()..end].to_string()); } else if text.starts_with(limit_s) { limits.push(text[limit_s.len()..end].to_string()); - } else { - if text.find("aborting").is_none() { - failed.push(text[..end].to_string()); - } + } else if text.find("aborting").is_none() { + failed.push(text[..end].to_string()); } } writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); @@ -196,10 +194,10 @@ fn compile_test() { panic!("ran miri on rustc test suite. Test failing for convenience"); } else { run_pass(); - for_all_targets(&sysroot, |target| { + for_all_targets(sysroot, |target| { miri_pass("tests/run-pass", &target, host); }); - compile_fail(&sysroot); + compile_fail(sysroot); } } @@ -218,7 +216,7 @@ fn vec_to_hist(mut v: Vec) -> Vec<(usize, T)> { let mut current = v.next(); 'outer: while let Some(current_val) = current { let mut n = 1; - while let Some(next) = v.next() { + for next in &mut v { if next == current_val { n += 1; } else { From 5f67ba76f5bc6956267f59caa4328c70cc95dc77 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 5 May 2017 10:37:04 +0200 Subject: [PATCH 0912/1096] Removed unused crate imports --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cdbf8f2cf9241..f373606218c73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,8 @@ extern crate log; extern crate log_settings; #[macro_use] extern crate rustc; -extern crate rustc_borrowck; extern crate rustc_const_math; extern crate rustc_data_structures; -extern crate rustc_mir; extern crate syntax; // From crates.io. From 23cf495f27d4c1051d29c1e53f6b91864a85fd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Tue, 9 May 2017 16:35:09 +0200 Subject: [PATCH 0913/1096] Update Cargo.toml, fix for build error (see #164) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ba7e38bed85b4..500341303e092 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ test = false [dependencies] #byteorder = "0.4.2" -byteorder = { git = "https://github.com/quininer/byteorder.git", branch = "i128", features = ["i128"]} +byteorder = { git = "https://github.com/BurntSushi/byteorder", features = ["i128"]} env_logger = "0.3.3" log = "0.3.6" log_settings = "0.1.1" From 9e44509e511170320f70cdb3b06213e1a6798611 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 9 May 2017 16:47:04 +0200 Subject: [PATCH 0914/1096] Update dependencies --- Cargo.lock | 123 +++++++++++++++++++++++++++++------------------------ Cargo.toml | 4 +- 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7263e2beeebd6..b868ed6671a48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,11 @@ name = "miri" version = "0.1.0" dependencies = [ - "byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)", - "cargo_metadata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)", + "cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -21,30 +21,30 @@ dependencies = [ [[package]] name = "byteorder" version = "1.0.0" -source = "git+https://github.com/quininer/byteorder.git?branch=i128#9bab6d7783f81da50feb234a120c918d9eabba6e" +source = "git+https://github.com/BurntSushi/byteorder#f8e7685b3a81c52f5448fd77fb4e0535bc92f880" [[package]] name = "cargo_metadata" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compiletest_rs" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "dtoa" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -52,13 +52,13 @@ name = "env_logger" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -72,17 +72,17 @@ dependencies = [ [[package]] name = "lazy_static" -version = "0.2.2" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "log" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -90,7 +90,7 @@ name = "log_settings" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -98,17 +98,17 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -130,49 +130,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-serialize" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "0.9.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "serde_codegen_internals" -version = "0.12.0" +name = "serde_derive" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "serde_derive" -version = "0.9.2" +name = "serde_derive_internals" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen_internals 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "0.9.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.11.4" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -182,7 +192,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -215,28 +225,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum byteorder 1.0.0 (git+https://github.com/quininer/byteorder.git?branch=i128)" = "" -"checksum cargo_metadata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "84e3b2d1646a740bb5aae05f7c0a7afd8ae40ea244f78bc36ac25fc8043a54a5" -"checksum compiletest_rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f344389765ad7bec166f64c1b39ed6dd2b54d81c4c5dd8af789169351d380c" -"checksum dtoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87a527ff375a9761c677bb24a677ce48af8035ba260e01e831e4e4b04f945d2a" +"checksum byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)" = "" +"checksum cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d84cb53c78e573aa126a4b9f963fdb2629f8183b26e235da08bb36dc7381162" +"checksum compiletest_rs 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "df47edea8bf052f23ce25a15cbf0be09c96911e3be943d1e81415bfaf0e74bf8" +"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" -"checksum itoa 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5537accdedeaa51526addad01e989bdaeb690e8e5fcca8dce893350912778636" +"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" -"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" -"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" +"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" +"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" -"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" +"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" -"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" -"checksum serde 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ff246881a798936bb630947e77add6c4b031fbf28312aca8e3d7c8949429e5f0" -"checksum serde_codegen_internals 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbca5cba592a2874e48fb67a61479f5b86c0b84a86cf82fa81f947ea538e1449" -"checksum serde_derive 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7278d46eaf402b063c25288d0e4232029e9fb2f20e272a932b2c15a9fed7f32d" -"checksum serde_json 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d30dd31e5b6b2752ba87da4bb34edc01391bbab71563fc1e95cdd1e30dce16b8" -"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3" +"checksum serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c06b68790963518008b8ae0152d48be4bbbe77015d2c717f6282eea1824be9a" +"checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" +"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" diff --git a/Cargo.toml b/Cargo.toml index 500341303e092..90e078266d195 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ byteorder = { git = "https://github.com/BurntSushi/byteorder", features = ["i128 env_logger = "0.3.3" log = "0.3.6" log_settings = "0.1.1" -cargo_metadata = "0.1" +cargo_metadata = "0.2" [dev-dependencies] -compiletest_rs = "0.2.5" +compiletest_rs = "0.2.6" From ecf452ce3acf1893e5dc26b6a575b0f293f86fd4 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 10 May 2017 16:38:29 -0400 Subject: [PATCH 0915/1096] don't match on floating-point literal --- tests/run-pass/union.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-pass/union.rs b/tests/run-pass/union.rs index 9e05a89a4ea37..e51c601289697 100644 --- a/tests/run-pass/union.rs +++ b/tests/run-pass/union.rs @@ -62,7 +62,7 @@ fn c() { unsafe { match v { Value { tag: Tag::I, u: U { i: 0 } } => true, - Value { tag: Tag::F, u: U { f: 0.0 } } => true, + Value { tag: Tag::F, u: U { f } } if f == 0.0 => true, _ => false, } } From 2b84e176fb9ec23b4ebebb4d357f31d43dc4bf35 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Sat, 13 May 2017 07:08:30 -0400 Subject: [PATCH 0916/1096] update for new boxier mir --- src/eval_context.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b9aa499015a8f..e3d62e33d64e1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -137,13 +137,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { source_info, kind: mir::StatementKind::Assign( mir::Lvalue::Local(mir::Local::new(2)), - mir::Rvalue::Use(mir::Operand::Constant(mir::Constant { + mir::Rvalue::Use(mir::Operand::Constant(Box::new(mir::Constant { span: DUMMY_SP, ty: tcx.types.usize, literal: mir::Literal::Value { value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), }, - })) + }))) ) }, mir::Statement { @@ -225,13 +225,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir::Rvalue::BinaryOp( mir::BinOp::Add, mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), - mir::Operand::Constant(mir::Constant { + mir::Operand::Constant(Box::new(mir::Constant { span: DUMMY_SP, ty: tcx.types.usize, literal: mir::Literal::Value { value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())), }, - }), + })), ) ) }, @@ -636,7 +636,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } General { discr, ref variants, .. } => { - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { + if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { let discr_val = adt_def.discriminants(self.tcx) .nth(variant) .expect("broken mir: Adt variant id invalid") @@ -662,7 +662,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } RawNullablePointer { nndiscr, .. } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { + if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); let operand = &operands[0]; @@ -683,7 +683,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { - if let mir::AggregateKind::Adt(_, variant, _, _) = *kind { + if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { if nonnull.packed { let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; self.memory.mark_packed(ptr, nonnull.stride().bytes()); @@ -712,7 +712,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { CEnum { .. } => { assert_eq!(operands.len(), 0); - if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind { + if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind { let n = adt_def.discriminants(self.tcx) .nth(variant) .expect("broken mir: Adt variant index invalid") @@ -997,8 +997,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), - Constant(mir::Constant { ref literal, .. }) => { + Constant(ref constant) => { use rustc::mir::Literal; + let mir::Constant { ref literal, .. } = **constant; let value = match *literal { Literal::Value { ref value } => self.const_to_value(value)?, From 0641e932bcdf993812b84978a960774aad365d87 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Sun, 21 May 2017 15:48:31 -0400 Subject: [PATCH 0917/1096] implement __rust_allocate_zeroed C ABI function --- src/terminator/mod.rs | 8 ++++++++ tests/run-pass/vecs.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 0c0239f592695..a3ff8c1b54ba8 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -493,6 +493,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } + "__rust_allocate_zeroed" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + let ptr = self.memory.allocate(size, align)?; + self.memory.write_repeat(ptr, 0, size)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "__rust_deallocate" => { let ptr = args[0].read_ptr(&self.memory)?; // FIXME: insert sanity check for size and align? diff --git a/tests/run-pass/vecs.rs b/tests/run-pass/vecs.rs index b3a88014e6f9a..fd3a00031d61e 100644 --- a/tests/run-pass/vecs.rs +++ b/tests/run-pass/vecs.rs @@ -13,6 +13,10 @@ fn make_vec_macro_repeat() -> Vec { vec![42; 5] } +fn make_vec_macro_repeat_zeroed() -> Vec { + vec![0; 7] +} + fn vec_into_iter() -> u8 { vec![1, 2, 3, 4] .into_iter() @@ -34,4 +38,5 @@ fn main() { assert_eq!(make_vec().capacity(), 4); assert_eq!(make_vec_macro(), [1, 2]); assert_eq!(make_vec_macro_repeat(), [42; 5]); + assert_eq!(make_vec_macro_repeat_zeroed(), [0; 7]); } From 781709268e66a5b54fde14298af4f099f3b67d0c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 May 2017 10:31:47 -0700 Subject: [PATCH 0918/1096] fix 'cargo run' in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3822e9ac10cc4..843535caf9def 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ how to fix it, you could send a PR. :smile: ## Running tests ```sh -cargo run tests/run-pass/vecs.rs # Or whatever test you like. +cargo run --bin miri tests/run-pass/vecs.rs # Or whatever test you like. ``` ## Debugging From 5accdf45eae803c02dddd1491251cd8a51a1ba72 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 May 2017 10:50:07 -0700 Subject: [PATCH 0919/1096] use `cargo +nightly` rather than `rustup run nightly cargo` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 843535caf9def..aa8e0315b7687 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ rustup update nightly You should also make `nightly` the default version for your Miri directory by running the following command while you're in it. If you don't do this, you can -run the later `cargo` commands by prefixing them with `rustup run nightly`. +run the later `cargo` commands by using `cargo +nightly` instead. ```sh rustup override add nightly From 48662d5199cff83fcc98bb08a1c9729a5f70a1f2 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 23 May 2017 23:40:39 -0400 Subject: [PATCH 0920/1096] update for upstream changes with ty::ParamEnv --- src/eval_context.rs | 10 +++++----- src/step.rs | 2 +- src/terminator/intrinsic.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e3d62e33d64e1..244f50befa9bd 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -377,7 +377,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); - ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) + ty.is_sized(self.tcx, ty::ParamEnv::empty(), DUMMY_SP) } pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> { @@ -1914,14 +1914,14 @@ pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bo // returned `false` does not appear unsound. The impact on // code quality is unknown at this time.) - let env = tcx.empty_parameter_environment(); - if !t.needs_drop(tcx, &env) { + let env = ty::ParamEnv::empty(); + if !t.needs_drop(tcx, env) { return false; } match t.sty { ty::TyAdt(def, _) if def.is_box() => { let typ = t.boxed_ty(); - if !typ.needs_drop(tcx, &env) && type_is_sized(tcx, typ) { + if !typ.needs_drop(tcx, env) && type_is_sized(tcx, typ) { tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { let layout = t.layout(&infcx).unwrap(); // `Box` does not allocate. @@ -2038,7 +2038,7 @@ impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNorma fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); - ty.is_sized(tcx, &tcx.empty_parameter_environment(), DUMMY_SP) + ty.is_sized(tcx, ty::ParamEnv::empty(), DUMMY_SP) } /// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we diff --git a/src/step.rs b/src/step.rs index 4f599f8ba14fb..27eb26e333ac9 100644 --- a/src/step.rs +++ b/src/step.rs @@ -164,7 +164,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { let mutable = !shared || !mir.return_ty.is_freeze( this.ecx.tcx, - &this.ecx.tcx.empty_parameter_environment(), + ty::ParamEnv::empty(), span); let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index ba51283af4c29..e2fd57ee8ef6a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -291,8 +291,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "needs_drop" => { let ty = substs.type_at(0); - let env = self.tcx.empty_parameter_environment(); - let needs_drop = ty.needs_drop(self.tcx, &env); + let env = ty::ParamEnv::empty(); + let needs_drop = ty.needs_drop(self.tcx, env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } From c384f9568d9f3e794db0d550ae1fc4041e19060a Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 30 May 2017 09:27:08 -0400 Subject: [PATCH 0921/1096] Rvalue::Box -> Rvalue::NullaryOp --- src/eval_context.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 244f50befa9bd..77d9d9b20cd8f 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -788,11 +788,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(val, dest, dest_ty)?; } - Box(ty) => { + NullaryOp(mir::NullOp::Box, ty) => { let ptr = self.alloc_ptr(ty)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } + NullaryOp(mir::NullOp::SizeOf, _ty) => { + unimplemented!() + } + Cast(kind, ref operand, cast_ty) => { debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty); use rustc::mir::CastKind::*; From 66495222ab34d1f9275a32b6113bff0103b06221 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 30 May 2017 09:27:50 -0400 Subject: [PATCH 0922/1096] closure_to_fn_coercion has been stabilized --- tests/run-pass/non_capture_closure_to_fn_ptr.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/run-pass/non_capture_closure_to_fn_ptr.rs b/tests/run-pass/non_capture_closure_to_fn_ptr.rs index 6f73a3d09dda7..c9daff9c9f469 100644 --- a/tests/run-pass/non_capture_closure_to_fn_ptr.rs +++ b/tests/run-pass/non_capture_closure_to_fn_ptr.rs @@ -1,5 +1,3 @@ -#![feature(closure_to_fn_coercion)] - // allow(const_err) to work around a bug in warnings #[allow(const_err)] static FOO: fn() = || { assert_ne!(42, 43) }; From ab90500d8c56bd83def08c5f5dc815074dceae52 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 May 2017 15:11:29 -0700 Subject: [PATCH 0923/1096] Make println!("String") work miri complains about a memory leak when the program terminates. This may be related to thread-local dtors not running. --- src/terminator/mod.rs | 50 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index a3ff8c1b54ba8..d098801ae92a9 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -367,19 +367,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Only trait methods can have a Self parameter. + // Intercept some methods (even if we can find MIR for them) + if let ty::InstanceDef::Item(def_id) = instance.def { + match &self.tcx.item_path_str(def_id)[..] { + "std::sys::imp::fast_thread_local::register_dtor" => { + // Just don't execute this one, we don't handle all this thread-local stuff anyway. + self.goto_block(destination.unwrap().1); + return Ok(true) + } + _ => {} + } + } + let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { - match &path[..] { - // let's just ignore all output for now + return match &path[..] { + // Intercept some methods if we cannot find their MIR. "std::io::_print" => { + trace!("Ignoring output."); self.goto_block(destination.unwrap().1); - return Ok(true); + Ok(true) }, - "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), + "std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), "std::panicking::rust_panic_with_hook" | - "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + "std::rt::begin_panic_fmt" => Err(EvalError::Panic), "std::panicking::panicking" | "std::rt::panicking" => { let (lval, block) = destination.expect("std::rt::panicking does not diverge"); @@ -387,11 +400,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let bool = self.tcx.types.bool; self.write_primval(lval, PrimVal::from_bool(false), bool)?; self.goto_block(block); - return Ok(true); + Ok(true) } - _ => {}, - } - return Err(EvalError::NoMirFor(path)); + _ => Err(EvalError::NoMirFor(path)), + }; }, Err(other) => return Err(other), }; @@ -569,6 +581,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; } + + "write" => { + let fd = self.value_to_primval(args[0], usize)?.to_u64()?; + let buf = args[1].read_ptr(&self.memory)?; + let n = self.value_to_primval(args[2], usize)?.to_u64()?; + trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); + let result = if fd == 1 { // stdout + use std::io::{self, Write}; + + let buf_cont = self.memory.read_bytes(buf, n)?; + let res = io::stdout().write(buf_cont); + match res { Ok(n) => n as isize, Err(_) => -1 } + } else { + info!("Ignored output to FD {}", fd); + n as isize // pretend it all went well + }; // now result is the value we return back to the program + self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; + } // unix panic code inside libstd will read the return value of this function "pthread_rwlock_rdlock" => { From 1ae01b422b7c961b35d2f70550f1f795464824a4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 May 2017 15:36:48 -0700 Subject: [PATCH 0924/1096] add instructions for how to compile libstd with xargo --- README.md | 22 ++++++++++++++++++++++ xargo/Cargo.lock | 4 ++++ xargo/Cargo.toml | 6 ++++++ xargo/Xargo.toml | 2 ++ xargo/src/lib.rs | 0 5 files changed, 34 insertions(+) create mode 100644 xargo/Cargo.lock create mode 100644 xargo/Cargo.toml create mode 100644 xargo/Xargo.toml create mode 100644 xargo/src/lib.rs diff --git a/README.md b/README.md index aa8e0315b7687..aa89df9731fa3 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,28 @@ Then, inside your own project, use `cargo +nightly miri` to run your project, if a bin project, or run `cargo +nightly miri test` to run all tests in your project through miri. +## Running miri with full libstd + +Per default libstd does not contain the MIR of non-polymorphic functions. When +miri hits a call to such a function, execution terminates. To fix this, it is +possible to compile libstd with full MIR: + +```sh +cargo install xargo +cd xargo/ +RUSTFLAGS='-Zalways-encode-mir' xargo build --target `rustc -vV | egrep '^host: ' | sed 's/^host: //'` +``` + +Now you can run miri against the libstd compiled by xargo: + +```sh +cargo run --bin miri -- --sysroot ~/.xargo/HOST tests/run-pass/vecs.rs +``` + +Notice that you will have to re-run the last step of the preparations above when +your toolchain changes (e.g., when you update the nightly). Also, xargo doesn't +currently work with nightlies newer than 2017-04-23. + ## Contributing and getting help Check out the issues on this GitHub repository for some ideas. There's lots that diff --git a/xargo/Cargo.lock b/xargo/Cargo.lock new file mode 100644 index 0000000000000..031ad9a879549 --- /dev/null +++ b/xargo/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "miri-xargo" +version = "0.0.0" + diff --git a/xargo/Cargo.toml b/xargo/Cargo.toml new file mode 100644 index 0000000000000..9129c105b112b --- /dev/null +++ b/xargo/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "miri-xargo" +description = "A dummy project for building libstd with xargo." +version = "0.0.0" + +[dependencies] diff --git a/xargo/Xargo.toml b/xargo/Xargo.toml new file mode 100644 index 0000000000000..32f45c4a9816f --- /dev/null +++ b/xargo/Xargo.toml @@ -0,0 +1,2 @@ +[dependencies] +std = {features = ["panic_unwind", "jemalloc"]} diff --git a/xargo/src/lib.rs b/xargo/src/lib.rs new file mode 100644 index 0000000000000..e69de29bb2d1d From 33d42f4b8236cef23980dbecef6e86e3e9b27c57 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 May 2017 21:01:13 -0700 Subject: [PATCH 0925/1096] also support writing to stderr --- src/terminator/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d098801ae92a9..4834bba5b4840 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -587,11 +587,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let buf = args[1].read_ptr(&self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); - let result = if fd == 1 { // stdout + let result = if fd == 1 || fd == 2 { // stdout/stderr use std::io::{self, Write}; let buf_cont = self.memory.read_bytes(buf, n)?; - let res = io::stdout().write(buf_cont); + let res = if fd == 1 { io::stdout().write(buf_cont) } else { io::stderr().write(buf_cont) }; match res { Ok(n) => n as isize, Err(_) => -1 } } else { info!("Ignored output to FD {}", fd); From 452cc9b396507dc94e2047cb4d2ad843b23eccc7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 May 2017 15:19:38 -0700 Subject: [PATCH 0926/1096] handle statics with linkage: initialize them with NULL --- src/lvalue.rs | 9 +++++++++ src/step.rs | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src/lvalue.rs b/src/lvalue.rs index 9660b8f4eec86..cad4ca9d0290b 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -104,6 +104,15 @@ impl<'tcx> Global<'tcx> { initialized: false, } } + + pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: bool) -> Self { + Global { + value, + mutable, + ty, + initialized: true, + } + } } impl<'a, 'tcx> EvalContext<'a, 'tcx> { diff --git a/src/step.rs b/src/step.rs index 27eb26e333ac9..110a7b2252a8f 100644 --- a/src/step.rs +++ b/src/step.rs @@ -13,6 +13,7 @@ use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; +use memory::Pointer; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -158,6 +159,11 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { if self.ecx.globals.contains_key(&cid) { return; } + if self.ecx.tcx.has_attr(def_id, "linkage") { + trace!("Initializing an extern global with NULL"); + self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Ptr(Pointer::from_int(0))), !shared)); + return; + } self.try(|this| { let mir = this.ecx.load_mir(instance.def)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); @@ -178,6 +184,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { ) }); } + fn try EvalResult<'tcx>>(&mut self, f: F) { if let Ok(ref mut n) = *self.new_constants { *n += 1; From 238211e1b3a2412f2c36b043071663a17a8c3501 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 May 2017 16:40:13 -0700 Subject: [PATCH 0927/1096] implement TLS --- src/error.rs | 6 ++++ src/memory.rs | 49 +++++++++++++++++++++++++++ src/terminator/mod.rs | 77 ++++++++++++++++++++++++++++++------------- 3 files changed, 110 insertions(+), 22 deletions(-) diff --git a/src/error.rs b/src/error.rs index fd692ef8b64a5..25d939be3ab3f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,8 @@ pub enum EvalError<'tcx> { }, ExecutionTimeLimitReached, StackFrameLimitReached, + OutOfTls, + TlsOutOfBounds, AlignmentCheckFailed { required: u64, has: u64, @@ -101,6 +103,10 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the configured maximum execution time", EvalError::StackFrameLimitReached => "reached the configured maximum number of stack frames", + EvalError::OutOfTls => + "reached the maximum number of representable TLS keys", + EvalError::TlsOutOfBounds => + "accessed an invalid (unallocated) TLS key", EvalError::AlignmentCheckFailed{..} => "tried to execute a misaligned read or write", EvalError::CalledClosureAsFunction => diff --git a/src/memory.rs b/src/memory.rs index f2440d3d72672..bb5a1d6f26905 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -15,6 +15,8 @@ use value::PrimVal; #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub u64); +pub type TlsKey = usize; + impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -149,6 +151,12 @@ pub struct Memory<'a, 'tcx> { /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. literal_alloc_cache: HashMap, AllocId>, + + /// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there. + thread_local: HashMap, + + /// The Key to use for the next thread-local allocation. + next_thread_local: TlsKey, } const ZST_ALLOC_ID: AllocId = AllocId(0); @@ -167,6 +175,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { packed: BTreeSet::new(), static_alloc: HashSet::new(), literal_alloc_cache: HashMap::new(), + thread_local: HashMap::new(), + next_thread_local: 0, } } @@ -345,6 +355,45 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn clear_packed(&mut self) { self.packed.clear(); } + + pub(crate) fn create_tls_key(&mut self) -> TlsKey { + let new_key = self.next_thread_local; + self.next_thread_local += 1; + self.thread_local.insert(new_key, Pointer::from_int(0)); + trace!("New TLS key allocated: {}", new_key); + return new_key; + } + + pub(crate) fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> { + return match self.thread_local.remove(&key) { + Some(_) => { + trace!("TLS key {} removed", key); + Ok(()) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } + + pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> { + return match self.thread_local.get(&key) { + Some(&ptr) => { + trace!("TLS key {} loaded: {:?}", key, ptr); + Ok(ptr) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } + + pub(crate) fn store_tls(&mut self, key: TlsKey, new_ptr: Pointer) -> EvalResult<'tcx> { + return match self.thread_local.get_mut(&key) { + Some(ptr) => { + trace!("TLS key {} stored: {:?}", key, new_ptr); + *ptr = new_ptr; + Ok(()) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } } // The derived `Ord` impl sorts first by the first field, then, if the fields are the same diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4834bba5b4840..94a221b32c774 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, TypeVariants, Ty, TyS, TypeAndMut}; use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::Pointer; +use memory::{Pointer, TlsKey}; use value::PrimVal; use value::Value; use rustc_data_structures::indexed_vec::Idx; @@ -367,18 +367,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Only trait methods can have a Self parameter. - // Intercept some methods (even if we can find MIR for them) - if let ty::InstanceDef::Item(def_id) = instance.def { - match &self.tcx.item_path_str(def_id)[..] { - "std::sys::imp::fast_thread_local::register_dtor" => { - // Just don't execute this one, we don't handle all this thread-local stuff anyway. - self.goto_block(destination.unwrap().1); - return Ok(true) - } - _ => {} - } - } - let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { @@ -480,7 +468,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn call_c_abi( &mut self, def_id: DefId, - args: &[mir::Operand<'tcx>], + arg_operands: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { @@ -490,7 +478,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .unwrap_or(name) .as_str(); - let args_res: EvalResult> = args.iter() + let args_res: EvalResult> = arg_operands.iter() .map(|arg| self.eval_operand(arg)) .collect(); let args = args_res?; @@ -555,9 +543,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1); - self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } @@ -567,9 +555,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64); - self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } @@ -579,7 +567,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.memory.read_c_str(name_ptr)?; info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); } - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } "write" => { @@ -605,9 +593,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } + // Hook pthread calls that go to the thread-local storage memory subsystem + "pthread_key_create" => { + let key = self.memory.create_tls_key(); + let key_ptr = args[0].read_ptr(&self.memory)?; + + // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. + let key_size = match self.operand_ty(&arg_operands[0]) { + &TyS { sty: TypeVariants::TyRawPtr(TypeAndMut { ty, .. }), .. } => { + let layout = self.type_layout(ty)?; + layout.size(&self.tcx.data_layout) + } + _ => return Err(EvalError::Unimplemented("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned())) + }; + + // Write the key into the memory where key_ptr wants it + if key >= (1 << key_size.bits()) { + return Err(EvalError::OutOfTls); + } + self.memory.write_int(key_ptr, key as i128, key_size.bytes())?; + + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + "pthread_key_delete" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + self.memory.delete_tls_key(key)?; + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + "pthread_getspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let ptr = self.memory.load_tls(key)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "pthread_setspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let new_ptr = args[1].read_ptr(&self.memory)?; + self.memory.store_tls(key, new_ptr)?; + + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + link_name if link_name.starts_with("pthread_") => { warn!("ignoring C ABI call: {}", link_name); - return Ok(()); }, _ => { From a66f359d91d2baf2d4224832456b1f9fa7ed0740 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 May 2017 22:38:07 -0700 Subject: [PATCH 0928/1096] support TLS destructors --- src/error.rs | 2 ++ src/memory.rs | 51 ++++++++++++++++++++++++++++++++----------- src/step.rs | 32 ++++++++++++++++++++++----- src/terminator/mod.rs | 21 ++++++++++++++---- 4 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/error.rs b/src/error.rs index 25d939be3ab3f..7b6542bff9345 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,6 +40,7 @@ pub enum EvalError<'tcx> { StackFrameLimitReached, OutOfTls, TlsOutOfBounds, + AbiViolation(String), AlignmentCheckFailed { required: u64, has: u64, @@ -107,6 +108,7 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the maximum number of representable TLS keys", EvalError::TlsOutOfBounds => "accessed an invalid (unallocated) TLS key", + EvalError::AbiViolation(ref msg) => msg, EvalError::AlignmentCheckFailed{..} => "tried to execute a misaligned read or write", EvalError::CalledClosureAsFunction => diff --git a/src/memory.rs b/src/memory.rs index bb5a1d6f26905..0280ad4c379bc 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -15,8 +15,6 @@ use value::PrimVal; #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub u64); -pub type TlsKey = usize; - impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -100,6 +98,19 @@ impl Pointer { pub fn never_ptr() -> Self { Pointer::new(NEVER_ALLOC_ID, 0) } + + pub fn is_null_ptr(&self) -> bool { + // FIXME: Is this the right way? + return *self == Pointer::from_int(0) + } +} + +pub type TlsKey = usize; + +#[derive(Copy, Clone, Debug)] +pub struct TlsEntry<'tcx> { + data: Pointer, // will eventually become a map from thread IDs to pointers + dtor: Option>, } //////////////////////////////////////////////////////////////////////////////// @@ -153,7 +164,7 @@ pub struct Memory<'a, 'tcx> { literal_alloc_cache: HashMap, AllocId>, /// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there. - thread_local: HashMap, + thread_local: HashMap>, /// The Key to use for the next thread-local allocation. next_thread_local: TlsKey, @@ -356,11 +367,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.packed.clear(); } - pub(crate) fn create_tls_key(&mut self) -> TlsKey { + pub(crate) fn create_tls_key(&mut self, dtor: Option>) -> TlsKey { let new_key = self.next_thread_local; self.next_thread_local += 1; - self.thread_local.insert(new_key, Pointer::from_int(0)); - trace!("New TLS key allocated: {}", new_key); + self.thread_local.insert(new_key, TlsEntry { data: Pointer::from_int(0), dtor }); + trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); return new_key; } @@ -376,24 +387,38 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> { return match self.thread_local.get(&key) { - Some(&ptr) => { - trace!("TLS key {} loaded: {:?}", key, ptr); - Ok(ptr) + Some(&TlsEntry { data, .. }) => { + trace!("TLS key {} loaded: {:?}", key, data); + Ok(data) }, None => Err(EvalError::TlsOutOfBounds) } } - pub(crate) fn store_tls(&mut self, key: TlsKey, new_ptr: Pointer) -> EvalResult<'tcx> { + pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> { return match self.thread_local.get_mut(&key) { - Some(ptr) => { - trace!("TLS key {} stored: {:?}", key, new_ptr); - *ptr = new_ptr; + Some(&mut TlsEntry { ref mut data, .. }) => { + trace!("TLS key {} stored: {:?}", key, new_data); + *data = new_data; Ok(()) }, None => Err(EvalError::TlsOutOfBounds) } } + + // Returns a dtor and its argument, if one is supposed to run + pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, Pointer)> { + for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() { + if !data.is_null_ptr() { + if let Some(dtor) = dtor { + let old_data = *data; + *data = Pointer::from_int(0); + return Some((dtor, old_data)); + } + } + } + return None; + } } // The derived `Ord` impl sorts first by the first field, then, if the fields are the same diff --git a/src/step.rs b/src/step.rs index 110a7b2252a8f..658a3445c4331 100644 --- a/src/step.rs +++ b/src/step.rs @@ -14,7 +14,7 @@ use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; use memory::Pointer; -use syntax::codemap::Span; +use syntax::codemap::{Span, DUMMY_SP}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { @@ -32,6 +32,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.clear_packed(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { + if let Some((instance, ptr)) = self.memory.fetch_tls_dtor() { + trace!("Running TLS dtor {:?} on {:?}", instance, ptr); + // TODO: Potientiually, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs + let mir = self.load_mir(instance.def)?; + // FIXME: Are these the right dummy values? + self.push_stack_frame( + instance, + DUMMY_SP, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + if let Some(arg_local) = self.frame().mir.args_iter().next() { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?; + } else { + return Err(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned())); + } + + return Ok(true); + } return Ok(false); } @@ -49,11 +71,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir, new_constants: &mut new, }.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id }); + // if ConstantExtractor added new frames, we don't execute anything here + // but await the next call to step if new? == 0 { self.statement(stmt)?; } - // if ConstantExtractor added new frames, we don't execute anything here - // but await the next call to step return Ok(true); } @@ -66,11 +88,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mir, new_constants: &mut new, }.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id }); + // if ConstantExtractor added new frames, we don't execute anything here + // but await the next call to step if new? == 0 { self.terminator(terminator)?; } - // if ConstantExtractor added new frames, we don't execute anything here - // but await the next call to step Ok(true) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 94a221b32c774..aaf03ea665f68 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -221,6 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((arg_val, arg_ty)); } + // Push the stack frame, and potentially be entirely done if the call got hooked if self.eval_fn_call_inner( instance, destination, @@ -229,6 +230,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); } + // Pass the arguments let mut arg_locals = self.frame().mir.args_iter(); trace!("ABI: {:?}", sig.abi); trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); @@ -595,19 +597,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { - let key = self.memory.create_tls_key(); let key_ptr = args[0].read_ptr(&self.memory)?; - + + // Extract the function type out of the signature (that seems easier than constructing it ourselves...) + // FIXME: Or should we instead construct the type we expect it to have? + let dtor_fn_ty = match self.operand_ty(&arg_operands[1]) { + &TyS { sty: TypeVariants::TyAdt(_, ref substs), .. } => { + substs.type_at(0) + } + _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: Second argument must be option of a function pointer.".to_owned())) + }; + let dtor_ptr = self.value_to_primval(args[1], dtor_fn_ty)?.to_ptr()?; + let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; + // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. let key_size = match self.operand_ty(&arg_operands[0]) { &TyS { sty: TypeVariants::TyRawPtr(TypeAndMut { ty, .. }), .. } => { let layout = self.type_layout(ty)?; layout.size(&self.tcx.data_layout) } - _ => return Err(EvalError::Unimplemented("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned())) + _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned())) }; - // Write the key into the memory where key_ptr wants it + // Create key and write it into the memory where key_ptr wants it + let key = self.memory.create_tls_key(dtor); if key >= (1 << key_size.bits()) { return Err(EvalError::OutOfTls); } From 14b16dcf45d00875e87e609362efb7d7d6cd84c3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 10:19:38 -0700 Subject: [PATCH 0929/1096] use proper span for TLS dtors; fix some nits --- src/memory.rs | 1 - src/step.rs | 7 +++---- src/terminator/mod.rs | 12 ++++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 0280ad4c379bc..bfdc45c921d13 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -100,7 +100,6 @@ impl Pointer { } pub fn is_null_ptr(&self) -> bool { - // FIXME: Is this the right way? return *self == Pointer::from_int(0) } } diff --git a/src/step.rs b/src/step.rs index 658a3445c4331..84f849c612255 100644 --- a/src/step.rs +++ b/src/step.rs @@ -14,7 +14,7 @@ use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; use memory::Pointer; -use syntax::codemap::{Span, DUMMY_SP}; +use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { @@ -36,10 +36,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("Running TLS dtor {:?} on {:?}", instance, ptr); // TODO: Potientiually, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs let mir = self.load_mir(instance.def)?; - // FIXME: Are these the right dummy values? self.push_stack_frame( instance, - DUMMY_SP, + mir.span, mir, Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, @@ -51,7 +50,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { return Err(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned())); } - + return Ok(true); } return Ok(false); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index aaf03ea665f68..9d55b9720bb59 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::{self, TypeVariants, Ty, TyS, TypeAndMut}; +use rustc::ty::{self, TypeVariants, Ty, TypeAndMut}; use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; @@ -600,19 +600,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let key_ptr = args[0].read_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - // FIXME: Or should we instead construct the type we expect it to have? - let dtor_fn_ty = match self.operand_ty(&arg_operands[1]) { - &TyS { sty: TypeVariants::TyAdt(_, ref substs), .. } => { + let dtor_fn_ty = match self.operand_ty(&arg_operands[1]).sty { + TypeVariants::TyAdt(_, ref substs) => { substs.type_at(0) } _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: Second argument must be option of a function pointer.".to_owned())) }; let dtor_ptr = self.value_to_primval(args[1], dtor_fn_ty)?.to_ptr()?; + // TODO: The null-pointer case here is entirely untested let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. - let key_size = match self.operand_ty(&arg_operands[0]) { - &TyS { sty: TypeVariants::TyRawPtr(TypeAndMut { ty, .. }), .. } => { + let key_size = match self.operand_ty(&arg_operands[0]).sty { + TypeVariants::TyRawPtr(TypeAndMut { ty, .. }) => { let layout = self.type_layout(ty)?; layout.size(&self.tcx.data_layout) } From 55438fe5bf2eee99511d811898dd48a70a0cecd1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 12:25:25 -0700 Subject: [PATCH 0930/1096] unify the way we intercept missing MIR and C ABI calls; only intercept C ABI calls if MIR is missing --- src/terminator/mod.rs | 90 ++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9d55b9720bb59..119e7249a7c0c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -172,7 +172,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.eval_fn_call_inner( instance, destination, + arg_operands, span, + sig, )? { return Ok(()); } @@ -202,18 +204,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } ty::InstanceDef::Item(_) => { - match sig.abi { - Abi::C => { - let ty = sig.output(); - let (ret, target) = destination.unwrap(); - self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; - self.dump_local(ret); - self.goto_block(target); - return Ok(()); - }, - Abi::Rust | Abi::RustCall => {}, - _ => unimplemented!(), - } let mut args = Vec::new(); for arg in arg_operands { let arg_val = self.eval_operand(arg)?; @@ -225,7 +215,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.eval_fn_call_inner( instance, destination, + arg_operands, span, + sig, )? { return Ok(()); } @@ -236,7 +228,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); trace!("arg_operands: {:?}", arg_operands); match sig.abi { - Abi::Rust => { + Abi::Rust | Abi::C => { for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_value(arg_val, dest, arg_ty)?; @@ -316,7 +308,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.eval_fn_call_inner( instance, destination, + arg_operands, span, + sig, )? { return Ok(()); } @@ -363,7 +357,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, instance: ty::Instance<'tcx>, destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], span: Span, + sig: ty::FnSig<'tcx>, ) -> EvalResult<'tcx, bool> { trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination); @@ -372,28 +368,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { - return match &path[..] { - // Intercept some methods if we cannot find their MIR. - "std::io::_print" => { - trace!("Ignoring output."); - self.goto_block(destination.unwrap().1); - Ok(true) - }, - "std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), - "std::panicking::rust_panic_with_hook" | - "std::rt::begin_panic_fmt" => Err(EvalError::Panic), - "std::panicking::panicking" | - "std::rt::panicking" => { - let (lval, block) = destination.expect("std::rt::panicking does not diverge"); - // we abort on panic -> `std::rt::panicking` always returns false - let bool = self.tcx.types.bool; - self.write_primval(lval, PrimVal::from_bool(false), bool)?; - self.goto_block(block); - Ok(true) - } - _ => Err(EvalError::NoMirFor(path)), - }; + self.call_missing_fn(instance, destination, arg_operands, sig, path)?; + return Ok(true); }, Err(other) => return Err(other), }; @@ -466,6 +442,50 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert!(nndiscr == 0 || nndiscr == 1); Ok(if not_null { nndiscr } else { 1 - nndiscr }) } + + /// Returns Ok() when the function was handled, fail otherwise + fn call_missing_fn( + &mut self, + instance: ty::Instance<'tcx>, + destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>, + arg_operands: &[mir::Operand<'tcx>], + sig: ty::FnSig<'tcx>, + path: String, + ) -> EvalResult<'tcx> { + if sig.abi == Abi::C { + // An external C function + let ty = sig.output(); + let (ret, target) = destination.unwrap(); + self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; + self.dump_local(ret); + self.goto_block(target); + return Ok(()); + } + + // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). + // Still, we can make many things mostly work by "emulating" or ignoring some functions. + match &path[..] { + "std::io::_print" => { + trace!("Ignoring output."); + self.goto_block(destination.unwrap().1); + Ok(()) + }, + "std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => Err(EvalError::Panic), + "std::panicking::panicking" | + "std::rt::panicking" => { + let (lval, block) = destination.expect("std::rt::panicking does not diverge"); + // we abort on panic -> `std::rt::panicking` always returns false + let bool = self.tcx.types.bool; + self.write_primval(lval, PrimVal::from_bool(false), bool)?; + self.goto_block(block); + Ok(()) + } + _ => Err(EvalError::NoMirFor(path)), + } + } fn call_c_abi( &mut self, From 720c5f874ea2eeef247eb8dddd2dcf4b93dada7d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 17:27:39 -0700 Subject: [PATCH 0931/1096] implement __rust_maybe_catch_panic --- src/terminator/mod.rs | 39 ++++++++++++++++++++++++++++++++++----- tests/run-pass/catch.rs | 9 +++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass/catch.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 119e7249a7c0c..e017a87f03461 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -456,9 +456,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // An external C function let ty = sig.output(); let (ret, target) = destination.unwrap(); - self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?; - self.dump_local(ret); - self.goto_block(target); + self.call_c_abi(instance.def_id(), arg_operands, ret, ty, target)?; return Ok(()); } @@ -493,6 +491,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { arg_operands: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, + dest_block: mir::BasicBlock, ) -> EvalResult<'tcx> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); @@ -539,6 +538,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } + "__rust_maybe_catch_panic" => { + // We abort on panic, so not much is going on here, but we still have to call the closure + let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + let f = args[0].read_ptr(&self.memory)?; + let data = args[1].read_ptr(&self.memory)?; // FIXME: Why does value_to_primval(args[2], u8_ptr_ty)?.to_ptr()? here end up doing the Wrong Thing (TM)? + let f_instance = self.memory.get_fn(f.alloc_id)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + + // Now we make a functon call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, + // and of coruse eval_main. + let mir = self.load_mir(f_instance.def)?; + self.push_stack_frame( + f_instance, + mir.span, + mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::Goto(dest_block), + )?; + + let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(Value::ByVal(PrimVal::Ptr(data)), dest, u8_ptr_ty)?; + + // Don't fall through + // FIXME: Do we have to do self.dump_local(ret) anywhere? + return Ok(()); + } + "memcmp" => { let left = args[0].read_ptr(&self.memory)?; let right = args[1].read_ptr(&self.memory)?; @@ -591,7 +618,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } - + "write" => { let fd = self.value_to_primval(args[0], usize)?.to_u64()?; let buf = args[1].read_ptr(&self.memory)?; @@ -684,6 +711,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. + self.dump_local(dest); + self.goto_block(dest_block); Ok(()) - } + } } diff --git a/tests/run-pass/catch.rs b/tests/run-pass/catch.rs new file mode 100644 index 0000000000000..439edc82dde25 --- /dev/null +++ b/tests/run-pass/catch.rs @@ -0,0 +1,9 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn main() { + let mut i = 3; + let _ = catch_unwind(AssertUnwindSafe(|| {i -= 2;} )); + for _ in 0..i { + println!("I"); + } +} From cd6e3e643133fc1c6cdfd6067445236f9b776c7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 17:36:16 -0700 Subject: [PATCH 0932/1096] If a "start" lang item incl. MIR is present, run that instead of running main directly This fixes the memory leaks when running a simple "Hello World" with MIR-libstd --- src/bin/miri.rs | 6 +- src/eval_context.rs | 151 ++++++++++++++++++++++++++------------ src/step.rs | 12 +-- src/terminator/mod.rs | 30 +++++--- tests/compile-fail/oom.rs | 4 +- 5 files changed, 136 insertions(+), 67 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 3f0a6f778b425..4a82d45493b88 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -84,7 +84,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) { let did = self.1.hir.body_owner_def_id(body_id); println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); - miri::eval_main(self.1, did, self.0); + miri::eval_main(self.1, did, None, self.0); self.2.session.abort_if_errors(); } } @@ -95,7 +95,9 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); } else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { let entry_def_id = tcx.hir.local_def_id(entry_node_id); - miri::eval_main(tcx, entry_def_id, limits); + let start_wrapper = tcx.lang_items.start_fn() + .and_then(|start_fn| if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None }); + miri::eval_main(tcx, entry_def_id, start_wrapper, limits); state.session.abort_if_errors(); } else { diff --git a/src/eval_context.rs b/src/eval_context.rs index 77d9d9b20cd8f..9a57e75da090b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -126,6 +126,7 @@ impl Default for ResourceLimits { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { + // Register array drop glue code let source_info = mir::SourceInfo { span: DUMMY_SP, scope: mir::ARGUMENT_VISIBILITY_SCOPE @@ -852,7 +853,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; }, - ref other => bug!("reify fn pointer on {:?}", other), + ref other => bug!("closure fn pointer on {:?}", other), }, } } @@ -1676,62 +1677,120 @@ impl<'tcx> Frame<'tcx> { pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, + main_id: DefId, + start_wrapper: Option, limits: ResourceLimits, ) { - let mut ecx = EvalContext::new(tcx, limits); - let instance = ty::Instance::mono(tcx, def_id); - let mir = ecx.load_mir(instance.def).expect("main function's MIR not found"); - - if !mir.return_ty.is_nil() || mir.arg_count != 0 { - let msg = "miri does not support main functions without `fn()` type signatures"; - tcx.sess.err(&EvalError::Unimplemented(String::from(msg)).to_string()); - return; - } - - ecx.push_stack_frame( - instance, - DUMMY_SP, - mir, - Lvalue::from_ptr(Pointer::zst_ptr()), - StackPopCleanup::None, - ).expect("could not allocate first stack frame"); - - loop { - match ecx.step() { - Ok(true) => {} - Ok(false) => { - let leaks = ecx.memory.leak_report(); - if leaks != 0 { - tcx.sess.err("the evaluated program leaked memory"); - } - return; + fn run_main<'a, 'tcx: 'a>( + ecx: &mut EvalContext<'a, 'tcx>, + main_id: DefId, + start_wrapper: Option, + ) -> EvalResult<'tcx> { + let main_instance = ty::Instance::mono(ecx.tcx, main_id); + let main_mir = ecx.load_mir(main_instance.def)?; + + if !main_mir.return_ty.is_nil() || main_mir.arg_count != 0 { + return Err(EvalError::Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned())); + } + + if let Some(start_id) = start_wrapper { + let start_instance = ty::Instance::mono(ecx.tcx, start_id); + let start_mir = ecx.load_mir(start_instance.def)?; + + if start_mir.arg_count != 3 { + return Err(EvalError::AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count))); } - Err(e) => { - report(tcx, &ecx, e); - return; + + // Push our stack frame + ecx.push_stack_frame( + start_instance, + start_mir.span, + start_mir, + Lvalue::from_ptr(Pointer::zst_ptr()), // we'll fix the return lvalue later + StackPopCleanup::None, + )?; + + let mut args = ecx.frame().mir.args_iter(); + + // First argument: pointer to main() + let main_ptr = ecx.memory.create_fn_alloc(main_instance); + let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; + let main_ty = main_instance.def.def_ty(ecx.tcx); + let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig()); + ecx.write_value(Value::ByVal(PrimVal::Ptr(main_ptr)), dest, main_ptr_ty)?; + + // Second argument (argc): 0 + let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; + let ty = ecx.tcx.types.isize; + ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?; + + // Third argument (argv): 0 + let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; + let ty = ecx.tcx.mk_imm_ptr(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8)); + ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?; + } else { + ecx.push_stack_frame( + main_instance, + main_mir.span, + main_mir, + Lvalue::from_ptr(Pointer::zst_ptr()), + StackPopCleanup::None, + )?; + } + + // Allocate memory for the return value. We have to do this when a stack frame was already pushed as the type code below + // calls EvalContext::substs, which needs a frame to be allocated (?!?) + let ret_ptr = { + let ty = ecx.tcx.types.isize; + let layout = ecx.type_layout(ty)?; + let size = layout.size(&ecx.tcx.data_layout).bytes(); + let align = layout.align(&ecx.tcx.data_layout).pref(); // FIXME is this right? + ecx.memory.allocate(size, align)? + }; + ecx.frame_mut().return_lvalue = Lvalue::from_ptr(ret_ptr); + + loop { + if !ecx.step()? { + ecx.memory.deallocate(ret_ptr)?; + return Ok(()); + } + } + } + + let mut ecx = EvalContext::new(tcx, limits); + match run_main(&mut ecx, main_id, start_wrapper) { + Ok(()) => { + let leaks = ecx.memory.leak_report(); + if leaks != 0 { + tcx.sess.err("the evaluated program leaked memory"); } } + Err(e) => { + report(tcx, &ecx, e); + } } } fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { - let frame = ecx.stack().last().expect("stackframe was empty"); - let block = &frame.mir.basic_blocks()[frame.block]; - let span = if frame.stmt < block.statements.len() { - block.statements[frame.stmt].source_info.span - } else { - block.terminator().source_info.span - }; - let mut err = tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame { instance, span, .. } in ecx.stack().iter().rev() { - if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { - err.span_note(span, "inside call to closure"); - continue; + if let Some(frame) = ecx.stack().last() { + let block = &frame.mir.basic_blocks()[frame.block]; + let span = if frame.stmt < block.statements.len() { + block.statements[frame.stmt].source_info.span + } else { + block.terminator().source_info.span + }; + let mut err = tcx.sess.struct_span_err(span, &e.to_string()); + for &Frame { instance, span, .. } in ecx.stack().iter().rev() { + if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { + err.span_note(span, "inside call to closure"); + continue; + } + err.span_note(span, &format!("inside call to {}", instance)); } - err.span_note(span, &format!("inside call to {}", instance)); + err.emit(); + } else { + tcx.sess.err(&e.to_string()); } - err.emit(); } // TODO(solson): Upstream these methods into rustc::ty::layout. diff --git a/src/step.rs b/src/step.rs index 84f849c612255..81bac8c985b8c 100644 --- a/src/step.rs +++ b/src/step.rs @@ -43,14 +43,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::from_ptr(Pointer::zst_ptr()), StackPopCleanup::None, )?; - if let Some(arg_local) = self.frame().mir.args_iter().next() { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?; - } else { - return Err(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned())); - } - + let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?; return Ok(true); } return Ok(false); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index e017a87f03461..09361aa43b3b4 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -637,23 +637,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; } - // unix panic code inside libstd will read the return value of this function - "pthread_rwlock_rdlock" => { + // Some things needed for sys::thread initialization to go through + "signal" | "sigaction" | "sigaltstack" => { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } + "sysconf" => { + let name = self.value_to_primval(args[0], usize)?.to_u64()?; + trace!("sysconf() called with name {}", name); + let result = match name { + 30 => 4096, // _SC_PAGESIZE + _ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))) + }; + self.write_primval(dest, PrimVal::Bytes(result), dest_ty)?; + } + + "mmap" => { + // This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value + let addr = args[0].read_ptr(&self.memory)?; + self.write_primval(dest, PrimVal::Ptr(addr), dest_ty)?; + } + // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { let key_ptr = args[0].read_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor_fn_ty = match self.operand_ty(&arg_operands[1]).sty { - TypeVariants::TyAdt(_, ref substs) => { - substs.type_at(0) - } - _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: Second argument must be option of a function pointer.".to_owned())) - }; - let dtor_ptr = self.value_to_primval(args[1], dtor_fn_ty)?.to_ptr()?; + let dtor_ptr = args[1].read_ptr(&self.memory)?; // TODO: The null-pointer case here is entirely untested let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; @@ -699,8 +709,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } + // Stub out all the other pthread calls to just return 0 link_name if link_name.starts_with("pthread_") => { warn!("ignoring C ABI call: {}", link_name); + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; }, _ => { diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index 1fd2c4b2bd6aa..d15f1d6ae3e75 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute, attr_literals)] -#![miri(memory_size=0)] +#![miri(memory_size=20)] fn main() { let _x = [42; 10]; - //~^ERROR tried to allocate 40 more bytes, but only 0 bytes are free of the 0 byte memory + //~^ERROR tried to allocate 40 more bytes, but only } From 99433a1ffdb1724cbafeafba88d6d52fee579bd1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 20:02:51 -0700 Subject: [PATCH 0933/1096] improve fn pointer signature check to allow some casts that should be permitted Also properly check the "non-capturing Fn to fn" case --- src/terminator/mod.rs | 74 ++++++++++++++++++++++++++---- tests/compile-fail/cast_fn_ptr2.rs | 9 ++++ tests/run-pass/cast_fn_ptr.rs | 9 ++++ 3 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 tests/compile-fail/cast_fn_ptr2.rs create mode 100644 tests/run-pass/cast_fn_ptr.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 09361aa43b3b4..0fea3a174be16 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -72,15 +72,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFnDef(_, _, real_sig) => { let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); - match instance.def { - // FIXME: this needs checks for weird transmutes - // we need to bail here, because noncapturing closures as fn ptrs fail the checks - ty::InstanceDef::ClosureOnceShim{..} => {} - _ => if sig.abi != real_sig.abi || - sig.variadic != real_sig.variadic || - sig.inputs_and_output != real_sig.inputs_and_output { - return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); - }, + if !self.check_sig_compat(sig, real_sig)? { + return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); } }, ref other => bug!("instance def ty: {:?}", other), @@ -138,6 +131,69 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + /// Decides whether it is okay to call the method with signature `real_sig` using signature `sig` + fn check_sig_compat( + &mut self, + sig: ty::FnSig<'tcx>, + real_sig: ty::FnSig<'tcx>, + ) -> EvalResult<'tcx, bool> { + fn check_ty_compat<'tcx>( + ty: ty::Ty<'tcx>, + real_ty: ty::Ty<'tcx>, + ) -> bool { + if ty == real_ty { return true; } // This is actually a fast pointer comparison + return match (&ty.sty, &real_ty.sty) { + // Permit changing the pointer type of raw pointers and references as well as + // mutability of raw pointers. + // TODO: Should not be allowed when fat pointers are involved. + (&TypeVariants::TyRawPtr(_), &TypeVariants::TyRawPtr(_)) => true, + (&TypeVariants::TyRef(_, _), &TypeVariants::TyRef(_, _)) => + ty.is_mutable_pointer() == real_ty.is_mutable_pointer(), + // rule out everything else + _ => false + } + } + + if sig.abi == real_sig.abi && + sig.variadic == real_sig.variadic && + sig.inputs_and_output.len() == real_sig.inputs_and_output.len() && + sig.inputs_and_output.iter().zip(real_sig.inputs_and_output).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) { + // 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. + if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => { + // First argument of real_sig must be a ZST + let fst_ty = real_sig.inputs_and_output[0]; + let layout = self.type_layout(fst_ty)?; + let size = layout.size(&self.tcx.data_layout).bytes(); + if size == 0 { + // 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 { + TypeVariants::TyTuple(tys, _) if sig.inputs().len() == tys.len() => + if sig.inputs().iter().zip(tys).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) { + return Ok(true) + }, + _ => {} + } + } + } + _ => {} + }; + + // Nope, this doesn't work. + return Ok(false); + } + fn eval_fn_call( &mut self, instance: ty::Instance<'tcx>, diff --git a/tests/compile-fail/cast_fn_ptr2.rs b/tests/compile-fail/cast_fn_ptr2.rs new file mode 100644 index 0000000000000..5d902e1f9aaaf --- /dev/null +++ b/tests/compile-fail/cast_fn_ptr2.rs @@ -0,0 +1,9 @@ +fn main() { + fn f(_ : (i32,i32)) {} + + let g = unsafe { + std::mem::transmute::(f) + }; + + g(42) //~ ERROR tried to call a function with sig fn((i32, i32)) through a function pointer of type fn(i32) +} diff --git a/tests/run-pass/cast_fn_ptr.rs b/tests/run-pass/cast_fn_ptr.rs new file mode 100644 index 0000000000000..109e8dfc2a02b --- /dev/null +++ b/tests/run-pass/cast_fn_ptr.rs @@ -0,0 +1,9 @@ +fn main() { + fn f(_: *const u8) {} + + let g = unsafe { + std::mem::transmute::(f) + }; + + g(&42 as *const _); +} From 1241938f97622e19d79528350670707c53dd26a6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 20:27:39 -0700 Subject: [PATCH 0934/1096] test suite now also passes on MIR-libstd Also enable some tests that were disabled for no apparant reason. (The comment in zst.rs was wrong, the test was disabled also for miri execution.) Delete env_args test as the args can actually be queried with MIR-libstd (currently, they are always empty) --- src/step.rs | 2 +- src/terminator/mod.rs | 4 ++++ tests/compile-fail/env_args.rs | 4 ---- tests/compile-fail/oom.rs | 6 +++--- tests/compile-fail/oom2.rs | 2 +- tests/compile-fail/stack_limit.rs | 12 +++++++++--- tests/run-pass/aux_test.rs | 1 - tests/run-pass/hello.rs | 3 +++ tests/run-pass/zst.rs | 6 ------ 9 files changed, 21 insertions(+), 19 deletions(-) delete mode 100644 tests/compile-fail/env_args.rs create mode 100644 tests/run-pass/hello.rs diff --git a/src/step.rs b/src/step.rs index 81bac8c985b8c..cbd9871e83fa3 100644 --- a/src/step.rs +++ b/src/step.rs @@ -34,7 +34,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.stack.is_empty() { if let Some((instance, ptr)) = self.memory.fetch_tls_dtor() { trace!("Running TLS dtor {:?} on {:?}", instance, ptr); - // TODO: Potientiually, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs + // TODO: Potientially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs let mir = self.load_mir(instance.def)?; self.push_stack_frame( instance, diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 0fea3a174be16..95901c56e88ed 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -622,6 +622,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); } + "__rust_start_panic" => { + return Err(EvalError::Panic); + } + "memcmp" => { let left = args[0].read_ptr(&self.memory)?; let right = args[1].read_ptr(&self.memory)?; diff --git a/tests/compile-fail/env_args.rs b/tests/compile-fail/env_args.rs deleted file mode 100644 index fe17e0a7b4897..0000000000000 --- a/tests/compile-fail/env_args.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let x = std::env::args(); //~ ERROR miri does not support program arguments - assert_eq!(x.count(), 1); -} diff --git a/tests/compile-fail/oom.rs b/tests/compile-fail/oom.rs index d15f1d6ae3e75..d4aebb912ee17 100644 --- a/tests/compile-fail/oom.rs +++ b/tests/compile-fail/oom.rs @@ -1,7 +1,7 @@ #![feature(custom_attribute, attr_literals)] -#![miri(memory_size=20)] +#![miri(memory_size=4095)] fn main() { - let _x = [42; 10]; - //~^ERROR tried to allocate 40 more bytes, but only + let _x = [42; 1024]; + //~^ERROR tried to allocate 4096 more bytes, but only } diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index a87e34474cff3..1a4a47efe6858 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -1,5 +1,5 @@ #![feature(box_syntax, custom_attribute, attr_literals)] -#![miri(memory_size=1000)] +#![miri(memory_size=2048)] fn main() { loop { diff --git a/tests/compile-fail/stack_limit.rs b/tests/compile-fail/stack_limit.rs index 2a78fbe5398f0..4b61e12d602fc 100644 --- a/tests/compile-fail/stack_limit.rs +++ b/tests/compile-fail/stack_limit.rs @@ -1,5 +1,5 @@ #![feature(custom_attribute, attr_literals)] -#![miri(stack_limit=2)] +#![miri(stack_limit=16)] fn bar() { foo(); @@ -10,10 +10,16 @@ fn foo() { } fn cake() { - flubber(); + flubber(3); } -fn flubber() {} +fn flubber(i: u32) { + if i > 0 { + flubber(i-1); + } else { + bar(); + } +} fn main() { bar(); diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index 1b1dbaa68387e..aa471f6cf8fdf 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,5 +1,4 @@ // aux-build:dep.rs -// ignore-cross-compile extern crate dep; diff --git a/tests/run-pass/hello.rs b/tests/run-pass/hello.rs new file mode 100644 index 0000000000000..e7a11a969c037 --- /dev/null +++ b/tests/run-pass/hello.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs index 78d3025587f04..06e41e59e6023 100644 --- a/tests/run-pass/zst.rs +++ b/tests/run-pass/zst.rs @@ -1,9 +1,3 @@ -// the following flag prevents this test from running on the host machine -// this should only be run on miri, because rust doesn't (yet?) optimize ZSTs of different types -// into the same memory location -// ignore-test - - #[derive(PartialEq, Debug)] struct A; From 633a34d6d35b7d73e929ecb38bd90d9298092fe2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 May 2017 20:38:22 -0700 Subject: [PATCH 0935/1096] re-disable aux_test -- it passes here, but not on Travis --- tests/run-pass/aux_test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index aa471f6cf8fdf..1b1dbaa68387e 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,4 +1,5 @@ // aux-build:dep.rs +// ignore-cross-compile extern crate dep; From 24a9a14dfa2e14250e8bb60cc70a2a6b20260980 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 10:24:37 -0700 Subject: [PATCH 0936/1096] fix various small nits --- src/bin/miri.rs | 4 ++-- src/eval_context.rs | 12 +++++------- src/terminator/mod.rs | 33 ++++++++++++++++++--------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4a82d45493b88..3af3bee3c8bad 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -95,8 +95,8 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); } else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { let entry_def_id = tcx.hir.local_def_id(entry_node_id); - let start_wrapper = tcx.lang_items.start_fn() - .and_then(|start_fn| if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None }); + let start_wrapper = tcx.lang_items.start_fn().and_then(|start_fn| + if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None }); miri::eval_main(tcx, entry_def_id, start_wrapper, limits); state.session.abort_if_errors(); diff --git a/src/eval_context.rs b/src/eval_context.rs index 9a57e75da090b..b44116e5b1018 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1558,6 +1558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { + // Debug output if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); @@ -1744,17 +1745,14 @@ pub fn eval_main<'a, 'tcx: 'a>( let ty = ecx.tcx.types.isize; let layout = ecx.type_layout(ty)?; let size = layout.size(&ecx.tcx.data_layout).bytes(); - let align = layout.align(&ecx.tcx.data_layout).pref(); // FIXME is this right? + let align = layout.align(&ecx.tcx.data_layout).abi(); ecx.memory.allocate(size, align)? }; ecx.frame_mut().return_lvalue = Lvalue::from_ptr(ret_ptr); - loop { - if !ecx.step()? { - ecx.memory.deallocate(ret_ptr)?; - return Ok(()); - } - } + while ecx.step()? {} + ecx.memory.deallocate(ret_ptr)?; + return Ok(()); } let mut ecx = EvalContext::new(tcx, limits); diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 95901c56e88ed..799c30f0d4e38 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -131,7 +131,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - /// Decides whether it is okay to call the method with signature `real_sig` using signature `sig` + /// Decides whether it is okay to call the method with signature `real_sig` using signature `sig`. + /// FIXME: This should take into account the platform-dependent ABI description. fn check_sig_compat( &mut self, sig: ty::FnSig<'tcx>, @@ -284,12 +285,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::>()); trace!("arg_operands: {:?}", arg_operands); match sig.abi { - Abi::Rust | Abi::C => { - for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(arg_val, dest, arg_ty)?; - } - } Abi::RustCall => { assert_eq!(args.len(), 2); @@ -332,8 +327,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout); } + }, + _ => { + for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) { + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(arg_val, dest, arg_ty)?; + } } - _ => unimplemented!(), } Ok(()) }, @@ -520,7 +520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Still, we can make many things mostly work by "emulating" or ignoring some functions. match &path[..] { "std::io::_print" => { - trace!("Ignoring output."); + trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR."); self.goto_block(destination.unwrap().1); Ok(()) }, @@ -595,15 +595,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "__rust_maybe_catch_panic" => { + // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); let f = args[0].read_ptr(&self.memory)?; - let data = args[1].read_ptr(&self.memory)?; // FIXME: Why does value_to_primval(args[2], u8_ptr_ty)?.to_ptr()? here end up doing the Wrong Thing (TM)? + let data = args[1].read_ptr(&self.memory)?; let f_instance = self.memory.get_fn(f.alloc_id)?; self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; - // Now we make a functon call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, - // and of coruse eval_main. + // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, + // and of course eval_main. let mir = self.load_mir(f_instance.def)?; self.push_stack_frame( f_instance, @@ -614,11 +615,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { )?; let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(Value::ByVal(PrimVal::Ptr(data)), dest, u8_ptr_ty)?; + let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + self.write_value(Value::ByVal(PrimVal::Ptr(data)), arg_dest, u8_ptr_ty)?; + + // We ourselbes return 0 + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; // Don't fall through - // FIXME: Do we have to do self.dump_local(ret) anywhere? return Ok(()); } From dad95474cbf48ad02c768a2a42a2c55150bad67f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 10:41:22 -0700 Subject: [PATCH 0937/1096] test thread-local key with no dtor --- src/terminator/mod.rs | 1 - tests/run-pass/thread-local-no-dtor.rs | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/thread-local-no-dtor.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 799c30f0d4e38..81927ba32996b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -727,7 +727,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Extract the function type out of the signature (that seems easier than constructing it ourselves...) let dtor_ptr = args[1].read_ptr(&self.memory)?; - // TODO: The null-pointer case here is entirely untested let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. diff --git a/tests/run-pass/thread-local-no-dtor.rs b/tests/run-pass/thread-local-no-dtor.rs new file mode 100644 index 0000000000000..8c69be8e2cd79 --- /dev/null +++ b/tests/run-pass/thread-local-no-dtor.rs @@ -0,0 +1,16 @@ +#![feature(libc)] +extern crate libc; + +use std::mem; + +pub type Key = libc::pthread_key_t; + +pub unsafe fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +fn main() { + let _ = unsafe { create(None) }; +} From cdf7a057f1a6cdb96ddee81cfbcc3a059e27cf2d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 13:19:15 -0700 Subject: [PATCH 0938/1096] latest rust nightly contains all the bits needed to re-compile libstd --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa89df9731fa3..1340ddb5b879d 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ miri hits a call to such a function, execution terminates. To fix this, it is possible to compile libstd with full MIR: ```sh +rustup component add rust-src +chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/ cargo install xargo cd xargo/ RUSTFLAGS='-Zalways-encode-mir' xargo build --target `rustc -vV | egrep '^host: ' | sed 's/^host: //'` @@ -73,8 +75,7 @@ cargo run --bin miri -- --sysroot ~/.xargo/HOST tests/run-pass/vecs.rs ``` Notice that you will have to re-run the last step of the preparations above when -your toolchain changes (e.g., when you update the nightly). Also, xargo doesn't -currently work with nightlies newer than 2017-04-23. +your toolchain changes (e.g., when you update the nightly). ## Contributing and getting help From b8e0b792222af098f01d778cccb6efb5affde52a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 14:02:20 -0700 Subject: [PATCH 0939/1096] add a test for output string formatting --- tests/run-pass/format.rs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/run-pass/format.rs diff --git a/tests/run-pass/format.rs b/tests/run-pass/format.rs new file mode 100644 index 0000000000000..78729b915613a --- /dev/null +++ b/tests/run-pass/format.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello {}", 13); +} From b44babf23039ed1132b0332c71e1bb959995949b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 15:39:55 -0700 Subject: [PATCH 0940/1096] allocate return pointer only when we start the program via the start lang item --- src/eval_context.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b44116e5b1018..85c23ef029412 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1689,6 +1689,7 @@ pub fn eval_main<'a, 'tcx: 'a>( ) -> EvalResult<'tcx> { let main_instance = ty::Instance::mono(ecx.tcx, main_id); let main_mir = ecx.load_mir(main_instance.def)?; + let mut cleanup_ptr = None; // Pointer to be deallocated when we are done if !main_mir.return_ty.is_nil() || main_mir.arg_count != 0 { return Err(EvalError::Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned())); @@ -1702,12 +1703,22 @@ pub fn eval_main<'a, 'tcx: 'a>( return Err(EvalError::AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count))); } + // Return value + let ret_ptr = { + let ty = ecx.tcx.types.isize; + let layout = ecx.type_layout_with_substs(ty, Substs::empty())?; + let size = layout.size(&ecx.tcx.data_layout).bytes(); + let align = layout.align(&ecx.tcx.data_layout).abi(); + ecx.memory.allocate(size, align)? + }; + cleanup_ptr = Some(ret_ptr); + // Push our stack frame ecx.push_stack_frame( start_instance, start_mir.span, start_mir, - Lvalue::from_ptr(Pointer::zst_ptr()), // we'll fix the return lvalue later + Lvalue::from_ptr(ret_ptr), StackPopCleanup::None, )?; @@ -1739,19 +1750,10 @@ pub fn eval_main<'a, 'tcx: 'a>( )?; } - // Allocate memory for the return value. We have to do this when a stack frame was already pushed as the type code below - // calls EvalContext::substs, which needs a frame to be allocated (?!?) - let ret_ptr = { - let ty = ecx.tcx.types.isize; - let layout = ecx.type_layout(ty)?; - let size = layout.size(&ecx.tcx.data_layout).bytes(); - let align = layout.align(&ecx.tcx.data_layout).abi(); - ecx.memory.allocate(size, align)? - }; - ecx.frame_mut().return_lvalue = Lvalue::from_ptr(ret_ptr); - while ecx.step()? {} - ecx.memory.deallocate(ret_ptr)?; + if let Some(cleanup_ptr) = cleanup_ptr { + ecx.memory.deallocate(cleanup_ptr)?; + } return Ok(()); } From 2b37d500f1f7dfce389a1cc93aefc88003899699 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 17:03:45 -0700 Subject: [PATCH 0941/1096] simplify determining size and alignment of a pointer --- src/eval_context.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 85c23ef029412..922a36c892ae5 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1704,13 +1704,7 @@ pub fn eval_main<'a, 'tcx: 'a>( } // Return value - let ret_ptr = { - let ty = ecx.tcx.types.isize; - let layout = ecx.type_layout_with_substs(ty, Substs::empty())?; - let size = layout.size(&ecx.tcx.data_layout).bytes(); - let align = layout.align(&ecx.tcx.data_layout).abi(); - ecx.memory.allocate(size, align)? - }; + let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi())?; cleanup_ptr = Some(ret_ptr); // Push our stack frame From d06c1653689d255bb66f0f778f870067f66b51e8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 18:12:06 -0700 Subject: [PATCH 0942/1096] simplify xargo instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1340ddb5b879d..fa3fea79e9407 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ rustup component add rust-src chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/ cargo install xargo cd xargo/ -RUSTFLAGS='-Zalways-encode-mir' xargo build --target `rustc -vV | egrep '^host: ' | sed 's/^host: //'` +RUSTFLAGS='-Zalways-encode-mir' xargo build ``` Now you can run miri against the libstd compiled by xargo: From fbc46276ff0b2823a4f96fdd3285a708700fbd9f Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 31 May 2017 10:43:36 -0400 Subject: [PATCH 0943/1096] prevent 'attempt to shift left with overflow' panic on platforms where pthread_key_t is 64 bits --- src/terminator/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 81927ba32996b..3c254734b328d 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -724,11 +724,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { let key_ptr = args[0].read_ptr(&self.memory)?; - + // Extract the function type out of the signature (that seems easier than constructing it ourselves...) let dtor_ptr = args[1].read_ptr(&self.memory)?; let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; - + // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. let key_size = match self.operand_ty(&arg_operands[0]).sty { TypeVariants::TyRawPtr(TypeAndMut { ty, .. }) => { @@ -737,14 +737,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned())) }; - + // Create key and write it into the memory where key_ptr wants it - let key = self.memory.create_tls_key(dtor); - if key >= (1 << key_size.bits()) { + let key = self.memory.create_tls_key(dtor) as u128; + if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) { return Err(EvalError::OutOfTls); } - self.memory.write_int(key_ptr, key as i128, key_size.bytes())?; - + self.memory.write_uint(key_ptr, key, key_size.bytes())?; + // Return success (0) self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } From 7624bca09e367d50d264d5b2304c9e55db111838 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 31 May 2017 10:47:26 -0400 Subject: [PATCH 0944/1096] ignore thread-local-no-dtor test on windows-gnu target --- tests/run-pass/thread-local-no-dtor.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-pass/thread-local-no-dtor.rs b/tests/run-pass/thread-local-no-dtor.rs index 8c69be8e2cd79..d5e2051edb702 100644 --- a/tests/run-pass/thread-local-no-dtor.rs +++ b/tests/run-pass/thread-local-no-dtor.rs @@ -1,3 +1,5 @@ +//ignore-windows-gnu + #![feature(libc)] extern crate libc; From 6619ed89baf3153bb8137e77552bcc7bdf31e3fa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 14:05:41 -0700 Subject: [PATCH 0945/1096] bin/miri.rs looks for RUST_SYSROOT, so should bin/cargo-miri.rs --- src/bin/cargo-miri.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs index b643c83395fd6..f67bbd39a7482 100644 --- a/src/bin/cargo-miri.rs +++ b/src/bin/cargo-miri.rs @@ -103,7 +103,7 @@ fn main() { let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) { format!("{}/toolchains/{}", home, toolchain) } else { - option_env!("SYSROOT") + option_env!("RUST_SYSROOT") .map(|s| s.to_owned()) .or_else(|| { Command::new("rustc") @@ -114,7 +114,7 @@ fn main() { .and_then(|out| String::from_utf8(out.stdout).ok()) .map(|s| s.trim().to_owned()) }) - .expect("need to specify SYSROOT env var during miri compilation, or use rustup or multirust") + .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust") }; // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly From a59d482574f6978f0226c6651f341b3f484045e6 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 31 May 2017 14:21:49 -0400 Subject: [PATCH 0946/1096] remove unneeded '-gnu' suffix from compiletest ignore directives --- tests/run-pass/send-is-not-static-par-for.rs | 2 +- tests/run-pass/thread-local-no-dtor.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/send-is-not-static-par-for.rs b/tests/run-pass/send-is-not-static-par-for.rs index 60e5048432bac..4ac1b5436f522 100644 --- a/tests/run-pass/send-is-not-static-par-for.rs +++ b/tests/run-pass/send-is-not-static-par-for.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//ignore-windows-gnu +//ignore-windows use std::sync::Mutex; diff --git a/tests/run-pass/thread-local-no-dtor.rs b/tests/run-pass/thread-local-no-dtor.rs index d5e2051edb702..4fb43793eaec5 100644 --- a/tests/run-pass/thread-local-no-dtor.rs +++ b/tests/run-pass/thread-local-no-dtor.rs @@ -1,4 +1,4 @@ -//ignore-windows-gnu +//ignore-windows #![feature(libc)] extern crate libc; From 44a45f7c34e600c81c965794919efc15327f5eda Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 30 May 2017 14:09:40 -0700 Subject: [PATCH 0947/1096] run test suite also against libstd with full MIR --- .travis.yml | 17 +++++++++++++++-- README.md | 2 +- src/bin/miri.rs | 4 ++++ tests/compile-fail/stack_limit.rs | 14 ++++---------- tests/compiletest.rs | 18 ++++++++++-------- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index e6450622b2e88..7cc14234efd58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,16 +6,29 @@ before_script: - rustup target add i686-unknown-linux-gnu - rustup target add i686-pc-windows-gnu - rustup target add i686-pc-windows-msvc +- rustup component add rust-src +- chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/ +- cargo install xargo +- export RUST_SYSROOT=$HOME/rust script: - | - export RUST_SYSROOT=$HOME/rust && + # Test plain miri cargo build && cargo test && - cargo install && + cargo install +- | + # Test cargo miri cd cargo-miri-test && cargo miri && cargo miri test && cd .. +- | + # get ourselves a MIR-ful libstd + cd xargo && + RUSTFLAGS='-Zalways-encode-mir' xargo build && + cd .. && + # and run the tests with it + MIRI_SYSROOT=~/.xargo/HOST cargo test notifications: email: on_success: never diff --git a/README.md b/README.md index fa3fea79e9407..d5873c986815d 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ RUSTFLAGS='-Zalways-encode-mir' xargo build Now you can run miri against the libstd compiled by xargo: ```sh -cargo run --bin miri -- --sysroot ~/.xargo/HOST tests/run-pass/vecs.rs +MIRI_SYSROOT=~/.xargo/HOST cargo run --bin miri tests/run-pass/vecs.rs ``` Notice that you will have to re-run the last step of the preparations above when diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 3af3bee3c8bad..8d27f9057f540 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -175,6 +175,10 @@ fn init_logger() { } fn find_sysroot() -> String { + if let Ok(sysroot) = std::env::var("MIRI_SYSROOT") { + return sysroot; + } + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); diff --git a/tests/compile-fail/stack_limit.rs b/tests/compile-fail/stack_limit.rs index 4b61e12d602fc..c6aaf80e6ac00 100644 --- a/tests/compile-fail/stack_limit.rs +++ b/tests/compile-fail/stack_limit.rs @@ -1,24 +1,18 @@ #![feature(custom_attribute, attr_literals)] #![miri(stack_limit=16)] +//error-pattern: reached the configured maximum number of stack frames + fn bar() { foo(); } fn foo() { - cake(); //~ ERROR reached the configured maximum number of stack frames + cake(); } fn cake() { - flubber(3); -} - -fn flubber(i: u32) { - if i > 0 { - flubber(i-1); - } else { - bar(); - } + bar(); } fn main() { diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 78b2a2f3ce252..e6535ef0212b5 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -65,14 +65,16 @@ fn for_all_targets(sysroot: &Path, mut f: F) { #[test] fn compile_test() { - let sysroot = std::process::Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output() - .expect("rustc not found") - .stdout; - let sysroot = std::str::from_utf8(&sysroot).expect("sysroot is not utf8").trim(); - let sysroot = &Path::new(&sysroot); + let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| { + let sysroot = std::process::Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output() + .expect("rustc not found") + .stdout; + String::from_utf8(sysroot).expect("sysroot is not utf8") + }); + let sysroot = &Path::new(sysroot.trim()); let host = std::process::Command::new("rustc") .arg("-vV") .output() From e6eaf2083ac66ba51ef05ff59ee1e65a43decddc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 31 May 2017 17:41:33 -0700 Subject: [PATCH 0948/1096] interpret StorageLive & StorageDead, and check dead stack slots are not used --- src/error.rs | 3 + src/eval_context.rs | 151 +++++++++++++++++++++++++++++++------------- src/lvalue.rs | 4 +- src/step.rs | 20 ++++-- 4 files changed, 128 insertions(+), 50 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7b6542bff9345..702c2c4940fac 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,7 @@ pub enum EvalError<'tcx> { ReadPointerAsBytes, InvalidPointerMath, ReadUndefBytes, + DeadLocal, InvalidBoolOp(mir::BinOp), Unimplemented(String), DerefFunctionPointer, @@ -83,6 +84,8 @@ impl<'tcx> Error for EvalError<'tcx> { "attempted to do math or a comparison on pointers into different allocations", EvalError::ReadUndefBytes => "attempted to read undefined bytes", + EvalError::DeadLocal => + "tried to access a dead local variable", EvalError::InvalidBoolOp(_) => "invalid boolean operation", EvalError::Unimplemented(ref msg) => msg, diff --git a/src/eval_context.rs b/src/eval_context.rs index 922a36c892ae5..c704ab230d58e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Write; use rustc::hir::def_id::DefId; @@ -74,11 +74,12 @@ pub struct Frame<'tcx> { pub return_lvalue: Lvalue<'tcx>, /// The list of locals for this stack frame, stored in order as - /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Value`s, which + /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option`s. + /// `None` represents a local that is currently dead, while a live local /// can either directly contain `PrimVal` or refer to some part of an `Allocation`. /// - /// Before being initialized, all locals are `Value::ByVal(PrimVal::Undef)`. - pub locals: Vec, + /// Before being initialized, arguments are `Value::ByVal(PrimVal::Undef)` and other locals are `None`. + pub locals: Vec>, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -452,10 +453,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; + /// Return the set of locals that have a stroage annotation anywhere + fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet { + use rustc::mir::StatementKind::*; + + let mut set = HashSet::new(); + for block in mir.basic_blocks() { + for stmt in block.statements.iter() { + match stmt.kind { + StorageLive(mir::Lvalue::Local(local)) | StorageDead(mir::Lvalue::Local(local)) => { + set.insert(local); + } + _ => {} + } + } + }; + set + } + // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local // `Value` for that. + let annotated_locals = collect_storage_annotations(mir); let num_locals = mir.local_decls.len() - 1; - let locals = vec![Value::ByVal(PrimVal::Undef); num_locals]; + let mut locals = Vec::with_capacity(num_locals); + for i in 0..num_locals { + let local = mir::Local::new(i+1); + locals.push(if annotated_locals.contains(&local) { None } else { Some(Value::ByVal(PrimVal::Undef)) }); + } self.stack.push(Frame { mir, @@ -509,21 +533,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // deallocate all locals that are backed by an allocation for local in frame.locals { - if let Value::ByRef(ptr) = local { - trace!("deallocating local"); - self.memory.dump_alloc(ptr.alloc_id); - match self.memory.deallocate(ptr) { - // We could alternatively check whether the alloc_id is static before calling - // deallocate, but this is much simpler and is probably the rare case. - Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, - other => return other, - } - } + self.deallocate_local(local)?; } Ok(()) } + pub fn deallocate_local(&mut self, local: Option) -> EvalResult<'tcx> { + if let Some(Value::ByRef(ptr)) = local { + trace!("deallocating local"); + self.memory.dump_alloc(ptr.alloc_id); + match self.memory.deallocate(ptr) { + // We could alternatively check whether the alloc_id is static before calling + // deallocate, but this is much simpler and is probably the rare case. + Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, + other => return other, + } + }; + Ok(()) + } + pub fn assign_discr_and_fields< V: IntoValTyPair<'tcx>, J: IntoIterator, @@ -1047,16 +1076,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local, field } => { // -1 since we don't store the return value match self.stack[frame].locals[local.index() - 1] { - Value::ByRef(ptr) => { + None => return Err(EvalError::DeadLocal), + Some(Value::ByRef(ptr)) => { assert!(field.is_none()); Lvalue::from_ptr(ptr) }, - val => { + Some(val) => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); + self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr)); // it stays live self.write_value_to_ptr(val, ptr, ty)?; let lval = Lvalue::from_ptr(ptr); if let Some((field, field_ty)) = field { @@ -1139,7 +1169,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { *this.globals.get_mut(&cid).expect("already checked") = Global { value: val, ..dest - } + }; + Ok(()) }; self.write_value_possibly_by_val(src_val, write_dest, dest.value, dest_ty) }, @@ -1150,7 +1181,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Lvalue::Local { frame, local, field } => { - let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i)); + let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i))?; self.write_value_possibly_by_val( src_val, |this, val| this.stack[frame].set_local(local, field.map(|(i, _)| i), val), @@ -1162,7 +1193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // The cases here can be a bit subtle. Read carefully! - fn write_value_possibly_by_val( + fn write_value_possibly_by_val EvalResult<'tcx>>( &mut self, src_val: Value, write_dest: F, @@ -1192,17 +1223,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // source and write that into the destination without making an allocation, so // we do so here. if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { - write_dest(self, src_val); + write_dest(self, src_val)?; } else { let dest_ptr = self.alloc_ptr(dest_ty)?; self.copy(src_ptr, dest_ptr, dest_ty)?; - write_dest(self, Value::ByRef(dest_ptr)); + write_dest(self, Value::ByRef(dest_ptr))?; } } else { // Finally, we have the simple case where neither source nor destination are // `ByRef`. We may simply copy the source value over the the destintion. - write_dest(self, src_val); + write_dest(self, src_val)?; } Ok(()) } @@ -1572,14 +1603,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { write!(msg, ":").unwrap(); match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { - Value::ByRef(ptr) => { + Err(EvalError::DeadLocal) => { + write!(msg, " is dead").unwrap(); + } + Err(err) => { + panic!("Failed to access local: {:?}", err); + } + Ok(Value::ByRef(ptr)) => { allocs.push(ptr.alloc_id); } - Value::ByVal(val) => { + Ok(Value::ByVal(val)) => { write!(msg, " {:?}", val).unwrap(); if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } } - Value::ByValPair(val1, val2) => { + Ok(Value::ByValPair(val1, val2)) => { write!(msg, " ({:?}, {:?})", val1, val2).unwrap(); if let PrimVal::Ptr(ptr) = val1 { allocs.push(ptr.alloc_id); } if let PrimVal::Ptr(ptr) = val2 { allocs.push(ptr.alloc_id); } @@ -1614,9 +1651,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let val = self.stack[frame].get_local(local, field); + let val = self.stack[frame].get_local(local, field)?; let new_val = f(self, val)?; - self.stack[frame].set_local(local, field, new_val); + self.stack[frame].set_local(local, field, new_val)?; // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; @@ -1626,53 +1663,79 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { - pub fn get_local(&self, local: mir::Local, field: Option) -> Value { + pub fn get_local(&self, local: mir::Local, field: Option) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. if let Some(field) = field { - match self.locals[local.index() - 1] { - Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), - val @ Value::ByVal(_) => { + Ok(match self.locals[local.index() - 1] { + None => return Err(EvalError::DeadLocal), + Some(Value::ByRef(_)) => bug!("can't have lvalue fields for ByRef"), + Some(val @ Value::ByVal(_)) => { assert_eq!(field, 0); val }, - Value::ByValPair(a, b) => { + Some(Value::ByValPair(a, b)) => { match field { 0 => Value::ByVal(a), 1 => Value::ByVal(b), _ => bug!("ByValPair has only two fields, tried to access {}", field), } }, - } + }) } else { - self.locals[local.index() - 1] + self.locals[local.index() - 1].ok_or(EvalError::DeadLocal) } } - fn set_local(&mut self, local: mir::Local, field: Option, value: Value) { + fn set_local(&mut self, local: mir::Local, field: Option, value: Value) -> EvalResult<'tcx> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. if let Some(field) = field { match self.locals[local.index() - 1] { - Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), - Value::ByVal(_) => { + None => return Err(EvalError::DeadLocal), + Some(Value::ByRef(_)) => bug!("can't have lvalue fields for ByRef"), + Some(Value::ByVal(_)) => { assert_eq!(field, 0); - self.set_local(local, None, value); + self.set_local(local, None, value)?; }, - Value::ByValPair(a, b) => { + Some(Value::ByValPair(a, b)) => { let prim = match value { Value::ByRef(_) => bug!("can't set ValPair field to ByRef"), Value::ByVal(val) => val, Value::ByValPair(_, _) => bug!("can't set ValPair field to ValPair"), }; match field { - 0 => self.set_local(local, None, Value::ByValPair(prim, b)), - 1 => self.set_local(local, None, Value::ByValPair(a, prim)), + 0 => self.set_local(local, None, Value::ByValPair(prim, b))?, + 1 => self.set_local(local, None, Value::ByValPair(a, prim))?, _ => bug!("ByValPair has only two fields, tried to access {}", field), } }, } } else { - self.locals[local.index() - 1] = value; + match self.locals[local.index() - 1] { + None => return Err(EvalError::DeadLocal), + Some(ref mut local) => { *local = value; } + } + } + return Ok(()); + } + + pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx> { + trace!("{:?} is now live", local); + if self.locals[local.index() - 1].is_some() { + // The variables comes live now, but was already accessed previously, when it was still dead + return Err(EvalError::DeadLocal); + } else { + self.locals[local.index() - 1] = Some(Value::ByVal(PrimVal::Undef)); } + return Ok(()); + } + + /// Returns the old value of the local + pub fn storage_dead(&mut self, local: mir::Local) -> EvalResult<'tcx, Option> { + trace!("{:?} is now dead", local); + + let old = self.locals[local.index() - 1]; + self.locals[local.index() - 1] = None; + return Ok(old); } } diff --git a/src/lvalue.rs b/src/lvalue.rs index cad4ca9d0290b..a09f72134e9d8 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByRef(ptr)) } Lvalue::Local { frame, local, field } => { - Ok(self.stack[frame].get_local(local, field.map(|(i, _)| i))) + Ok(self.stack[frame].get_local(local, field.map(|(i, _)| i))?) } Lvalue::Global(cid) => { Ok(self.globals.get(&cid).expect("global not cached").value) @@ -226,7 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (base_ptr, base_extra) = match base { Lvalue::Ptr { ptr, extra } => (ptr, extra), - Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { + Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i))? { Value::ByRef(ptr) => { assert!(field.is_none(), "local can't be ByRef and have a field offset"); (ptr, LvalueExtra::None) diff --git a/src/step.rs b/src/step.rs index cbd9871e83fa3..1df55a8b620d5 100644 --- a/src/step.rs +++ b/src/step.rs @@ -126,9 +126,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // Miri can safely ignore these. Only translation needs it. - StorageLive(_) | - StorageDead(_) => {} + // Mark locals as dead or alive. + StorageLive(ref lvalue) | StorageDead(ref lvalue)=> { + let (frame, local) = match self.eval_lvalue(lvalue)? { + Lvalue::Local{ frame, local, field: None } if self.stack.len() == frame+1 => (frame, local), + _ => return Err(EvalError::Unimplemented("Stroage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type + }; + match stmt.kind { + StorageLive(_) => self.stack[frame].storage_live(local)?, + _ => { + let old_val = self.stack[frame].storage_dead(local)?; + self.deallocate_local(old_val)?; + } + }; + } // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. @@ -240,7 +251,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { constant.span, mir, Lvalue::Global(cid), - StackPopCleanup::MarkStatic(false)) + StackPopCleanup::MarkStatic(false), + ) }); } } From db6ce463fe3d40dac627378bfbaeaa700da43a5c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 1 Jun 2017 11:01:55 -0700 Subject: [PATCH 0949/1096] fix some nits --- src/eval_context.rs | 8 +++++--- src/lvalue.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index c704ab230d58e..f364f829f9032 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -453,7 +453,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; - /// Return the set of locals that have a stroage annotation anywhere + /// Return the set of locals that have a storage annotation anywhere fn collect_storage_annotations<'tcx>(mir: &'tcx mir::Mir<'tcx>) -> HashSet { use rustc::mir::StatementKind::*; @@ -475,10 +475,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // `Value` for that. let annotated_locals = collect_storage_annotations(mir); let num_locals = mir.local_decls.len() - 1; - let mut locals = Vec::with_capacity(num_locals); + let mut locals = vec![None; num_locals]; for i in 0..num_locals { let local = mir::Local::new(i+1); - locals.push(if annotated_locals.contains(&local) { None } else { Some(Value::ByVal(PrimVal::Undef)) }); + if !annotated_locals.contains(&local) { + locals[i] = Some(Value::ByVal(PrimVal::Undef)); + } } self.stack.push(Frame { diff --git a/src/lvalue.rs b/src/lvalue.rs index a09f72134e9d8..ce0651bf12916 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByRef(ptr)) } Lvalue::Local { frame, local, field } => { - Ok(self.stack[frame].get_local(local, field.map(|(i, _)| i))?) + self.stack[frame].get_local(local, field.map(|(i, _)| i)) } Lvalue::Global(cid) => { Ok(self.globals.get(&cid).expect("global not cached").value) From dd7735b722cc5de7c217012818de355f427d0bb5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 1 Jun 2017 17:59:00 -0700 Subject: [PATCH 0950/1096] make StorageLive kill the current value of the local --- src/eval_context.rs | 13 +++++-------- src/step.rs | 9 ++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index f364f829f9032..b923ef24dccdb 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1720,15 +1720,12 @@ impl<'tcx> Frame<'tcx> { return Ok(()); } - pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx> { + pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, Option> { trace!("{:?} is now live", local); - if self.locals[local.index() - 1].is_some() { - // The variables comes live now, but was already accessed previously, when it was still dead - return Err(EvalError::DeadLocal); - } else { - self.locals[local.index() - 1] = Some(Value::ByVal(PrimVal::Undef)); - } - return Ok(()); + + let old = self.locals[local.index() - 1]; + self.locals[local.index() - 1] = Some(Value::ByVal(PrimVal::Undef)); // StorageLive *always* kills the value that's currently stored + return Ok(old); } /// Returns the old value of the local diff --git a/src/step.rs b/src/step.rs index 1df55a8b620d5..aef73f8eb0d92 100644 --- a/src/step.rs +++ b/src/step.rs @@ -132,13 +132,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local{ frame, local, field: None } if self.stack.len() == frame+1 => (frame, local), _ => return Err(EvalError::Unimplemented("Stroage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type }; - match stmt.kind { + let old_val = match stmt.kind { StorageLive(_) => self.stack[frame].storage_live(local)?, - _ => { - let old_val = self.stack[frame].storage_dead(local)?; - self.deallocate_local(old_val)?; - } + StorageDead(_) => self.stack[frame].storage_dead(local)?, + _ => bug!("We already checked that we are a storage stmt") }; + self.deallocate_local(old_val)?; } // Defined to do nothing. These are added by optimization passes, to avoid changing the From ec7f1d5248fe1cee904a6d8af094167ded779781 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 2 Jun 2017 06:53:52 +0200 Subject: [PATCH 0951/1096] Fix typo --- src/step.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/step.rs b/src/step.rs index aef73f8eb0d92..24a1df51f4361 100644 --- a/src/step.rs +++ b/src/step.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StorageLive(ref lvalue) | StorageDead(ref lvalue)=> { let (frame, local) = match self.eval_lvalue(lvalue)? { Lvalue::Local{ frame, local, field: None } if self.stack.len() == frame+1 => (frame, local), - _ => return Err(EvalError::Unimplemented("Stroage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type + _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type }; let old_val = match stmt.kind { StorageLive(_) => self.stack[frame].storage_live(local)?, From ca8347a1ffac55d3e3de163575b982f1a5175aa1 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Fri, 2 Jun 2017 21:00:35 -0400 Subject: [PATCH 0952/1096] update for upstream ParamEnv changes --- src/eval_context.rs | 23 ++++++++++------------- src/step.rs | 3 ++- src/terminator/intrinsic.rs | 3 ++- src/traits.rs | 3 ++- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b923ef24dccdb..0ed8e5dc495df 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -379,7 +379,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); - ty.is_sized(self.tcx, ty::ParamEnv::empty(), DUMMY_SP) + ty.is_sized(self.tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP) } pub fn load_mir(&self, instance: ty::InstanceDef<'tcx>) -> EvalResult<'tcx, &'tcx mir::Mir<'tcx>> { @@ -438,9 +438,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Is this inefficient? Needs investigation. let ty = self.monomorphize(ty, substs); - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { - ty.layout(&infcx).map_err(EvalError::Layout) - }) + ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout) } pub fn push_stack_frame( @@ -2033,7 +2031,7 @@ pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bo // returned `false` does not appear unsound. The impact on // code quality is unknown at this time.) - let env = ty::ParamEnv::empty(); + let env = ty::ParamEnv::empty(Reveal::All); if !t.needs_drop(tcx, env) { return false; } @@ -2041,11 +2039,9 @@ pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bo ty::TyAdt(def, _) if def.is_box() => { let typ = t.boxed_ty(); if !typ.needs_drop(tcx, env) && type_is_sized(tcx, typ) { - tcx.infer_ctxt((), traits::Reveal::All).enter(|infcx| { - let layout = t.layout(&infcx).unwrap(); - // `Box` does not allocate. - layout.size(&tcx.data_layout).bytes() != 0 - }) + let layout = t.layout(tcx, ty::ParamEnv::empty(Reveal::All)).unwrap(); + // `Box` does not allocate. + layout.size(&tcx.data_layout).bytes() != 0 } else { true } @@ -2157,7 +2153,7 @@ impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNorma fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic assert!(!ty.needs_subst()); - ty.is_sized(tcx, ty::ParamEnv::empty(), DUMMY_SP) + ty.is_sized(tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP) } /// Attempts to resolve an obligation. The result is a shallow vtable resolution -- meaning that we @@ -2176,13 +2172,14 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + tcx.infer_ctxt(()).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation_cause = traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID); let obligation = traits::Obligation::new(obligation_cause, - trait_ref.to_poly_trait_predicate()); + ty::ParamEnv::empty(Reveal::All), + trait_ref.to_poly_trait_predicate()); let selection = match selcx.select(&obligation) { Ok(Some(selection)) => selection, diff --git a/src/step.rs b/src/step.rs index 24a1df51f4361..9e04d583a2252 100644 --- a/src/step.rs +++ b/src/step.rs @@ -6,6 +6,7 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir; +use rustc::traits::Reveal; use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; @@ -197,7 +198,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { let mutable = !shared || !mir.return_ty.is_freeze( this.ecx.tcx, - ty::ParamEnv::empty(), + ty::ParamEnv::empty(Reveal::All), span); let cleanup = StackPopCleanup::MarkStatic(mutable); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index e2fd57ee8ef6a..e65c7eda1219b 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -1,4 +1,5 @@ use rustc::mir; +use rustc::traits::Reveal; use rustc::ty::layout::{Layout, Size, Align}; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; @@ -291,7 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "needs_drop" => { let ty = substs.type_at(0); - let env = ty::ParamEnv::empty(); + let env = ty::ParamEnv::empty(Reveal::All); let needs_drop = ty.needs_drop(self.tcx, env); self.write_primval(dest, PrimVal::from_bool(needs_drop), dest_ty)?; } diff --git a/src/traits.rs b/src/traits.rs index 77541a5b70fb4..622eddfde1baa 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -16,11 +16,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - self.tcx.infer_ctxt((), Reveal::All).enter(|infcx| { + self.tcx.infer_ctxt(()).enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( traits::ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID), + ty::ParamEnv::empty(Reveal::All), trait_ref.to_poly_trait_predicate(), ); let selection = selcx.select(&obligation).unwrap().unwrap(); From 31cf66d0e82800d4239bfba6d2e1c9d4431c8d2c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 1 Jun 2017 17:24:21 -0700 Subject: [PATCH 0953/1096] remove our array drop glue and use rustc's instead; implement the new Offset and SizeOf operators --- src/eval_context.rs | 197 ++---------------- src/lvalue.rs | 2 +- src/terminator/drop.rs | 20 +- src/terminator/intrinsic.rs | 6 +- src/terminator/mod.rs | 13 +- tests/run-pass/call_drop_on_array_elements.rs | 9 +- .../call_drop_on_zst_array_elements.rs | 21 ++ 7 files changed, 52 insertions(+), 216 deletions(-) create mode 100644 tests/run-pass/call_drop_on_zst_array_elements.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ed8e5dc495df..b2cd1a665d364 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -4,7 +4,6 @@ use std::fmt::Write; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; use rustc::middle::const_val::ConstVal; -use rustc_const_math::{ConstInt, ConstUsize}; use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; @@ -15,7 +14,6 @@ use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP, Span}; use syntax::ast; use syntax::abi::Abi; -use syntax::symbol::Symbol; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; @@ -43,9 +41,6 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: u64, - - /// Drop glue for arrays and slices - pub(crate) seq_drop_glue: &'tcx mir::Mir<'tcx>, } /// A stack frame. @@ -127,180 +122,6 @@ impl Default for ResourceLimits { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self { - // Register array drop glue code - let source_info = mir::SourceInfo { - span: DUMMY_SP, - scope: mir::ARGUMENT_VISIBILITY_SCOPE - }; - // i = 0; len = Len(*a0); goto head; - let start_block = mir::BasicBlockData { - statements: vec![ - mir::Statement { - source_info, - kind: mir::StatementKind::Assign( - mir::Lvalue::Local(mir::Local::new(2)), - mir::Rvalue::Use(mir::Operand::Constant(Box::new(mir::Constant { - span: DUMMY_SP, - ty: tcx.types.usize, - literal: mir::Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())), - }, - }))) - ) - }, - mir::Statement { - source_info, - kind: mir::StatementKind::Assign( - mir::Lvalue::Local(mir::Local::new(3)), - mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection { - base: mir::Lvalue::Local(mir::Local::new(1)), - elem: mir::ProjectionElem::Deref, - }))), - ) - }, - ], - terminator: Some(mir::Terminator { - source_info: source_info, - kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, - }), - is_cleanup: false - }; - // head: done = i == len; switch done { 1 => ret, 0 => loop } - let head = mir::BasicBlockData { - statements: vec![ - mir::Statement { - source_info, - kind: mir::StatementKind::Assign( - mir::Lvalue::Local(mir::Local::new(4)), - mir::Rvalue::BinaryOp( - mir::BinOp::Eq, - mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), - mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))), - ) - ) - }, - ], - terminator: Some(mir::Terminator { - source_info: source_info, - kind: mir::TerminatorKind::SwitchInt { - targets: vec![ - mir::BasicBlock::new(2), - mir::BasicBlock::new(4), - ], - discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))), - switch_ty: tcx.types.bool, - values: vec![ConstInt::U8(0)].into(), - }, - }), - is_cleanup: false - }; - // loop: drop (*a0)[i]; goto inc; - let loop_ = mir::BasicBlockData { - statements: Vec::new(), - terminator: Some(mir::Terminator { - source_info: source_info, - kind: mir::TerminatorKind::Drop { - target: mir::BasicBlock::new(3), - unwind: None, - location: mir::Lvalue::Projection(Box::new( - mir::LvalueProjection { - base: mir::Lvalue::Projection(Box::new( - mir::LvalueProjection { - base: mir::Lvalue::Local(mir::Local::new(1)), - elem: mir::ProjectionElem::Deref, - } - )), - elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))), - } - )), - }, - }), - is_cleanup: false - }; - // inc: i++; goto head; - let inc = mir::BasicBlockData { - statements: vec![ - mir::Statement { - source_info, - kind: mir::StatementKind::Assign( - mir::Lvalue::Local(mir::Local::new(2)), - mir::Rvalue::BinaryOp( - mir::BinOp::Add, - mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))), - mir::Operand::Constant(Box::new(mir::Constant { - span: DUMMY_SP, - ty: tcx.types.usize, - literal: mir::Literal::Value { - value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(1, tcx.sess.target.uint_type).unwrap())), - }, - })), - ) - ) - }, - ], - terminator: Some(mir::Terminator { - source_info: source_info, - kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) }, - }), - is_cleanup: false - }; - // ret: return; - let ret = mir::BasicBlockData { - statements: Vec::new(), - terminator: Some(mir::Terminator { - source_info: source_info, - kind: mir::TerminatorKind::Return, - }), - is_cleanup: false - }; - let locals = vec![ - mir::LocalDecl { - mutability: mir::Mutability::Mut, - ty: tcx.mk_nil(), - name: None, - source_info, - is_user_variable: false, - }, - mir::LocalDecl { - mutability: mir::Mutability::Mut, - ty: tcx.mk_mut_ptr(tcx.mk_slice(tcx.mk_param(0, Symbol::intern("T")))), - name: None, - source_info, - is_user_variable: false, - }, - mir::LocalDecl { - mutability: mir::Mutability::Mut, - ty: tcx.types.usize, - name: None, - source_info, - is_user_variable: false, - }, - mir::LocalDecl { - mutability: mir::Mutability::Mut, - ty: tcx.types.usize, - name: None, - source_info, - is_user_variable: false, - }, - mir::LocalDecl { - mutability: mir::Mutability::Mut, - ty: tcx.types.bool, - name: None, - source_info, - is_user_variable: false, - }, - ]; - let seq_drop_glue = mir::Mir::new( - vec![start_block, head, loop_, inc, ret].into_iter().collect(), - Vec::new().into_iter().collect(), // vis scopes - Vec::new().into_iter().collect(), // promoted - tcx.mk_nil(), // return type - locals.into_iter().collect(), - 1, // arg_count - Vec::new(), // upvars - DUMMY_SP, - ); - let seq_drop_glue = tcx.alloc_mir(seq_drop_glue); EvalContext { tcx, memory: Memory::new(&tcx.data_layout, limits.memory_size), @@ -308,7 +129,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, - seq_drop_glue: seq_drop_glue, } } @@ -631,6 +451,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(value, dest, dest_ty)?; } + BinaryOp(mir::BinOp::Offset, ref left, ref right) => { + let pointer_ty = self.operand_ty(left); + let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; + // FIXME: assuming here that type size is < i64::max_value() + let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; + let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64; + + let ptr = self.eval_operand_to_primval(left)?.to_ptr()?; + let result_ptr = ptr.signed_offset(offset * pointee_size); + self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; + } + BinaryOp(bin_op, ref left, ref right) => { // ignore overflow bit, rustc inserts check branches for us self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?; @@ -823,8 +655,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } - NullaryOp(mir::NullOp::SizeOf, _ty) => { - unimplemented!() + NullaryOp(mir::NullOp::SizeOf, ty) => { + let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type"); + self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } Cast(kind, ref operand, cast_ty) => { diff --git a/src/lvalue.rs b/src/lvalue.rs index ce0651bf12916..1d796d254d784 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -362,7 +362,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let n_ptr = self.eval_operand(operand)?; let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; - assert!(n < len); + assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); let ptr = base_ptr.offset(n * elem_size); (ptr, LvalueExtra::None) } diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 93dfe408e31a9..776061954251c 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -1,6 +1,5 @@ use rustc::mir; use rustc::ty::{self, Ty}; -use rustc::ty::subst::Kind; use syntax::codemap::Span; use error::EvalResult; @@ -21,7 +20,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; self.drop(val, instance, ty, span) } - pub(crate) fn drop(&mut self, mut arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { + pub(crate) fn drop(&mut self, arg: Value, mut instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop: {:#?}, {:?}, {:?}", arg, ty.sty, instance.def); if let ty::InstanceDef::DropGlue(_, None) = instance.def { @@ -44,23 +43,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { None => return Ok(()), } }, - ty::TyArray(elem, n) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - let ptr = match arg { - Value::ByVal(PrimVal::Ptr(src_ptr)) => src_ptr, - _ => bug!("expected thin ptr, got {:?}", arg), - }; - arg = Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(n as u128)); - self.seq_drop_glue - }, - ty::TySlice(elem) => { - instance.substs = self.tcx.mk_substs([ - Kind::from(elem), - ].iter().cloned()); - self.seq_drop_glue - }, _ => self.load_mir(instance.def)?, }; diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index e65c7eda1219b..0ca12ee50633c 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -360,11 +360,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "size_of" => { let ty = substs.type_at(0); - // FIXME: change the `box_free` lang item to take `T: ?Sized` and have it use the - // `size_of_val` intrinsic, then change this back to - // .expect("size_of intrinsic called on unsized value") - // see https://github.com/rust-lang/rust/pull/37708 - let size = self.type_size(ty)?.unwrap_or(!0) as u128; + let size = self.type_size(ty)?.expect("size_of intrinsic called on unsized value") as u128; self.write_primval(dest, PrimVal::from_u128(size), dest_ty)?; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 3c254734b328d..c2131d6627edd 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::{self, TypeVariants, Ty, TypeAndMut}; +use rustc::ty::{self, TypeVariants, Ty}; use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; @@ -730,12 +730,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. - let key_size = match self.operand_ty(&arg_operands[0]).sty { - TypeVariants::TyRawPtr(TypeAndMut { ty, .. }) => { - let layout = self.type_layout(ty)?; - layout.size(&self.tcx.data_layout) - } - _ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned())) + let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference) + .ok_or(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty; + let key_size = { + let layout = self.type_layout(key_type)?; + layout.size(&self.tcx.data_layout) }; // Create key and write it into the memory where key_ptr wants it diff --git a/tests/run-pass/call_drop_on_array_elements.rs b/tests/run-pass/call_drop_on_array_elements.rs index 80dd63de5e9a5..68dbf61da4ead 100644 --- a/tests/run-pass/call_drop_on_array_elements.rs +++ b/tests/run-pass/call_drop_on_array_elements.rs @@ -1,4 +1,4 @@ -struct Bar; +struct Bar(i32); // ZSTs are tested separately static mut DROP_COUNT: usize = 0; @@ -9,8 +9,13 @@ impl Drop for Bar { } fn main() { - let b = [Bar, Bar, Bar, Bar]; + let b = [Bar(0), Bar(0), Bar(0), Bar(0)]; assert_eq!(unsafe { DROP_COUNT }, 0); drop(b); assert_eq!(unsafe { DROP_COUNT }, 4); + + // check empty case + let b : [Bar; 0] = []; + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); } diff --git a/tests/run-pass/call_drop_on_zst_array_elements.rs b/tests/run-pass/call_drop_on_zst_array_elements.rs new file mode 100644 index 0000000000000..1887130fdd8a4 --- /dev/null +++ b/tests/run-pass/call_drop_on_zst_array_elements.rs @@ -0,0 +1,21 @@ +struct Bar; + +static mut DROP_COUNT: usize = 0; + +impl Drop for Bar { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1; } + } +} + +fn main() { + let b = [Bar, Bar, Bar, Bar]; + assert_eq!(unsafe { DROP_COUNT }, 0); + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); + + // check empty case + let b : [Bar; 0] = []; + drop(b); + assert_eq!(unsafe { DROP_COUNT }, 4); +} From 1b5f77e4c1520853816e4a40e92ac049833d031b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 17:26:19 -0700 Subject: [PATCH 0954/1096] Implement Offset like the other binary operators, share code with the intrinsic Also improve drop glue tests --- src/eval_context.rs | 19 +- src/operator.rs | 224 ++++++++++-------- src/terminator/intrinsic.rs | 16 +- tests/run-pass/call_drop_on_array_elements.rs | 5 +- 4 files changed, 136 insertions(+), 128 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b2cd1a665d364..142d90f8dc4d3 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -451,18 +451,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value(value, dest, dest_ty)?; } - BinaryOp(mir::BinOp::Offset, ref left, ref right) => { - let pointer_ty = self.operand_ty(left); - let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - // FIXME: assuming here that type size is < i64::max_value() - let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64; - - let ptr = self.eval_operand_to_primval(left)?.to_ptr()?; - let result_ptr = ptr.signed_offset(offset * pointee_size); - self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; - } - BinaryOp(bin_op, ref left, ref right) => { // ignore overflow bit, rustc inserts check branches for us self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?; @@ -853,6 +841,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + // FIXME: assuming here that type size is < i64::max_value() + let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; + // FIXME: Check overflow, out-of-bounds + Ok(ptr.signed_offset(offset * pointee_size)) + } + pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { let value = self.eval_operand(op)?; let ty = self.operand_ty(op); diff --git a/src/operator.rs b/src/operator.rs index 155d5574daa04..0109cddb5736e 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -1,5 +1,5 @@ use rustc::mir; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; @@ -25,11 +25,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, (PrimVal, bool)> { let left_ty = self.operand_ty(left); let right_ty = self.operand_ty(right); - let left_kind = self.ty_to_primval_kind(left_ty)?; - let right_kind = self.ty_to_primval_kind(right_ty)?; let left_val = self.eval_operand_to_primval(left)?; let right_val = self.eval_operand_to_primval(right)?; - binary_op(op, left_val, left_kind, right_val, right_kind) + self.binary_op(op, left_val, left_ty, right_val, right_ty) } /// Applies the binary operation `op` to the two operands and writes a tuple of the result @@ -132,119 +130,141 @@ macro_rules! f64_arithmetic { ) } -/// Returns the result of the specified operation and whether it overflowed. -pub fn binary_op<'tcx>( - bin_op: mir::BinOp, - left: PrimVal, - left_kind: PrimValKind, - right: PrimVal, - right_kind: PrimValKind, -) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::BinOp::*; - use value::PrimValKind::*; - - // FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store - // plain bytes, and leave that to PrimVal::Bytes. - fn normalize(val: PrimVal) -> PrimVal { - if let PrimVal::Ptr(ptr) = val { - if let Ok(bytes) = ptr.to_int() { - return PrimVal::Bytes(bytes as u128); +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + /// Returns the result of the specified operation and whether it overflowed. + pub fn binary_op( + &self, + bin_op: mir::BinOp, + left: PrimVal, + left_ty: Ty<'tcx>, + right: PrimVal, + right_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, (PrimVal, bool)> { + use rustc::mir::BinOp::*; + use value::PrimValKind::*; + + // FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store + // plain bytes, and leave that to PrimVal::Bytes. + fn normalize(val: PrimVal) -> PrimVal { + if let PrimVal::Ptr(ptr) = val { + if let Ok(bytes) = ptr.to_int() { + return PrimVal::Bytes(bytes as u128); + } } + val } - val - } - let (left, right) = (normalize(left), normalize(right)); + let (left, right) = (normalize(left), normalize(right)); - let (l, r) = match (left, right) { - (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), - - (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { - if left_ptr.alloc_id == right_ptr.alloc_id { - // If the pointers are into the same allocation, fall through to the more general - // match later, which will do comparisons on the pointer offsets. - (left_ptr.offset as u128, right_ptr.offset as u128) - } else { - return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); - } + // Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers. + if bin_op == Offset { + let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; + let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?; + return Ok((PrimVal::Ptr(ptr), false)); } - (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) | - (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { - return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false)); - } + let (l, r) = match (left, right) { + (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), + + (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { + if left_ptr.alloc_id == right_ptr.alloc_id { + // If the pointers are into the same allocation, fall through to the more general + // match later, which will do comparisons on the pointer offsets. + (left_ptr.offset as u128, right_ptr.offset as u128) + } else { + return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); + } + } - (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), - }; + (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) | + (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { + return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false)); + } - // These ops can have an RHS with a different numeric type. - if bin_op == Shl || bin_op == Shr { - return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), - Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), - _ => bug!("it has already been checked that this is a shift op"), + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), }; - } - if left_kind != right_kind { - let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); - return Err(EvalError::Unimplemented(msg)); - } + let left_kind = self.ty_to_primval_kind(left_ty)?; + let right_kind = self.ty_to_primval_kind(right_ty)?; - let val = match (bin_op, left_kind) { - (Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)), - (Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)), - (Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)), - (Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)), - (Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)), - (Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)), - - (Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)), - (Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)), - (Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)), - (Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)), - (Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)), - (Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)), - - (Add, F32) => f32_arithmetic!(+, l, r), - (Sub, F32) => f32_arithmetic!(-, l, r), - (Mul, F32) => f32_arithmetic!(*, l, r), - (Div, F32) => f32_arithmetic!(/, l, r), - (Rem, F32) => f32_arithmetic!(%, l, r), - - (Add, F64) => f64_arithmetic!(+, l, r), - (Sub, F64) => f64_arithmetic!(-, l, r), - (Mul, F64) => f64_arithmetic!(*, l, r), - (Div, F64) => f64_arithmetic!(/, l, r), - (Rem, F64) => f64_arithmetic!(%, l, r), - - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), - (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), - (Lt, _) => PrimVal::from_bool(l < r), - (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), - (Le, _) => PrimVal::from_bool(l <= r), - (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), - (Gt, _) => PrimVal::from_bool(l > r), - (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), - (Ge, _) => PrimVal::from_bool(l >= r), - - (BitOr, _) => PrimVal::Bytes(l | r), - (BitAnd, _) => PrimVal::Bytes(l & r), - (BitXor, _) => PrimVal::Bytes(l ^ r), - - (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), - (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), - (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), - (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), - (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + // These ops can have an RHS with a different numeric type. + if bin_op == Shl || bin_op == Shr { + return match bin_op { + Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), + Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), + _ => bug!("it has already been checked that this is a shift op"), + }; + } + if bin_op == Offset { + // We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM. + if left_kind == PrimValKind::Ptr && right_kind.is_int() && r == 0 { + return Ok((PrimVal::Bytes(l), false)); + } else { + let msg = format!("unimplemented Offset: {:?}, {:?}", left, right); + return Err(EvalError::Unimplemented(msg)); + } + } - _ => { + if left_kind != right_kind { let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); return Err(EvalError::Unimplemented(msg)); } - }; - Ok((val, false)) + let val = match (bin_op, left_kind) { + (Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)), + (Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)), + (Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)), + (Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)), + (Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)), + (Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)), + + (Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)), + (Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)), + (Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)), + (Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)), + (Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)), + (Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)), + + (Add, F32) => f32_arithmetic!(+, l, r), + (Sub, F32) => f32_arithmetic!(-, l, r), + (Mul, F32) => f32_arithmetic!(*, l, r), + (Div, F32) => f32_arithmetic!(/, l, r), + (Rem, F32) => f32_arithmetic!(%, l, r), + + (Add, F64) => f64_arithmetic!(+, l, r), + (Sub, F64) => f64_arithmetic!(-, l, r), + (Mul, F64) => f64_arithmetic!(*, l, r), + (Div, F64) => f64_arithmetic!(/, l, r), + (Rem, F64) => f64_arithmetic!(%, l, r), + + (Eq, _) => PrimVal::from_bool(l == r), + (Ne, _) => PrimVal::from_bool(l != r), + (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), + (Lt, _) => PrimVal::from_bool(l < r), + (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), + (Le, _) => PrimVal::from_bool(l <= r), + (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)), + (Gt, _) => PrimVal::from_bool(l > r), + (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)), + (Ge, _) => PrimVal::from_bool(l >= r), + + (BitOr, _) => PrimVal::Bytes(l | r), + (BitAnd, _) => PrimVal::Bytes(l & r), + (BitXor, _) => PrimVal::Bytes(l ^ r), + + (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r), + (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r), + (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r), + (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r), + (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), + + _ => { + let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); + return Err(EvalError::Unimplemented(msg)); + } + }; + + Ok((val, false)) + } } fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 0ca12ee50633c..843a5b06b6a04 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -7,7 +7,6 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::{Lvalue, LvalueExtra}; -use operator; use value::{PrimVal, PrimValKind, Value}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -103,8 +102,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(_) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), }; - let kind = self.ty_to_primval_kind(ty)?; - let (val, _) = operator::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?; + let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?; let dest = self.force_allocation(dest)?.to_ptr(); self.write_pair_to_ptr(old, val, dest, dest_ty)?; self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; @@ -125,7 +123,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"), }; self.write_primval(dest, old, ty)?; - let kind = self.ty_to_primval_kind(ty)?; let op = match intrinsic_name.split('_').nth(1).unwrap() { "or" => mir::BinOp::BitOr, "xor" => mir::BinOp::BitXor, @@ -135,7 +132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!(), }; // FIXME: what do atomics do on overflow? - let (val, _) = operator::binary_op(op, old, kind, change, kind)?; + let (val, _) = self.binary_op(op, old, ty, change, ty)?; self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; }, @@ -219,7 +216,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { let ty = substs.type_at(0); - let kind = self.ty_to_primval_kind(ty)?; let a = self.value_to_primval(arg_vals[0], ty)?; let b = self.value_to_primval(arg_vals[1], ty)?; let op = match intrinsic_name { @@ -230,7 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "frem_fast" => mir::BinOp::Rem, _ => bug!(), }; - let result = operator::binary_op(op, a, kind, b, kind)?; + let result = self.binary_op(op, a, ty, b, ty)?; self.write_primval(dest, result.0, dest_ty)?; } @@ -298,13 +294,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "offset" => { - let pointee_ty = substs.type_at(0); - // FIXME: assuming here that type size is < i64::max_value() - let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; - let ptr = arg_vals[0].read_ptr(&self.memory)?; - let result_ptr = ptr.signed_offset(offset * pointee_size); + let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; } diff --git a/tests/run-pass/call_drop_on_array_elements.rs b/tests/run-pass/call_drop_on_array_elements.rs index 68dbf61da4ead..c9b59f635e145 100644 --- a/tests/run-pass/call_drop_on_array_elements.rs +++ b/tests/run-pass/call_drop_on_array_elements.rs @@ -1,15 +1,16 @@ -struct Bar(i32); // ZSTs are tested separately +struct Bar(u16); // ZSTs are tested separately static mut DROP_COUNT: usize = 0; impl Drop for Bar { fn drop(&mut self) { + assert_eq!(self.0 as usize, unsafe { DROP_COUNT }); // tests whether we are called at a valid address unsafe { DROP_COUNT += 1; } } } fn main() { - let b = [Bar(0), Bar(0), Bar(0), Bar(0)]; + let b = [Bar(0), Bar(1), Bar(2), Bar(3)]; assert_eq!(unsafe { DROP_COUNT }, 0); drop(b); assert_eq!(unsafe { DROP_COUNT }, 4); From 716653fd761de2fe1fd8f0f4db1b89d2b4e84ec9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Jun 2017 18:34:13 -0700 Subject: [PATCH 0955/1096] add comments in the tests explaining what the ignore flags do --- tests/run-pass/aux_test.rs | 1 + tests/run-pass/u128.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index 1b1dbaa68387e..dd46c638a2afe 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,4 +1,5 @@ // aux-build:dep.rs +// This ignores the test against rustc, but runs it against miri: // ignore-cross-compile extern crate dep; diff --git a/tests/run-pass/u128.rs b/tests/run-pass/u128.rs index bd68157e4bc22..4fe40a9694d48 100644 --- a/tests/run-pass/u128.rs +++ b/tests/run-pass/u128.rs @@ -8,11 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-stage0 +// This disables the test completely: // ignore-stage1 -// ignore-emscripten - #![feature(i128_type)] fn b(t: T) -> T { t } From cfff91ba3ec4cfb777e3f18c9cd1e5b1c4985713 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Jun 2017 18:35:33 -0700 Subject: [PATCH 0956/1096] write_bytes intrinsic: if the write count is 0, the pointer does not have to be valid --- src/terminator/intrinsic.rs | 6 ++++-- src/terminator/mod.rs | 1 + tests/run-pass/hashmap.rs | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass/hashmap.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 843a5b06b6a04..15bab4aacbfa2 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -414,8 +414,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - self.memory.check_align(ptr, ty_align, size * count)?; - self.memory.write_repeat(ptr, val_byte, size * count)?; + if count > 0 { + self.memory.check_align(ptr, ty_align, size * count)?; + self.memory.write_repeat(ptr, val_byte, size * count)?; + } } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c2131d6627edd..49570c7ba5b76 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -742,6 +742,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) { return Err(EvalError::OutOfTls); } + // TODO: Does this need checking for alignment? self.memory.write_uint(key_ptr, key, key_size.bytes())?; // Return success (0) diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs new file mode 100644 index 0000000000000..775dee252f625 --- /dev/null +++ b/tests/run-pass/hashmap.rs @@ -0,0 +1,19 @@ +use std::collections::{self, HashMap}; +use std::hash::BuildHasherDefault; + +// This disables the test completely: +// ignore-stage1 +// TODO: The tests actually passes against rustc and miri with MIR-libstd, but right now, we cannot express that in the test flags + +fn main() { + let map : HashMap> = Default::default(); + assert_eq!(map.values().fold(0, |x, y| x+y), 0); + + // TODO: This performs bit operations on the least significant bit of a pointer +// for i in 0..33 { +// map.insert(format!("key_{}", i), i); +// assert_eq!(map.values().fold(0, |x, y| x+y), i*(i+1)/2); +// } + + // TODO: Test Entry API +} From 6197f4fac93f2d23f3b2b367f2bcc8342c3595fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 10:42:02 -0700 Subject: [PATCH 0957/1096] Permit int->ptr->int roundtrip --- src/eval_context.rs | 1 + src/memory.rs | 10 ++++++++-- tests/run-pass/ptr_int_casts.rs | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass/ptr_int_casts.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 142d90f8dc4d3..8eba793c9f65e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1438,6 +1438,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { panic!("Failed to access local: {:?}", err); } Ok(Value::ByRef(ptr)) => { + write!(msg, " by ref:").unwrap(); allocs.push(ptr.alloc_id); } Ok(Value::ByVal(val)) => { diff --git a/src/memory.rs b/src/memory.rs index bfdc45c921d13..e21b9c9e4d365 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -605,7 +605,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - if self.relocations(ptr, size)?.count() != 0 { + if self.has_non_int_relocations(ptr, size)? { return Err(EvalError::ReadPointerAsBytes); } self.check_defined(ptr, size)?; @@ -703,7 +703,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = ptr.offset as usize; match alloc.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { + if self.has_non_int_relocations(ptr, (size + 1) as u64)? { return Err(EvalError::ReadPointerAsBytes); } self.check_defined(ptr, (size + 1) as u64)?; @@ -887,6 +887,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) } + fn has_non_int_relocations(&self, ptr: Pointer, size: u64) + -> EvalResult<'tcx, bool> + { + Ok(self.relocations(ptr, size)?.any(|(_, &alloc_id)| alloc_id != NEVER_ALLOC_ID)) + } + fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs new file mode 100644 index 0000000000000..4983ea4f886b5 --- /dev/null +++ b/tests/run-pass/ptr_int_casts.rs @@ -0,0 +1,16 @@ +// fn eq_ref(x: &T, y: &T) -> bool { +// x as *const _ == y as *const _ +// } + +fn main() { + // int-ptr-int + assert_eq!(1 as *const i32 as usize, 1); + + // TODO +// { // ptr-int-ptr +// let x = 13; +// let y = &x as *const _ as usize; +// let y = y as *const _; +// assert!(eq_ref(&x, unsafe { &*y })); +// } +} From 1d0e622a81fff95d47de6ec9e4c0f28b9f54e285 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 10:43:31 -0700 Subject: [PATCH 0958/1096] test that we cannot observe the bytes representing a pointer --- tests/compile-fail/pointer_byte_read_1.rs | 7 +++++++ tests/compile-fail/pointer_byte_read_2.rs | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/compile-fail/pointer_byte_read_1.rs create mode 100644 tests/compile-fail/pointer_byte_read_2.rs diff --git a/tests/compile-fail/pointer_byte_read_1.rs b/tests/compile-fail/pointer_byte_read_1.rs new file mode 100644 index 0000000000000..285a0684a93ea --- /dev/null +++ b/tests/compile-fail/pointer_byte_read_1.rs @@ -0,0 +1,7 @@ +fn main() { + let x = 13; + let y = &x; + let z = &y as *const &i32 as *const usize; + let ptr_bytes = unsafe { *z }; // the actual deref is fine, because we read the entire pointer at once + let _ = ptr_bytes == 15; //~ ERROR: tried to access part of a pointer value as raw bytes +} diff --git a/tests/compile-fail/pointer_byte_read_2.rs b/tests/compile-fail/pointer_byte_read_2.rs new file mode 100644 index 0000000000000..b0f619332e00c --- /dev/null +++ b/tests/compile-fail/pointer_byte_read_2.rs @@ -0,0 +1,7 @@ +fn main() { + let x = 13; + let y = &x; + let z = &y as *const &i32 as *const u8; + // the deref fails, because we are reading only a part of the pointer + let _ = unsafe { *z }; //~ ERROR: tried to access part of a pointer value as raw bytes +} From 70227c87bfecc96beebb62be1fff19ed4aa8e167 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 18:18:37 -0700 Subject: [PATCH 0959/1096] fix arith_offset not taking the size of the type into account; test for offset --- src/terminator/intrinsic.rs | 7 ++++--- tests/run-pass/ptr_arith_offset.rs | 6 ++++++ tests/run-pass/ptr_offset.rs | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/ptr_arith_offset.rs create mode 100644 tests/run-pass/ptr_offset.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 15bab4aacbfa2..770cde777096a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -43,10 +43,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { + // FIXME: Switch to non-checked, wrapped version of pointer_offset + let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; - let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()?; - let new_ptr = ptr.signed_offset(offset as i64); - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; + self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; } "assume" => { diff --git a/tests/run-pass/ptr_arith_offset.rs b/tests/run-pass/ptr_arith_offset.rs new file mode 100644 index 0000000000000..7912da9fd437c --- /dev/null +++ b/tests/run-pass/ptr_arith_offset.rs @@ -0,0 +1,6 @@ +fn main() { + let v = [1i16, 2]; + let x = &v as *const i16; + let x = x.wrapping_offset(1); + assert_eq!(unsafe { *x }, 2); +} diff --git a/tests/run-pass/ptr_offset.rs b/tests/run-pass/ptr_offset.rs new file mode 100644 index 0000000000000..6add5212db9f6 --- /dev/null +++ b/tests/run-pass/ptr_offset.rs @@ -0,0 +1,6 @@ +fn main() { + let v = [1i16, 2]; + let x = &v as *const i16; + let x = unsafe { x.offset(1) }; + assert_eq!(unsafe { *x }, 2); +} From f39e015163f4a1ddfa615cbb0f9a27999be2c20b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 18:47:31 -0700 Subject: [PATCH 0960/1096] check for overflow when doing pointer arithmetic --- src/error.rs | 3 ++ src/eval_context.rs | 31 ++++++++++++++------- src/lvalue.rs | 8 +++--- src/memory.rs | 24 ++++++++++++---- src/terminator/intrinsic.rs | 2 +- src/terminator/mod.rs | 10 +++---- src/traits.rs | 10 +++---- src/value.rs | 4 +-- tests/compile-fail/ptr_offset_overflow.rs | 8 ++++++ tests/run-pass/ptr_arith_offset_overflow.rs | 9 ++++++ 10 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 tests/compile-fail/ptr_offset_overflow.rs create mode 100644 tests/run-pass/ptr_arith_offset_overflow.rs diff --git a/src/error.rs b/src/error.rs index 702c2c4940fac..42df2398b46bb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,6 +23,7 @@ pub enum EvalError<'tcx> { }, ReadPointerAsBytes, InvalidPointerMath, + OverflowingPointerMath, ReadUndefBytes, DeadLocal, InvalidBoolOp(mir::BinOp), @@ -82,6 +83,8 @@ impl<'tcx> Error for EvalError<'tcx> { "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::InvalidPointerMath => "attempted to do math or a comparison on pointers into different allocations", + EvalError::OverflowingPointerMath => + "attempted to do overflowing math on a pointer", EvalError::ReadUndefBytes => "attempted to read undefined bytes", EvalError::DeadLocal => diff --git a/src/eval_context.rs b/src/eval_context.rs index 8eba793c9f65e..4b61762176608 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -391,7 +391,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); - let discr_dest = dest_ptr.offset(discr_offset); + let discr_dest = dest_ptr.offset(discr_offset)?; self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); - let dest = dest.offset(offset.bytes()); + let dest = dest.offset(offset.bytes())?; let dest_size = self.type_size(ty)? .expect("bad StructWrappedNullablePointer discrfield"); self.memory.write_int(dest, 0, dest_size)?; @@ -610,7 +610,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); for i in 0..length { - let elem_dest = dest.offset(i * elem_size); + let elem_dest = dest.offset(i * elem_size)?; self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -841,11 +841,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + // FIXME: assuming here that type size is < i64::max_value() + let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; + let offset = offset.overflowing_mul(pointee_size).0; + Ok(ptr.wrapping_signed_offset(offset)) + } + pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - // FIXME: Check overflow, out-of-bounds - Ok(ptr.signed_offset(offset * pointee_size)) + // FIXME: Check out-of-bounds + return if let Some(offset) = offset.checked_mul(pointee_size) { + ptr.signed_offset(offset) + } else { + Err(EvalError::OverflowingPointerMath) + } } pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { @@ -1099,8 +1110,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0), a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1), b, field_1_size)?; + self.memory.write_primval(ptr.offset(field_0)?, a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1)?, b, field_1_size)?; Ok(()) } @@ -1217,7 +1228,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(PrimVal::Ptr(p))) } else { trace!("reading fat pointer extra of type {}", pointee_ty); - let extra = ptr.offset(self.memory.pointer_size()); + let extra = ptr.offset(self.memory.pointer_size())?; let extra = match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | @@ -1402,8 +1413,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); - let src_f_ptr = src_ptr.offset(src_field_offset); - let dst_f_ptr = dest.offset(dst_field_offset); + let src_f_ptr = src_ptr.offset(src_field_offset)?; + let dst_f_ptr = dest.offset(dst_field_offset)?; if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr, src_fty)?; } else { diff --git a/src/lvalue.rs b/src/lvalue.rs index 1d796d254d784..e4deddbfb48b4 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -270,7 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => offset.bytes(), }; - let ptr = base_ptr.offset(offset); + let ptr = base_ptr.offset(offset)?; let field_ty = self.monomorphize(field_ty, self.substs()); @@ -363,7 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); - let ptr = base_ptr.offset(n * elem_size); + let ptr = base_ptr.offset(n * elem_size)?; (ptr, LvalueExtra::None) } @@ -384,7 +384,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size); + let ptr = base_ptr.offset(index * elem_size)?; (ptr, LvalueExtra::None) } @@ -398,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size); + let ptr = base_ptr.offset(u64::from(from) * elem_size)?; let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); (ptr, extra) } diff --git a/src/memory.rs b/src/memory.rs index e21b9c9e4d365..46d5968abac22 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -60,20 +60,32 @@ impl Pointer { Pointer { alloc_id, offset } } - pub fn signed_offset(self, i: i64) -> Self { + pub fn wrapping_signed_offset<'tcx>(self, i: i64) -> Self { + Pointer::new(self.alloc_id, self.offset.wrapping_add(i as u64)) + } + + pub fn signed_offset<'tcx>(self, i: i64) -> EvalResult<'tcx, Self> { // FIXME: is it possible to over/underflow here? if i < 0 { // trickery to ensure that i64::min_value() works fine // this formula only works for true negative values, it panics for zero! let n = u64::max_value() - (i as u64) + 1; - Pointer::new(self.alloc_id, self.offset - n) + if let Some(res) = self.offset.checked_sub(n) { + Ok(Pointer::new(self.alloc_id, res)) + } else { + Err(EvalError::OverflowingPointerMath) + } } else { self.offset(i as u64) } } - pub fn offset(self, i: u64) -> Self { - Pointer::new(self.alloc_id, self.offset + i) + pub fn offset<'tcx>(self, i: u64) -> EvalResult<'tcx, Self> { + if let Some(res) = self.offset.checked_add(i) { + Ok(Pointer::new(self.alloc_id, res)) + } else { + Err(EvalError::OverflowingPointerMath) + } } pub fn points_to_zst(&self) -> bool { @@ -271,7 +283,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.grow(amount, false); } else if size > new_size { self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size), size - new_size)?; + self.clear_relocations(ptr.offset(new_size)?, size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; // `as usize` is fine here, since it is smaller than `size`, which came from a usize alloc.bytes.truncate(new_size as usize); @@ -919,7 +931,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size), 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size)?, 0)?.count(); if overlapping_start + overlapping_end != 0 { return Err(EvalError::ReadPointerAsBytes); } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 770cde777096a..31886e9cc6d91 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: Switch to non-checked, wrapped version of pointer_offset let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; - let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; + let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 49570c7ba5b76..ebf8723e827e1 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match arg_val { Value::ByRef(ptr) => { for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)); + let arg = Value::ByRef(ptr.offset(offset)?); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); self.write_value(arg, dest, ty)?; @@ -387,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; - let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3)))?; + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3))?)?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); @@ -473,7 +473,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes()); + let nonnull = adt_ptr.offset(offset.bytes())?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); @@ -654,7 +654,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { - let new_ptr = ptr.offset(num - idx as u64 - 1); + let new_ptr = ptr.offset(num - idx as u64 - 1)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -666,7 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { - let new_ptr = ptr.offset(idx as u64); + let new_ptr = ptr.offset(idx as u64)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; diff --git a/src/traits.rs b/src/traits.rs index 622eddfde1baa..bf9e47da991a1 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -56,14 +56,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; - self.memory.write_usize(vtable.offset(ptr_size), size)?; - self.memory.write_usize(vtable.offset(ptr_size * 2), align)?; + self.memory.write_usize(vtable.offset(ptr_size)?, size)?; + self.memory.write_usize(vtable.offset(ptr_size * 2)?, align)?; for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { let instance = ::eval_context::resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64)), fn_ptr)?; + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64))?, fn_ptr)?; } } @@ -88,8 +88,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - let size = self.memory.read_usize(vtable.offset(pointer_size))?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2))?; + let size = self.memory.read_usize(vtable.offset(pointer_size)?)?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2)?)?; Ok((size, align)) } diff --git a/src/value.rs b/src/value.rs index e812d116286ac..efe5aac71f5bc 100644 --- a/src/value.rs +++ b/src/value.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size()))?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size())?)?; Ok((ptr, vtable)) } @@ -105,7 +105,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size()))?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size())?)?; Ok((ptr, len)) }, ByValPair(ptr, val) => { diff --git a/tests/compile-fail/ptr_offset_overflow.rs b/tests/compile-fail/ptr_offset_overflow.rs new file mode 100644 index 0000000000000..fa93d0daf76c5 --- /dev/null +++ b/tests/compile-fail/ptr_offset_overflow.rs @@ -0,0 +1,8 @@ +//error-pattern: overflowing math on a pointer +fn main() { + let v = [1i8, 2]; + let x = &v[1] as *const i8; + // One of them is guaranteed to overflow + let _ = unsafe { x.offset(isize::max_value()) }; + let _ = unsafe { x.offset(isize::min_value()) }; +} diff --git a/tests/run-pass/ptr_arith_offset_overflow.rs b/tests/run-pass/ptr_arith_offset_overflow.rs new file mode 100644 index 0000000000000..3383c3b801482 --- /dev/null +++ b/tests/run-pass/ptr_arith_offset_overflow.rs @@ -0,0 +1,9 @@ +fn main() { + let v = [1i16, 2]; + let x = &v[1] as *const i16; + // Adding 2*isize::max and then 1 is like substracting 1 + let x = x.wrapping_offset(isize::max_value()); + let x = x.wrapping_offset(isize::max_value()); + let x = x.wrapping_offset(1); + assert_eq!(unsafe { *x }, 1); +} From 2a231d66fc3c9d5678363d5017ee386f9f36428e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 4 Jun 2017 19:31:34 -0700 Subject: [PATCH 0961/1096] check bounds when using offset intrinsic or MIR op --- src/error.rs | 9 ++++---- src/eval_context.rs | 9 ++++++-- src/memory.rs | 25 +++++++++++++---------- tests/compile-fail/out_of_bounds_ptr_1.rs | 8 ++++++++ tests/compile-fail/out_of_bounds_ptr_2.rs | 7 +++++++ tests/compile-fail/out_of_bounds_read.rs | 2 +- tests/compile-fail/out_of_bounds_read2.rs | 2 +- tests/compile-fail/ptr_offset_overflow.rs | 2 -- 8 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 tests/compile-fail/out_of_bounds_ptr_1.rs create mode 100644 tests/compile-fail/out_of_bounds_ptr_2.rs diff --git a/src/error.rs b/src/error.rs index 42df2398b46bb..dc7b227b7972e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,7 +18,7 @@ pub enum EvalError<'tcx> { InvalidDiscriminant, PointerOutOfBounds { ptr: Pointer, - size: u64, + access: bool, allocation_size: u64, }, ReadPointerAsBytes, @@ -150,9 +150,10 @@ impl<'tcx> Error for EvalError<'tcx> { impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - EvalError::PointerOutOfBounds { ptr, size, allocation_size } => { - write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}", - ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size) + EvalError::PointerOutOfBounds { ptr, access, allocation_size } => { + write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", + if access { "memory access" } else { "pointer computed" }, + ptr.offset, ptr.alloc_id, allocation_size) }, EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), EvalError::FunctionPointerTyMismatch(sig, got) => diff --git a/src/eval_context.rs b/src/eval_context.rs index 4b61762176608..abae18a0ff1f1 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -849,11 +849,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + if offset == 0 { + // rustc relies on Offset-by-0 to be well-defined even for "bad" pointers like Unique::empty(). + return Ok(ptr); + } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - // FIXME: Check out-of-bounds return if let Some(offset) = offset.checked_mul(pointee_size) { - ptr.signed_offset(offset) + let ptr = ptr.signed_offset(offset)?; + self.memory.check_bounds(ptr, false)?; + Ok(ptr) } else { Err(EvalError::OverflowingPointerMath) } diff --git a/src/memory.rs b/src/memory.rs index 46d5968abac22..24ac27f8fa324 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -120,7 +120,7 @@ pub type TlsKey = usize; #[derive(Copy, Clone, Debug)] pub struct TlsEntry<'tcx> { - data: Pointer, // will eventually become a map from thread IDs to pointers + data: Pointer, // Will eventually become a map from thread IDs to pointers, if we ever support more than one thread. dtor: Option>, } @@ -173,8 +173,8 @@ pub struct Memory<'a, 'tcx> { /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. literal_alloc_cache: HashMap, AllocId>, - - /// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there. + + /// pthreads-style thread-local storage. thread_local: HashMap>, /// The Key to use for the next thread-local allocation. @@ -366,6 +366,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } + pub(crate) fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> { + let alloc = self.get(ptr.alloc_id)?; + let allocation_size = alloc.bytes.len() as u64; + if ptr.offset > allocation_size { + return Err(EvalError::PointerOutOfBounds { ptr, access, allocation_size }); + } + Ok(()) + } + pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) { self.packed.insert(Entry { alloc_id: ptr.alloc_id, @@ -586,11 +595,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&[]); } self.check_align(ptr, align, size)?; + self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; - let allocation_size = alloc.bytes.len() as u64; - if ptr.offset + size > allocation_size { - return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size }); - } assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); let offset = ptr.offset as usize; @@ -602,11 +608,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } self.check_align(ptr, align, size)?; + self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; - let allocation_size = alloc.bytes.len() as u64; - if ptr.offset + size > allocation_size { - return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size }); - } assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); let offset = ptr.offset as usize; diff --git a/tests/compile-fail/out_of_bounds_ptr_1.rs b/tests/compile-fail/out_of_bounds_ptr_1.rs new file mode 100644 index 0000000000000..8dce7e5786264 --- /dev/null +++ b/tests/compile-fail/out_of_bounds_ptr_1.rs @@ -0,0 +1,8 @@ +// error-pattern: pointer computed at offset 5, outside bounds of allocation +fn main() { + let v = [0i8; 4]; + let x = &v as *const i8; + // The error is inside another function, so we cannot match it by line + let x = unsafe { x.offset(5) }; + panic!("this should never print: {:?}", x); +} diff --git a/tests/compile-fail/out_of_bounds_ptr_2.rs b/tests/compile-fail/out_of_bounds_ptr_2.rs new file mode 100644 index 0000000000000..0bb670fd022f7 --- /dev/null +++ b/tests/compile-fail/out_of_bounds_ptr_2.rs @@ -0,0 +1,7 @@ +// error-pattern: overflowing math on a pointer +fn main() { + let v = [0i8; 4]; + let x = &v as *const i8; + let x = unsafe { x.offset(-1) }; + panic!("this should never print: {:?}", x); +} diff --git a/tests/compile-fail/out_of_bounds_read.rs b/tests/compile-fail/out_of_bounds_read.rs index f6a305840c241..8c56b14bdf221 100644 --- a/tests/compile-fail/out_of_bounds_read.rs +++ b/tests/compile-fail/out_of_bounds_read.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2 + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: which has size 2 panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/out_of_bounds_read2.rs b/tests/compile-fail/out_of_bounds_read2.rs index 5509a8346e552..d29b22ffb2a6b 100644 --- a/tests/compile-fail/out_of_bounds_read2.rs +++ b/tests/compile-fail/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: memory access at offset 6, outside bounds of allocation panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/ptr_offset_overflow.rs b/tests/compile-fail/ptr_offset_overflow.rs index fa93d0daf76c5..ebd972a871752 100644 --- a/tests/compile-fail/ptr_offset_overflow.rs +++ b/tests/compile-fail/ptr_offset_overflow.rs @@ -2,7 +2,5 @@ fn main() { let v = [1i8, 2]; let x = &v[1] as *const i8; - // One of them is guaranteed to overflow - let _ = unsafe { x.offset(isize::max_value()) }; let _ = unsafe { x.offset(isize::min_value()) }; } From a2911534141898b6a57ad0fc09d95b94f2405122 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 15:18:40 -0700 Subject: [PATCH 0962/1096] Permit ptr->int->ptr roundtrip --- src/eval_context.rs | 17 +++++++++++++---- tests/run-pass/ptr_int_casts.rs | 19 +++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index abae18a0ff1f1..e601f4b38fac0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -662,7 +662,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); if self.type_is_fat_ptr(src_ty) { - trace!("misc cast: {:?}", src); match (src, self.type_is_fat_ptr(dest_ty)) { (Value::ByRef(_), _) | (Value::ByValPair(..), true) => { @@ -674,9 +673,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { - let src_val = self.value_to_primval(src, src_ty)?; - let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?; - self.write_value(Value::ByVal(dest_val), dest, dest_ty)?; + // First, try casting + let dest_val = self.value_to_primval(src, src_ty).and_then( + |src_val| { self.cast_primval(src_val, src_ty, dest_ty) }) + // Alternatively, if the sizes are equal, try just reading at the target type + .or_else(|err| { + let size = self.type_size(src_ty)?; + if size.is_some() && size == self.type_size(dest_ty)? { + self.value_to_primval(src, dest_ty) + } else { + Err(err) + } + }); + self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?; } } diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs index 4983ea4f886b5..e245cb22475d5 100644 --- a/tests/run-pass/ptr_int_casts.rs +++ b/tests/run-pass/ptr_int_casts.rs @@ -1,16 +1,15 @@ -// fn eq_ref(x: &T, y: &T) -> bool { -// x as *const _ == y as *const _ -// } +fn eq_ref(x: &T, y: &T) -> bool { + x as *const _ == y as *const _ +} fn main() { // int-ptr-int assert_eq!(1 as *const i32 as usize, 1); - // TODO -// { // ptr-int-ptr -// let x = 13; -// let y = &x as *const _ as usize; -// let y = y as *const _; -// assert!(eq_ref(&x, unsafe { &*y })); -// } + { // ptr-int-ptr + let x = 13; + let y = &x as *const _ as usize; + let y = y as *const _; + assert!(eq_ref(&x, unsafe { &*y })); + } } From a6e6a6fd29616f4c4a60eb11923bc4e2b0097acd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 15:19:07 -0700 Subject: [PATCH 0963/1096] Add some more tests involving Offset/arith_offset and ZST pointers --- tests/run-pass/drop_empty_slice.rs | 9 +++++++++ tests/run-pass/iter_slice.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/run-pass/drop_empty_slice.rs create mode 100644 tests/run-pass/iter_slice.rs diff --git a/tests/run-pass/drop_empty_slice.rs b/tests/run-pass/drop_empty_slice.rs new file mode 100644 index 0000000000000..25e7bc329db54 --- /dev/null +++ b/tests/run-pass/drop_empty_slice.rs @@ -0,0 +1,9 @@ +#![feature(box_syntax)] +// This disables the test completely: +// ignore-stage1 + +fn main() { + // With the nested Vec, this is calling Offset(Unique::empty(), 0). + let args : Vec> = Vec::new(); + let local = box args; +} diff --git a/tests/run-pass/iter_slice.rs b/tests/run-pass/iter_slice.rs new file mode 100644 index 0000000000000..fd7229c3455e4 --- /dev/null +++ b/tests/run-pass/iter_slice.rs @@ -0,0 +1,12 @@ +fn main() { + for _ in Vec::::new().iter() { // this iterates over a Unique::empty() + panic!("We should never be here."); + } + + // Iterate over a ZST (uses arith_offset internally) + let mut count = 0; + for _ in &[(), (), ()] { + count += 1; + } + assert_eq!(count, 3); +} From 36505c7b40b461744061c9da49a1c76996d46c4c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 17:11:51 -0700 Subject: [PATCH 0964/1096] fix bitops being accidentally allowed on pointers from the same allocation --- src/operator.rs | 102 +++++++++++++--------- src/value.rs | 8 ++ tests/compile-fail/overflowing-lsh-neg.rs | 16 ++++ tests/compile-fail/ptr_bitops.rs | 7 ++ 4 files changed, 94 insertions(+), 39 deletions(-) create mode 100644 tests/compile-fail/overflowing-lsh-neg.rs create mode 100644 tests/compile-fail/ptr_bitops.rs diff --git a/src/operator.rs b/src/operator.rs index 0109cddb5736e..b58098e419677 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -155,57 +155,48 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let (left, right) = (normalize(left), normalize(right)); + let left_kind = self.ty_to_primval_kind(left_ty)?; + let right_kind = self.ty_to_primval_kind(right_ty)?; + // Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers. if bin_op == Offset { - let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?; - return Ok((PrimVal::Ptr(ptr), false)); + if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) { + let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; + let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?; + return Ok((PrimVal::Ptr(ptr), false)); + } else { + bug!("Offset used with wrong type"); + } } let (l, r) = match (left, right) { (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), + // One argument is a pointer value -- this is handled separately (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { - if left_ptr.alloc_id == right_ptr.alloc_id { - // If the pointers are into the same allocation, fall through to the more general - // match later, which will do comparisons on the pointer offsets. - (left_ptr.offset as u128, right_ptr.offset as u128) - } else { - return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false)); - } + return self.ptr_ops(bin_op, left_ptr, left_kind, right_ptr, right_kind); + } + (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) => { + return self.ptr_ops(bin_op, ptr, left_kind, Pointer::from_int(bytes as u64), right_kind); } - - (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) | (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { - return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false)); + return self.ptr_ops(bin_op, Pointer::from_int(bytes as u64), left_kind, ptr, right_kind); } (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), }; - let left_kind = self.ty_to_primval_kind(left_ty)?; - let right_kind = self.ty_to_primval_kind(right_ty)?; - // These ops can have an RHS with a different numeric type. - if bin_op == Shl || bin_op == Shr { + if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { return match bin_op { Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), _ => bug!("it has already been checked that this is a shift op"), }; } - if bin_op == Offset { - // We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM. - if left_kind == PrimValKind::Ptr && right_kind.is_int() && r == 0 { - return Ok((PrimVal::Bytes(l), false)); - } else { - let msg = format!("unimplemented Offset: {:?}, {:?}", left, right); - return Err(EvalError::Unimplemented(msg)); - } - } if left_kind != right_kind { - let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); + let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); return Err(EvalError::Unimplemented(msg)); } @@ -258,25 +249,58 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), _ => { - let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); + let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); return Err(EvalError::Unimplemented(msg)); } }; Ok((val, false)) } -} -fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::BinOp::*; - match bin_op { - Eq => Ok(PrimVal::from_bool(false)), - Ne => Ok(PrimVal::from_bool(true)), - Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), - _ if left.to_int().is_ok() ^ right.to_int().is_ok() => { - Err(EvalError::ReadPointerAsBytes) - }, - _ => bug!(), + fn ptr_ops( + &self, + bin_op: mir::BinOp, + left: Pointer, + left_kind: PrimValKind, + right: Pointer, + right_kind: PrimValKind, + ) -> EvalResult<'tcx, (PrimVal, bool)> { + use rustc::mir::BinOp::*; + use value::PrimValKind::*; + + if left_kind != right_kind { + let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); + return Err(EvalError::Unimplemented(msg)); + } + + let val = match (bin_op, left_kind) { + (Eq, k) if k.is_ptr() => PrimVal::from_bool(left == right), + (Ne, k) if k.is_ptr() => PrimVal::from_bool(left != right), + (Lt, k) | (Le, k) | (Gt, k) | (Ge, k) if k.is_ptr() => { + if left.alloc_id == right.alloc_id { + PrimVal::from_bool(match bin_op { + Lt => left.offset < right.offset, + Le => left.offset <= right.offset, + Gt => left.offset > right.offset, + Ge => left.offset >= right.offset, + _ => bug!("We already established it has to be a comparison operator."), + }) + } else { + return Err(EvalError::InvalidPointerMath); + } + } + (Sub, k) if k == PrimValKind::from_uint_size(self.memory.pointer_size()) => { + if left.alloc_id == right.alloc_id { + return int_arithmetic!(k, overflowing_sub, left.offset, right.offset); + } else { + return Err(EvalError::InvalidPointerMath); + } + } + _ => { + return Err(EvalError::ReadPointerAsBytes); + } + }; + Ok((val, false)) } } diff --git a/src/value.rs b/src/value.rs index efe5aac71f5bc..fe4c6608ed380 100644 --- a/src/value.rs +++ b/src/value.rs @@ -243,4 +243,12 @@ impl PrimValKind { _ => bug!("can't make int with size {}", size), } } + + pub fn is_ptr(self) -> bool { + use self::PrimValKind::*; + match self { + Ptr | FnPtr => true, + _ => false, + } + } } diff --git a/tests/compile-fail/overflowing-lsh-neg.rs b/tests/compile-fail/overflowing-lsh-neg.rs new file mode 100644 index 0000000000000..3a889be741efd --- /dev/null +++ b/tests/compile-fail/overflowing-lsh-neg.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(exceeding_bitshifts)] +#![allow(const_err)] + +fn main() { + let _n = 2i64 << -1; //~ Overflow(Shl) +} diff --git a/tests/compile-fail/ptr_bitops.rs b/tests/compile-fail/ptr_bitops.rs new file mode 100644 index 0000000000000..78fd8e912b5e7 --- /dev/null +++ b/tests/compile-fail/ptr_bitops.rs @@ -0,0 +1,7 @@ +fn main() { + let bytes = [0i8, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let one = bytes.as_ptr().wrapping_offset(1); + let three = bytes.as_ptr().wrapping_offset(3); + let res = (one as usize) | (three as usize); //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes + println!("{}", res); +} From 7bfda59fe274035bd9cb393da9a285c40f306dea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 17:14:48 -0700 Subject: [PATCH 0965/1096] don't bother inserting integer relocations into the relocation table --- src/memory.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 24ac27f8fa324..32763d47d170e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -620,7 +620,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - if self.has_non_int_relocations(ptr, size)? { + if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } self.check_defined(ptr, size)?; @@ -718,7 +718,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = ptr.offset as usize; match alloc.bytes[offset..].iter().position(|&c| c == 0) { Some(size) => { - if self.has_non_int_relocations(ptr, (size + 1) as u64)? { + if self.relocations(ptr, (size + 1) as u64)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } self.check_defined(ptr, (size + 1) as u64)?; @@ -761,7 +761,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> { self.write_usize(dest, ptr.offset as u64)?; - self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); + if ptr.alloc_id != NEVER_ALLOC_ID { + self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); + } Ok(()) } @@ -902,12 +904,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) } - fn has_non_int_relocations(&self, ptr: Pointer, size: u64) - -> EvalResult<'tcx, bool> - { - Ok(self.relocations(ptr, size)?.any(|(_, &alloc_id)| alloc_id != NEVER_ALLOC_ID)) - } - fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); From 684de68d6c2d0d0b4da7cc5cdec92d3fe488ea8c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 18:07:26 -0700 Subject: [PATCH 0966/1096] properly wrap pointer offsets at pointer size --- src/eval_context.rs | 20 ++++++++++---------- src/lvalue.rs | 8 ++++---- src/memory.rs | 24 ++++++++++++++---------- src/terminator/mod.rs | 10 +++++----- src/traits.rs | 10 +++++----- src/value.rs | 4 ++-- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e601f4b38fac0..d45c419c78a9a 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -391,7 +391,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); - let discr_dest = dest_ptr.offset(discr_offset)?; + let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?; self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); - let dest = dest.offset(offset.bytes())?; + let dest = dest.offset(offset.bytes(), self.memory.layout)?; let dest_size = self.type_size(ty)? .expect("bad StructWrappedNullablePointer discrfield"); self.memory.write_int(dest, 0, dest_size)?; @@ -610,7 +610,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); for i in 0..length { - let elem_dest = dest.offset(i * elem_size)?; + let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -854,7 +854,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; - Ok(ptr.wrapping_signed_offset(offset)) + Ok(ptr.wrapping_signed_offset(offset, self.memory.layout)) } pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { @@ -865,7 +865,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; return if let Some(offset) = offset.checked_mul(pointee_size) { - let ptr = ptr.signed_offset(offset)?; + let ptr = ptr.signed_offset(offset, self.memory.layout)?; self.memory.check_bounds(ptr, false)?; Ok(ptr) } else { @@ -1124,8 +1124,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0)?, a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1)?, b, field_1_size)?; + self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?, a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?, b, field_1_size)?; Ok(()) } @@ -1242,7 +1242,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(PrimVal::Ptr(p))) } else { trace!("reading fat pointer extra of type {}", pointee_ty); - let extra = ptr.offset(self.memory.pointer_size())?; + let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; let extra = match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | @@ -1427,8 +1427,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); - let src_f_ptr = src_ptr.offset(src_field_offset)?; - let dst_f_ptr = dest.offset(dst_field_offset)?; + let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; + let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr, src_fty)?; } else { diff --git a/src/lvalue.rs b/src/lvalue.rs index e4deddbfb48b4..c5811774e941e 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -270,7 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => offset.bytes(), }; - let ptr = base_ptr.offset(offset)?; + let ptr = base_ptr.offset(offset, self.memory.layout)?; let field_ty = self.monomorphize(field_ty, self.substs()); @@ -363,7 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); - let ptr = base_ptr.offset(n * elem_size)?; + let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; (ptr, LvalueExtra::None) } @@ -384,7 +384,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size)?; + let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?; (ptr, LvalueExtra::None) } @@ -398,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size)?; + let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?; let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); (ptr, extra) } diff --git a/src/memory.rs b/src/memory.rs index 32763d47d170e..18f757a6bd6c0 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -60,11 +60,11 @@ impl Pointer { Pointer { alloc_id, offset } } - pub fn wrapping_signed_offset<'tcx>(self, i: i64) -> Self { - Pointer::new(self.alloc_id, self.offset.wrapping_add(i as u64)) + pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { + Pointer::new(self.alloc_id, (self.offset.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64) } - pub fn signed_offset<'tcx>(self, i: i64) -> EvalResult<'tcx, Self> { + pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { // FIXME: is it possible to over/underflow here? if i < 0 { // trickery to ensure that i64::min_value() works fine @@ -76,13 +76,17 @@ impl Pointer { Err(EvalError::OverflowingPointerMath) } } else { - self.offset(i as u64) + self.offset(i as u64, layout) } } - pub fn offset<'tcx>(self, i: u64) -> EvalResult<'tcx, Self> { + pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { if let Some(res) = self.offset.checked_add(i) { - Ok(Pointer::new(self.alloc_id, res)) + if res as u128 >= (1u128 << layout.pointer_size.bits()) { + Err(EvalError::OverflowingPointerMath) + } else { + Ok(Pointer::new(self.alloc_id, res)) + } } else { Err(EvalError::OverflowingPointerMath) } @@ -283,7 +287,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.grow(amount, false); } else if size > new_size { self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size)?, size - new_size)?; + self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; // `as usize` is fine here, since it is smaller than `size`, which came from a usize alloc.bytes.truncate(new_size as usize); @@ -595,7 +599,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&[]); } self.check_align(ptr, align, size)?; - self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -608,7 +612,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } self.check_align(ptr, align, size)?; - self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -930,7 +934,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size)?, 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count(); if overlapping_start + overlapping_end != 0 { return Err(EvalError::ReadPointerAsBytes); } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ebf8723e827e1..afc3bf1c37feb 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match arg_val { Value::ByRef(ptr) => { for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)?); + let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); self.write_value(arg, dest, ty)?; @@ -387,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; - let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3))?)?; + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); @@ -473,7 +473,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes())?; + let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); @@ -654,7 +654,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { - let new_ptr = ptr.offset(num - idx as u64 - 1)?; + let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -666,7 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { - let new_ptr = ptr.offset(idx as u64)?; + let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; diff --git a/src/traits.rs b/src/traits.rs index bf9e47da991a1..322ebc1981b48 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -56,14 +56,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; - self.memory.write_usize(vtable.offset(ptr_size)?, size)?; - self.memory.write_usize(vtable.offset(ptr_size * 2)?, align)?; + self.memory.write_usize(vtable.offset(ptr_size, self.memory.layout)?, size)?; + self.memory.write_usize(vtable.offset(ptr_size * 2, self.memory.layout)?, align)?; for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { let instance = ::eval_context::resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64))?, fn_ptr)?; + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; } } @@ -88,8 +88,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - let size = self.memory.read_usize(vtable.offset(pointer_size)?)?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2)?)?; + let size = self.memory.read_usize(vtable.offset(pointer_size, self.memory.layout)?)?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.memory.layout)?)?; Ok((size, align)) } diff --git a/src/value.rs b/src/value.rs index fe4c6608ed380..387002eee7bc2 100644 --- a/src/value.rs +++ b/src/value.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size())?)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; Ok((ptr, vtable)) } @@ -105,7 +105,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size())?)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; Ok((ptr, len)) }, ByValPair(ptr, val) => { From 91b93bc701dfb015c375ee485792ce06615362c6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 18:23:25 -0700 Subject: [PATCH 0967/1096] less strict kind test for pointer operations --- src/operator.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index b58098e419677..3fe3b63407910 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -268,15 +268,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::BinOp::*; use value::PrimValKind::*; - if left_kind != right_kind { + if left_kind != right_kind || !(left_kind.is_ptr() || left_kind == PrimValKind::from_uint_size(self.memory.pointer_size())) { let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); return Err(EvalError::Unimplemented(msg)); } - let val = match (bin_op, left_kind) { - (Eq, k) if k.is_ptr() => PrimVal::from_bool(left == right), - (Ne, k) if k.is_ptr() => PrimVal::from_bool(left != right), - (Lt, k) | (Le, k) | (Gt, k) | (Ge, k) if k.is_ptr() => { + let val = match bin_op { + Eq => PrimVal::from_bool(left == right), + Ne => PrimVal::from_bool(left != right), + Lt | Le | Gt | Ge => { if left.alloc_id == right.alloc_id { PrimVal::from_bool(match bin_op { Lt => left.offset < right.offset, @@ -289,9 +289,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::InvalidPointerMath); } } - (Sub, k) if k == PrimValKind::from_uint_size(self.memory.pointer_size()) => { + Sub => { if left.alloc_id == right.alloc_id { - return int_arithmetic!(k, overflowing_sub, left.offset, right.offset); + return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset); } else { return Err(EvalError::InvalidPointerMath); } From c8be312933c375a76a021a947330f1024dc83b9a Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 6 Jun 2017 09:49:34 -0400 Subject: [PATCH 0968/1096] fix issue 184 by marking the destination as a packed struct --- src/terminator/intrinsic.rs | 11 ++++++++++- tests/run-pass/issue-miri-184.rs | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/run-pass/issue-miri-184.rs diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 31886e9cc6d91..193b7c3cde177 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -383,8 +383,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "transmute" => { + let src_ty = substs.type_at(0); let dest_ty = substs.type_at(1); - self.write_value(arg_vals[0], dest, dest_ty)?; + let (_, src_align) = self.size_and_align_of_dst(src_ty, arg_vals[0])?; + let (size, dest_align) = self.size_and_align_of_dst(dest_ty, arg_vals[0])?; + if dest_align < src_align { + let ptr = self.force_allocation(dest)?.to_ptr(); + self.memory.mark_packed(ptr, size); + self.write_value_to_ptr(arg_vals[0], ptr, dest_ty)?; + } else { + self.write_value(arg_vals[0], dest, dest_ty)?; + } } "uninit" => { diff --git a/tests/run-pass/issue-miri-184.rs b/tests/run-pass/issue-miri-184.rs new file mode 100644 index 0000000000000..24775fe8a2d9d --- /dev/null +++ b/tests/run-pass/issue-miri-184.rs @@ -0,0 +1,4 @@ +pub fn main() { + let bytes: [u8; 8] = unsafe { ::std::mem::transmute(0u64) }; + let _: &[u8] = &bytes; +} From 49fb43f293fa933871f52be7189905adbe9ff53c Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 6 Jun 2017 10:29:53 -0400 Subject: [PATCH 0969/1096] use type_align() and type_size() instaed of size_and_align_of_dst() --- src/terminator/intrinsic.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 193b7c3cde177..696381b1c0c09 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -385,8 +385,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); let dest_ty = substs.type_at(1); - let (_, src_align) = self.size_and_align_of_dst(src_ty, arg_vals[0])?; - let (size, dest_align) = self.size_and_align_of_dst(dest_ty, arg_vals[0])?; + let src_align = self.type_align(src_ty)?; + let dest_align = self.type_align(dest_ty)?; + let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); if dest_align < src_align { let ptr = self.force_allocation(dest)?.to_ptr(); self.memory.mark_packed(ptr, size); From e1562fbe71c8b444e0f5b2977bf8e36ddfc36b8b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 6 Jun 2017 10:15:39 -0700 Subject: [PATCH 0970/1096] comments --- src/memory.rs | 3 ++- src/terminator/intrinsic.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 18f757a6bd6c0..66180d6075c71 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -166,7 +166,8 @@ pub struct Memory<'a, 'tcx> { /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking /// afterwards. In the case where no packed structs are present, it's just a single emptyness /// check of a set instead of heavily influencing all memory access code as other solutions - /// would. + /// would. This is simpler than the alternative of passing a "packed" parameter to every + /// load/store method. /// /// One disadvantage of this solution is the fact that you can cast a pointer to a packed /// struct to a pointer to a normal struct and if you access a field of both in the same MIR diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 696381b1c0c09..ce3216f00153a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -43,7 +43,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { - // FIXME: Switch to non-checked, wrapped version of pointer_offset let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; From 3e1596d8c9f8e2eb586d3aa8dcdafa8375ecf8ec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Jun 2017 15:39:44 -0700 Subject: [PATCH 0971/1096] Error out when "primitive MIR math" (as opposed to unchecked intrinsics) overflows Fixes #178 --- src/error.rs | 6 +++--- src/eval_context.rs | 12 +++++++++--- src/memory.rs | 6 +++--- tests/compile-fail/out_of_bounds_ptr_2.rs | 2 +- tests/compile-fail/ptr_offset_overflow.rs | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/error.rs b/src/error.rs index dc7b227b7972e..1a8ca2d03ca6c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,7 +23,6 @@ pub enum EvalError<'tcx> { }, ReadPointerAsBytes, InvalidPointerMath, - OverflowingPointerMath, ReadUndefBytes, DeadLocal, InvalidBoolOp(mir::BinOp), @@ -32,6 +31,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), + OverflowingMath, InvalidChar(u128), OutOfMemory { allocation_size: u64, @@ -83,8 +83,6 @@ impl<'tcx> Error for EvalError<'tcx> { "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::InvalidPointerMath => "attempted to do math or a comparison on pointers into different allocations", - EvalError::OverflowingPointerMath => - "attempted to do overflowing math on a pointer", EvalError::ReadUndefBytes => "attempted to read undefined bytes", EvalError::DeadLocal => @@ -100,6 +98,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::OverflowingMath => + "attempted to do overflowing math", EvalError::NoMirFor(..) => "mir not found", EvalError::InvalidChar(..) => diff --git a/src/eval_context.rs b/src/eval_context.rs index d45c419c78a9a..b9310f1f899a8 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -452,8 +452,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } BinaryOp(bin_op, ref left, ref right) => { - // ignore overflow bit, rustc inserts check branches for us - self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?; + if self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)? { + // There was an overflow in an unchecked binop. Right now, we consider this an error and bail out. + // The rationale is that the reason rustc emits unchecked binops in release mode (vs. the checked binops + // it emits in debug mode) is performance, but it doesn't cust us any performance in miri. + // If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops, + // we have to go back to just ignoring the overflow here. + return Err(EvalError::OverflowingMath); + } } CheckedBinaryOp(bin_op, ref left, ref right) => { @@ -869,7 +875,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.check_bounds(ptr, false)?; Ok(ptr) } else { - Err(EvalError::OverflowingPointerMath) + Err(EvalError::OverflowingMath) } } diff --git a/src/memory.rs b/src/memory.rs index 66180d6075c71..d663835bc1fc6 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -73,7 +73,7 @@ impl Pointer { if let Some(res) = self.offset.checked_sub(n) { Ok(Pointer::new(self.alloc_id, res)) } else { - Err(EvalError::OverflowingPointerMath) + Err(EvalError::OverflowingMath) } } else { self.offset(i as u64, layout) @@ -83,12 +83,12 @@ impl Pointer { pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { if let Some(res) = self.offset.checked_add(i) { if res as u128 >= (1u128 << layout.pointer_size.bits()) { - Err(EvalError::OverflowingPointerMath) + Err(EvalError::OverflowingMath) } else { Ok(Pointer::new(self.alloc_id, res)) } } else { - Err(EvalError::OverflowingPointerMath) + Err(EvalError::OverflowingMath) } } diff --git a/tests/compile-fail/out_of_bounds_ptr_2.rs b/tests/compile-fail/out_of_bounds_ptr_2.rs index 0bb670fd022f7..f7546494574b0 100644 --- a/tests/compile-fail/out_of_bounds_ptr_2.rs +++ b/tests/compile-fail/out_of_bounds_ptr_2.rs @@ -1,4 +1,4 @@ -// error-pattern: overflowing math on a pointer +// error-pattern: overflowing math fn main() { let v = [0i8; 4]; let x = &v as *const i8; diff --git a/tests/compile-fail/ptr_offset_overflow.rs b/tests/compile-fail/ptr_offset_overflow.rs index ebd972a871752..578468c3399bb 100644 --- a/tests/compile-fail/ptr_offset_overflow.rs +++ b/tests/compile-fail/ptr_offset_overflow.rs @@ -1,4 +1,4 @@ -//error-pattern: overflowing math on a pointer +//error-pattern: overflowing math fn main() { let v = [1i8, 2]; let x = &v[1] as *const i8; From e0559a6b24ce6e4c4b3f07c7f82b5e88aede8e88 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 8 Jun 2017 10:56:49 -0700 Subject: [PATCH 0972/1096] typos --- src/eval_context.rs | 2 +- src/memory.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index b9310f1f899a8..e358b98911a1e 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -455,7 +455,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)? { // There was an overflow in an unchecked binop. Right now, we consider this an error and bail out. // The rationale is that the reason rustc emits unchecked binops in release mode (vs. the checked binops - // it emits in debug mode) is performance, but it doesn't cust us any performance in miri. + // it emits in debug mode) is performance, but it doesn't cost us any performance in miri. // If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops, // we have to go back to just ignoring the overflow here. return Err(EvalError::OverflowingMath); diff --git a/src/memory.rs b/src/memory.rs index d663835bc1fc6..f106f9d088c00 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -166,7 +166,7 @@ pub struct Memory<'a, 'tcx> { /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking /// afterwards. In the case where no packed structs are present, it's just a single emptyness /// check of a set instead of heavily influencing all memory access code as other solutions - /// would. This is simpler than the alternative of passing a "packed" parameter to every + /// would. This is simpler than the alternative of passing a "packed" parameter to every /// load/store method. /// /// One disadvantage of this solution is the fact that you can cast a pointer to a packed From 3a5abf031bd576e791464365fec154bfb1d9b794 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 8 Jun 2017 11:34:49 -0700 Subject: [PATCH 0973/1096] fix comment in aux_test --- tests/run-pass/aux_test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index dd46c638a2afe..4ab121d121031 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,6 +1,7 @@ // aux-build:dep.rs -// This ignores the test against rustc, but runs it against miri: + // ignore-cross-compile +// TODO: The above accidentally also ignores this test against rustc even when are are not cross-compiling. extern crate dep; From f174cc8a4c5dd4038f8c83389b7834d30819a9f1 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Sat, 10 Jun 2017 20:39:48 -0400 Subject: [PATCH 0974/1096] tcx.infer_ctxt() no longer takes an argument --- src/eval_context.rs | 2 +- src/traits.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e358b98911a1e..6bb7640356501 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -2032,7 +2032,7 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - tcx.infer_ctxt(()).enter(|infcx| { + tcx.infer_ctxt().enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation_cause = traits::ObligationCause::misc(span, diff --git a/src/traits.rs b/src/traits.rs index 322ebc1981b48..9c4e60a299f4a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -16,7 +16,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn fulfill_obligation(&self, trait_ref: ty::PolyTraitRef<'tcx>) -> traits::Vtable<'tcx, ()> { // Do the initial selection for the obligation. This yields the shallow result we are // looking for -- that is, what specific impl. - self.tcx.infer_ctxt(()).enter(|infcx| { + self.tcx.infer_ctxt().enter(|infcx| { let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new( From 7504512cbdf2bbbe53c633392d3e7f91c144f2d0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 Jun 2017 17:24:23 -0700 Subject: [PATCH 0975/1096] rustup 1.4 fixed the permissions of the extracted files --- .travis.yml | 1 - README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7cc14234efd58..5a2a893196dc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ before_script: - rustup target add i686-pc-windows-gnu - rustup target add i686-pc-windows-msvc - rustup component add rust-src -- chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/ - cargo install xargo - export RUST_SYSROOT=$HOME/rust script: diff --git a/README.md b/README.md index d5873c986815d..bd9599f1c068f 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,6 @@ possible to compile libstd with full MIR: ```sh rustup component add rust-src -chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/ cargo install xargo cd xargo/ RUSTFLAGS='-Zalways-encode-mir' xargo build From 4ac9fa67a4473c27c2b8d2f43611d1ca0f113b90 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 11 Jun 2017 21:14:40 -0700 Subject: [PATCH 0976/1096] enable a test that was accidentally left disabled --- tests/run-pass/drop_empty_slice.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/run-pass/drop_empty_slice.rs b/tests/run-pass/drop_empty_slice.rs index 25e7bc329db54..b21c8a612c57b 100644 --- a/tests/run-pass/drop_empty_slice.rs +++ b/tests/run-pass/drop_empty_slice.rs @@ -1,9 +1,7 @@ #![feature(box_syntax)] -// This disables the test completely: -// ignore-stage1 fn main() { - // With the nested Vec, this is calling Offset(Unique::empty(), 0). + // With the nested Vec, this is calling Offset(Unique::empty(), 0) on drop. let args : Vec> = Vec::new(); - let local = box args; + let _ = box args; } From 03577a905a12b9bd23a5a1e0b5f9e2403ba8a91a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 12 Jun 2017 13:56:29 +0200 Subject: [PATCH 0977/1096] Fix some clippy warnings --- src/eval_context.rs | 4 ++-- tests/run-pass/union.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 6bb7640356501..3b21f96bde0ce 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1680,12 +1680,12 @@ pub fn eval_main<'a, 'tcx: 'a>( } } Err(e) => { - report(tcx, &ecx, e); + report(tcx, &ecx, &e); } } } -fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { +fn report(tcx: TyCtxt, ecx: &EvalContext, e: &EvalError) { if let Some(frame) = ecx.stack().last() { let block = &frame.mir.basic_blocks()[frame.block]; let span = if frame.stmt < block.statements.len() { diff --git a/tests/run-pass/union.rs b/tests/run-pass/union.rs index e51c601289697..342c94f3d4a34 100644 --- a/tests/run-pass/union.rs +++ b/tests/run-pass/union.rs @@ -62,7 +62,7 @@ fn c() { unsafe { match v { Value { tag: Tag::I, u: U { i: 0 } } => true, - Value { tag: Tag::F, u: U { f } } if f == 0.0 => true, + Value { tag: Tag::F, u: U { f } } => f == 0.0, _ => false, } } From 4b1a12c2403280910f9a76736c91ed3c4f99b3c0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 12 Jun 2017 15:22:58 -0700 Subject: [PATCH 0978/1096] test the Rc::{into,from}_raw roundtrip This uses some pointer arithmetic based on field offsets --- tests/run-pass/rc.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/run-pass/rc.rs b/tests/run-pass/rc.rs index c96818932d777..c6de3675abe8c 100644 --- a/tests/run-pass/rc.rs +++ b/tests/run-pass/rc.rs @@ -8,6 +8,16 @@ fn rc_refcell() -> i32 { x } +fn rc_raw() { + let r = Rc::new(0); + let r2 = Rc::into_raw(r.clone()); + let r2 = unsafe { Rc::from_raw(r2) }; + assert!(Rc::ptr_eq(&r, &r2)); + drop(r); + assert!(Rc::try_unwrap(r2).is_ok()); +} + fn main() { rc_refcell(); + rc_raw(); } From a28c7990eab3418e7a43b22f63b24b00f0876245 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 20 Jun 2017 07:08:29 +0900 Subject: [PATCH 0979/1096] update compiletest and remove obsolete comment --- Cargo.lock | 6 +++--- tests/run-pass/aux_test.rs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b868ed6671a48..96022d4655280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = "0.1.0" dependencies = [ "byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)", "cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -227,7 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)" = "" "checksum cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d84cb53c78e573aa126a4b9f963fdb2629f8183b26e235da08bb36dc7381162" -"checksum compiletest_rs 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "df47edea8bf052f23ce25a15cbf0be09c96911e3be943d1e81415bfaf0e74bf8" +"checksum compiletest_rs 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ea3116e739370ad85431a30446b5068ba79171bc6c3d458e90adc834df71359a" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" diff --git a/tests/run-pass/aux_test.rs b/tests/run-pass/aux_test.rs index 4ab121d121031..beed82e058029 100644 --- a/tests/run-pass/aux_test.rs +++ b/tests/run-pass/aux_test.rs @@ -1,7 +1,6 @@ // aux-build:dep.rs // ignore-cross-compile -// TODO: The above accidentally also ignores this test against rustc even when are are not cross-compiling. extern crate dep; From f14ebd1142ec6fb319e17b1ef8bf5b2db710f8ef Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 20 Jun 2017 19:35:46 +0900 Subject: [PATCH 0980/1096] handle EndRegion as no-op --- src/step.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/step.rs b/src/step.rs index 9e04d583a2252..839f666f621d0 100644 --- a/src/step.rs +++ b/src/step.rs @@ -141,6 +141,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.deallocate_local(old_val)?; } + EndRegion(..) => {} + // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} From f5ca91e81280034c7fca0ce2f52f24513e7b6ee5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Jun 2017 09:52:58 +0200 Subject: [PATCH 0981/1096] Update to latest nightly --- src/step.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/step.rs b/src/step.rs index 839f666f621d0..eaf3069a907e0 100644 --- a/src/step.rs +++ b/src/step.rs @@ -141,6 +141,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.deallocate_local(old_val)?; } + // Just a borrowck thing EndRegion(..) => {} // Defined to do nothing. These are added by optimization passes, to avoid changing the From fcf495821e3f26f9b14472d3c05bb6847106108d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 10:58:59 +0200 Subject: [PATCH 0982/1096] Get rid of the integer allocation --- src/cast.rs | 9 +- src/error.rs | 3 + src/eval_context.rs | 54 ++++++----- src/lvalue.rs | 29 +++--- src/memory.rs | 93 ++++++------------- src/operator.rs | 95 +++++++++----------- src/step.rs | 11 ++- src/terminator/drop.rs | 9 +- src/terminator/intrinsic.rs | 40 +++++---- src/terminator/mod.rs | 48 +++++----- src/traits.rs | 19 ++-- src/value.rs | 82 ++++++++++++++--- tests/compile-fail/cast_box_int_to_fn_ptr.rs | 2 +- tests/compile-fail/cast_int_to_fn_ptr.rs | 2 +- tests/compile-fail/null_pointer_deref.rs | 2 +- tests/compile-fail/wild_pointer_deref.rs | 2 +- 16 files changed, 256 insertions(+), 244 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index 413c9b6ba827b..aa24de944a7c0 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -3,7 +3,6 @@ use syntax::ast::{FloatTy, IntTy, UintTy}; use error::{EvalResult, EvalError}; use eval_context::EvalContext; -use memory::Pointer; use value::PrimVal; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -24,7 +23,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false), - FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty), + FnPtr | Ptr => self.cast_ptr(val, dest_ty), } } @@ -71,7 +70,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), - TyRawPtr(_) => Ok(PrimVal::Ptr(Pointer::from_int(v as u64))), + TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } @@ -92,11 +91,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => - Ok(PrimVal::Ptr(ptr)), + Ok(ptr), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/error.rs b/src/error.rs index 1a8ca2d03ca6c..e7e446e93ad7c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,6 +22,7 @@ pub enum EvalError<'tcx> { allocation_size: u64, }, ReadPointerAsBytes, + ReadBytesAsPointer, InvalidPointerMath, ReadUndefBytes, DeadLocal, @@ -81,6 +82,8 @@ impl<'tcx> Error for EvalError<'tcx> { "pointer offset outside bounds of allocation", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", + EvalError::ReadBytesAsPointer => + "a memory access tried to interpret some bytes as a pointer", EvalError::InvalidPointerMath => "attempted to do math or a comparison on pointers into different allocations", EvalError::ReadUndefBytes => diff --git a/src/eval_context.rs b/src/eval_context.rs index 3b21f96bde0ce..55a031b310a58 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -66,7 +66,8 @@ pub struct Frame<'tcx> { pub return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to. - pub return_lvalue: Lvalue<'tcx>, + /// None if the function is a diverging function + pub return_lvalue: Option>, /// The list of locals for this stack frame, stored in order as /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option`s. @@ -266,7 +267,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance: ty::Instance<'tcx>, span: codemap::Span, mir: &'tcx mir::Mir<'tcx>, - return_lvalue: Lvalue<'tcx>, + return_lvalue: Option>, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -323,7 +324,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { - StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue { + StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue.expect("diverging static") { let global_value = self.globals.get_mut(&id) .expect("global should have been cached (static)"); match global_value.value { @@ -389,13 +390,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { where J::IntoIter: ExactSizeIterator, { // FIXME(solson) - let dest_ptr = self.force_allocation(dest)?.to_ptr(); + let dest_ptr = self.force_allocation(dest)?.to_ptr()?; let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?; self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { - ptr: dest_ptr, + ptr: PrimVal::Ptr(dest_ptr), extra: LvalueExtra::DowncastVariant(variant_idx), }; @@ -481,7 +482,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match *dest_layout { Univariant { ref variant, .. } => { if variant.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; self.memory.mark_packed(ptr, variant.stride().bytes()); } self.assign_fields(dest, dest_ty, operands)?; @@ -499,7 +500,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .to_u128_unchecked(); let discr_size = discr.size().bytes(); if variants[variant].packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; self.memory.mark_packed(ptr, variants[variant].stride().bytes()); } @@ -541,7 +542,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { if nonnull.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0; + let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; self.memory.mark_packed(ptr, nonnull.stride().bytes()); } if nndiscr == variant as u64 { @@ -554,7 +555,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr()?; let dest = dest.offset(offset.bytes(), self.memory.layout)?; let dest_size = self.type_size(ty)? @@ -613,7 +614,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr()?; for i in 0..length { let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; @@ -630,8 +631,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; - let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); - let ptr = PrimVal::Ptr(raw_ptr); + let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); let val = match extra { LvalueExtra::None => Value::ByVal(ptr), @@ -726,7 +726,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Discriminant(ref lvalue) => { let lval = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); - let ptr = self.force_allocation(lval)?.to_ptr(); + let ptr = self.force_allocation(lval)?.to_ptr()?; let discr_val = self.read_discriminant_value(ptr, ty)?; if let ty::TyAdt(adt_def, _) = ty.sty { if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) { @@ -856,14 +856,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; - Ok(ptr.wrapping_signed_offset(offset, self.memory.layout)) + ptr.wrapping_signed_offset(offset, self.memory.layout) } - pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { if offset == 0 { // rustc relies on Offset-by-0 to be well-defined even for "bad" pointers like Unique::empty(). return Ok(ptr); @@ -872,7 +872,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; - self.memory.check_bounds(ptr, false)?; + self.memory.check_bounds(ptr.to_ptr()?, false)?; Ok(ptr) } else { Err(EvalError::OverflowingMath) @@ -1036,7 +1036,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(src_val, ptr, dest_ty) + self.write_value_to_ptr(src_val, ptr.to_ptr()?, dest_ty) } Lvalue::Local { frame, local, field } => { @@ -1242,20 +1242,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn read_ptr(&mut self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(crate) fn read_ptr(&self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(pointee_ty) { - Ok(Value::ByVal(PrimVal::Ptr(p))) + Ok(Value::ByVal(p)) } else { trace!("reading fat pointer extra of type {}", pointee_ty); let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; let extra = match self.tcx.struct_tail(pointee_ty).sty { - ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), + ty::TyDynamic(..) => self.memory.read_ptr(extra)?, ty::TySlice(..) | ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), _ => bug!("unsized primval ptr read from {:?}", pointee_ty), }; - Ok(Value::ByValPair(PrimVal::Ptr(p), extra)) + Ok(Value::ByValPair(p, extra)) } } @@ -1301,7 +1301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Ptr)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr)?, ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, tam.ty).map(Some), @@ -1360,7 +1360,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; let len = PrimVal::from_u128(length as u128); - let ptr = PrimVal::Ptr(ptr); self.write_value(Value::ByValPair(ptr, len), dest, dest_ty) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { @@ -1374,7 +1373,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - let ptr = PrimVal::Ptr(ptr); let extra = PrimVal::Ptr(vtable); self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty) }, @@ -1423,7 +1421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr()?; let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); @@ -1632,7 +1630,7 @@ pub fn eval_main<'a, 'tcx: 'a>( start_instance, start_mir.span, start_mir, - Lvalue::from_ptr(ret_ptr), + Some(Lvalue::from_ptr(ret_ptr)), StackPopCleanup::None, )?; @@ -1659,7 +1657,7 @@ pub fn eval_main<'a, 'tcx: 'a>( main_instance, main_mir.span, main_mir, - Lvalue::from_ptr(Pointer::zst_ptr()), + Some(Lvalue::zst()), StackPopCleanup::None, )?; } diff --git a/src/lvalue.rs b/src/lvalue.rs index c5811774e941e..b61c18274aeb0 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -12,7 +12,10 @@ use value::{PrimVal, Value}; pub enum Lvalue<'tcx> { /// An lvalue referring to a value allocated in the `Memory` system. Ptr { - ptr: Pointer, + /// An lvalue may have an invalid (integral or undef) pointer, + /// since it might be turned back into a reference + /// before ever being dereferenced. + ptr: PrimVal, extra: LvalueExtra, }, @@ -61,11 +64,15 @@ pub struct Global<'tcx> { } impl<'tcx> Lvalue<'tcx> { + pub fn zst() -> Self { + Self::from_ptr(Pointer::zst_ptr()) + } + pub fn from_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr, extra: LvalueExtra::None } + Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra: LvalueExtra::None } } - pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { + pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) { match self { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), @@ -73,10 +80,10 @@ impl<'tcx> Lvalue<'tcx> { } } - pub(super) fn to_ptr(self) -> Pointer { + pub(super) fn to_ptr(self) -> EvalResult<'tcx, Pointer> { let (ptr, extra) = self.to_ptr_and_extra(); assert_eq!(extra, LvalueExtra::None); - ptr + ptr.to_ptr() } pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) { @@ -127,7 +134,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) + Ok(Value::ByRef(ptr.to_ptr()?)) } Lvalue::Local { frame, local, field } => { self.stack[frame].get_local(local, field.map(|(i, _)| i)) @@ -141,7 +148,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { - Local(mir::RETURN_POINTER) => self.frame().return_lvalue, + Local(mir::RETURN_POINTER) => self.frame().return_lvalue.expect("diverging function returned"), Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(ref static_) => { @@ -229,14 +236,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i))? { Value::ByRef(ptr) => { assert!(field.is_none(), "local can't be ByRef and have a field offset"); - (ptr, LvalueExtra::None) + (PrimVal::Ptr(ptr), LvalueExtra::None) }, Value::ByVal(PrimVal::Undef) => { // FIXME: allocate in fewer cases if self.ty_to_primval_kind(base_ty).is_ok() { return Ok(base); } else { - (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + (PrimVal::Ptr(self.force_allocation(base)?.to_ptr()?), LvalueExtra::None) } }, Value::ByVal(_) => { @@ -264,7 +271,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = match base_extra { LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; + let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?; offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() } _ => offset.bytes(), @@ -276,7 +283,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); - self.memory.mark_packed(ptr, size); + self.memory.mark_packed(ptr.to_ptr()?, size); } let extra = if self.type_is_sized(field_ty) { diff --git a/src/memory.rs b/src/memory.rs index f106f9d088c00..9e33a162b5923 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,7 +6,7 @@ use rustc::ty; use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; -use value::PrimVal; +use value::{PrimVal, self}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -61,70 +61,31 @@ impl Pointer { } pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { - Pointer::new(self.alloc_id, (self.offset.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64) + Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) } pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - // FIXME: is it possible to over/underflow here? - if i < 0 { - // trickery to ensure that i64::min_value() works fine - // this formula only works for true negative values, it panics for zero! - let n = u64::max_value() - (i as u64) + 1; - if let Some(res) = self.offset.checked_sub(n) { - Ok(Pointer::new(self.alloc_id, res)) - } else { - Err(EvalError::OverflowingMath) - } - } else { - self.offset(i as u64, layout) - } + Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) } pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - if let Some(res) = self.offset.checked_add(i) { - if res as u128 >= (1u128 << layout.pointer_size.bits()) { - Err(EvalError::OverflowingMath) - } else { - Ok(Pointer::new(self.alloc_id, res)) - } - } else { - Err(EvalError::OverflowingMath) - } + Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) } pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } - pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, u64> { - match self.alloc_id { - NEVER_ALLOC_ID => Ok(self.offset), - _ => Err(EvalError::ReadPointerAsBytes), - } - } - - pub fn from_int(i: u64) -> Self { - Pointer::new(NEVER_ALLOC_ID, i) - } - pub fn zst_ptr() -> Self { Pointer::new(ZST_ALLOC_ID, 0) } - - pub fn never_ptr() -> Self { - Pointer::new(NEVER_ALLOC_ID, 0) - } - - pub fn is_null_ptr(&self) -> bool { - return *self == Pointer::from_int(0) - } } pub type TlsKey = usize; #[derive(Copy, Clone, Debug)] pub struct TlsEntry<'tcx> { - data: Pointer, // Will eventually become a map from thread IDs to pointers, if we ever support more than one thread. + data: PrimVal, // Will eventually become a map from thread IDs to `PrimVal`s, if we ever support more than one thread. dtor: Option>, } @@ -187,7 +148,6 @@ pub struct Memory<'a, 'tcx> { } const ZST_ALLOC_ID: AllocId = AllocId(0); -const NEVER_ALLOC_ID: AllocId = AllocId(1); impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self { @@ -395,7 +355,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn create_tls_key(&mut self, dtor: Option>) -> TlsKey { let new_key = self.next_thread_local; self.next_thread_local += 1; - self.thread_local.insert(new_key, TlsEntry { data: Pointer::from_int(0), dtor }); + self.thread_local.insert(new_key, TlsEntry { data: PrimVal::Bytes(0), dtor }); trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); return new_key; } @@ -410,7 +370,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> { + pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, PrimVal> { return match self.thread_local.get(&key) { Some(&TlsEntry { data, .. }) => { trace!("TLS key {} loaded: {:?}", key, data); @@ -420,7 +380,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> { + pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: PrimVal) -> EvalResult<'tcx> { return match self.thread_local.get_mut(&key) { Some(&mut TlsEntry { ref mut data, .. }) => { trace!("TLS key {} stored: {:?}", key, new_data); @@ -431,14 +391,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - // Returns a dtor and its argument, if one is supposed to run - pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, Pointer)> { + /// Returns a dtor and its argument, if one is supposed to run + pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, PrimVal)> { for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() { - if !data.is_null_ptr() { + if *data != PrimVal::Bytes(0) { if let Some(dtor) = dtor { - let old_data = *data; - *data = Pointer::from_int(0); - return Some((dtor, old_data)); + let ret = Some((dtor, *data)); + *data = PrimVal::Bytes(0); + return ret; } } } @@ -467,7 +427,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -482,7 +442,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }, None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), + None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -513,7 +473,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let mut allocs_seen = HashSet::new(); while let Some(id) = allocs_to_print.pop_front() { - if id == ZST_ALLOC_ID || id == NEVER_ALLOC_ID { continue; } + if id == ZST_ALLOC_ID { continue; } let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); let prefix_len = msg.len(); let mut relocations = vec![]; @@ -563,7 +523,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap(); let target = match target_id { ZST_ALLOC_ID => String::from("zst"), - NEVER_ALLOC_ID => String::from("int ptr"), _ => format!("({})", target_id), }; // this `as usize` is fine, since we can't print more chars than `usize::MAX` @@ -647,7 +606,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as being the entry point to a static (see `static_alloc` field) pub fn mark_static(&mut self, alloc_id: AllocId) { trace!("mark_static: {:?}", alloc_id); - if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { + if alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); } } @@ -677,7 +636,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // mark recursively mem::replace(relocations, Default::default()) }, - None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()), + None if alloc_id == ZST_ALLOC_ID => return Ok(()), None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), _ => return Ok(()), }; @@ -749,9 +708,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { + pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, PrimVal> { let size = self.pointer_size(); - self.check_defined(ptr, size)?; + if self.check_defined(ptr, size).is_err() { + return Ok(PrimVal::Undef); + } let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size, size)?; let offset = read_target_uint(endianess, bytes).unwrap(); @@ -759,16 +720,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = offset as u64; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)), - None => Ok(Pointer::from_int(offset)), + Some(&alloc_id) => Ok(PrimVal::Ptr(Pointer::new(alloc_id, offset))), + None => Ok(PrimVal::Bytes(offset as u128)), } } pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> { self.write_usize(dest, ptr.offset as u64)?; - if ptr.alloc_id != NEVER_ALLOC_ID { - self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); - } + self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); Ok(()) } diff --git a/src/operator.rs b/src/operator.rs index 3fe3b63407910..0303fbe1f79f6 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -4,7 +4,6 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::Lvalue; -use memory::Pointer; use value::{ PrimVal, PrimValKind, @@ -73,6 +72,7 @@ macro_rules! int_arithmetic { ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ let l = $l; let r = $r; + use value::PrimValKind::*; match $kind { I8 => overflow!($int_op, l as i8, r as i8), I16 => overflow!($int_op, l as i16, r as i16), @@ -143,18 +143,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::BinOp::*; use value::PrimValKind::*; - // FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store - // plain bytes, and leave that to PrimVal::Bytes. - fn normalize(val: PrimVal) -> PrimVal { - if let PrimVal::Ptr(ptr) = val { - if let Ok(bytes) = ptr.to_int() { - return PrimVal::Bytes(bytes as u128); - } - } - val - } - let (left, right) = (normalize(left), normalize(right)); - let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; @@ -162,29 +150,43 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if bin_op == Offset { if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) { let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?; - return Ok((PrimVal::Ptr(ptr), false)); + let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; + return Ok((ptr, false)); } else { bug!("Offset used with wrong type"); } } - let (l, r) = match (left, right) { - (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), - - // One argument is a pointer value -- this is handled separately - (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { - return self.ptr_ops(bin_op, left_ptr, left_kind, right_ptr, right_kind); - } - (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) => { - return self.ptr_ops(bin_op, ptr, left_kind, Pointer::from_int(bytes as u64), right_kind); + // unrelated pointer ops + let op: Option bool> = match bin_op { + Eq => Some(PrimVal::eq), + Ne => Some(PrimVal::ne), + _ => None, + }; + if let Some(op) = op { + // only floats can't be binary compared + let ok = left_kind != F32 && left_kind != F64; + let ok = ok && right_kind != F32 && right_kind != F64; + if ok { + return Ok((PrimVal::from_bool(op(&left, &right)), false)); } - (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { - return self.ptr_ops(bin_op, Pointer::from_int(bytes as u64), left_kind, ptr, right_kind); + } + + + if let (Ok(left), Ok(right)) = (left.to_ptr(), right.to_ptr()) { + if left.alloc_id == right.alloc_id { + return self.ptr_ops( + bin_op, + left.offset, + right.offset, + ); + } else { + return Err(EvalError::InvalidPointerMath); } + } - (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), - }; + let l = left.to_bytes()?; + let r = right.to_bytes()?; // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { @@ -260,41 +262,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn ptr_ops( &self, bin_op: mir::BinOp, - left: Pointer, - left_kind: PrimValKind, - right: Pointer, - right_kind: PrimValKind, + left: u64, + right: u64, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use value::PrimValKind::*; - - if left_kind != right_kind || !(left_kind.is_ptr() || left_kind == PrimValKind::from_uint_size(self.memory.pointer_size())) { - let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); - return Err(EvalError::Unimplemented(msg)); - } let val = match bin_op { Eq => PrimVal::from_bool(left == right), Ne => PrimVal::from_bool(left != right), Lt | Le | Gt | Ge => { - if left.alloc_id == right.alloc_id { - PrimVal::from_bool(match bin_op { - Lt => left.offset < right.offset, - Le => left.offset <= right.offset, - Gt => left.offset > right.offset, - Ge => left.offset >= right.offset, - _ => bug!("We already established it has to be a comparison operator."), - }) - } else { - return Err(EvalError::InvalidPointerMath); - } + PrimVal::from_bool(match bin_op { + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!("We already established it has to be a comparison operator."), + }) } Sub => { - if left.alloc_id == right.alloc_id { - return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset); - } else { - return Err(EvalError::InvalidPointerMath); - } + let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); + return int_arithmetic!(usize, overflowing_sub, left, right); } _ => { return Err(EvalError::ReadPointerAsBytes); diff --git a/src/step.rs b/src/step.rs index eaf3069a907e0..0fd3f0b3a43ed 100644 --- a/src/step.rs +++ b/src/step.rs @@ -14,7 +14,6 @@ use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; -use memory::Pointer; use syntax::codemap::Span; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -41,13 +40,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance, mir.span, mir, - Lvalue::from_ptr(Pointer::zst_ptr()), + Some(Lvalue::zst()), StackPopCleanup::None, )?; let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?; + self.write_primval(dest, ptr, ty)?; return Ok(true); } return Ok(false); @@ -192,7 +191,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } if self.ecx.tcx.has_attr(def_id, "linkage") { trace!("Initializing an extern global with NULL"); - self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Ptr(Pointer::from_int(0))), !shared)); + self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), !shared)); return; } self.try(|this| { @@ -210,7 +209,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { instance, span, mir, - Lvalue::Global(cid), + Some(Lvalue::Global(cid)), cleanup, ) }); @@ -253,7 +252,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { this.ecx.push_stack_frame(this.instance, constant.span, mir, - Lvalue::Global(cid), + Some(Lvalue::Global(cid)), StackPopCleanup::MarkStatic(false), ) }); diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 776061954251c..463d9b3388b53 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -5,7 +5,6 @@ use syntax::codemap::Span; use error::EvalResult; use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Lvalue, LvalueExtra}; -use memory::Pointer; use value::PrimVal; use value::Value; @@ -13,9 +12,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)), - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(PrimVal::Ptr(ptr)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(ptr, PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(ptr), _ => bug!("force_allocation broken"), }; self.drop(val, instance, ty, span) @@ -50,7 +49,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance, span, mir, - Lvalue::from_ptr(Pointer::zst_ptr()), + Some(Lvalue::zst()), StackPopCleanup::None, )?; diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index ce3216f00153a..a69380f1b18da 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; + self.write_primval(dest, result_ptr, dest_ty)?; } "assume" => { @@ -60,7 +60,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); - let dest = arg_vals[0].read_ptr(&self.memory)?; + let dest = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -79,7 +79,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_xchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -93,7 +93,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_cxchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; let old = self.read_value(ptr, ty)?; @@ -103,7 +103,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), }; let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?; - let dest = self.force_allocation(dest)?.to_ptr(); + let dest = self.force_allocation(dest)?.to_ptr()?; self.write_pair_to_ptr(old, val, dest, dest_ty)?; self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; } @@ -114,7 +114,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -143,11 +143,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: check whether overlapping occurs let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); - let elem_align = self.type_align(elem_ty)?; - let src = arg_vals[0].read_ptr(&self.memory)?; - let dest = arg_vals[1].read_ptr(&self.memory)?; - let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - self.memory.copy(src, dest, count * elem_size, elem_align)?; + if elem_size != 0 { + let elem_align = self.type_align(elem_ty)?; + let src = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let dest = arg_vals[1].read_ptr(&self.memory)?.to_ptr()?; + let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; + self.memory.copy(src, dest, count * elem_size, elem_align)?; + } } "ctpop" | @@ -163,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = arg_vals[0].read_ptr(&self.memory)?; + let adt_ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } @@ -259,7 +261,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match dest { Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?, - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr.to_ptr()?, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, } @@ -282,7 +284,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } @@ -297,7 +299,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?; + self.write_primval(dest, result_ptr, dest_ty)?; } "overflowing_sub" => { @@ -388,7 +390,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest_align = self.type_align(dest_ty)?; let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); if dest_align < src_align { - let ptr = self.force_allocation(dest)?.to_ptr(); + let ptr = self.force_allocation(dest)?.to_ptr()?; self.memory.mark_packed(ptr, size); self.write_value_to_ptr(arg_vals[0], ptr, dest_ty)?; } else { @@ -410,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => - self.memory.mark_definedness(ptr, size, false)?, + self.memory.mark_definedness(ptr.to_ptr()?, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, uninit)?, } @@ -422,7 +424,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { self.memory.check_align(ptr, ty_align, size * count)?; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index afc3bf1c37feb..b9182a532e40f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -30,7 +30,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::TerminatorKind::*; match terminator.kind { Return => { - self.dump_local(self.frame().return_lvalue); + self.dump_local(self.frame().return_lvalue.expect("diverging function returned")); self.pop_stack_frame()? } @@ -388,7 +388,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; - let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + let instance = self.memory.get_fn(fn_ptr.to_ptr()?.alloc_id)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); let ty = self.get_field_ty(ty, 0)?; @@ -430,12 +430,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(other) => return Err(other), }; let (return_lvalue, return_to_block) = match destination { - Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), - None => { - // FIXME(solson) - let lvalue = Lvalue::from_ptr(Pointer::never_ptr()); - (lvalue, StackPopCleanup::None) - } + Some((lvalue, block)) => (Some(lvalue), StackPopCleanup::Goto(block)), + None => (None, StackPopCleanup::None), }; self.push_stack_frame( @@ -579,7 +575,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "__rust_deallocate" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; // FIXME: insert sanity check for size and align? let _old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let _align = self.value_to_primval(args[2], usize)?.to_u64()?; @@ -587,7 +583,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, "__rust_reallocate" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let size = self.value_to_primval(args[2], usize)?.to_u64()?; let align = self.value_to_primval(args[3], usize)?.to_u64()?; let new_ptr = self.memory.reallocate(ptr, size, align)?; @@ -598,7 +594,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - let f = args[0].read_ptr(&self.memory)?; + let f = args[0].read_ptr(&self.memory)?.to_ptr()?; let data = args[1].read_ptr(&self.memory)?; let f_instance = self.memory.get_fn(f.alloc_id)?; self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -610,13 +606,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { f_instance, mir.span, mir, - Lvalue::from_ptr(Pointer::zst_ptr()), + Some(Lvalue::zst()), StackPopCleanup::Goto(dest_block), )?; let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_value(Value::ByVal(PrimVal::Ptr(data)), arg_dest, u8_ptr_ty)?; + self.write_primval(arg_dest, data, u8_ptr_ty)?; // We ourselbes return 0 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -630,8 +626,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memcmp" => { - let left = args[0].read_ptr(&self.memory)?; - let right = args[1].read_ptr(&self.memory)?; + let left = args[0].read_ptr(&self.memory)?.to_ptr()?; + let right = args[1].read_ptr(&self.memory)?.to_ptr()?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; let result = { @@ -650,7 +646,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memrchr" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { @@ -662,7 +658,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memchr" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { @@ -675,7 +671,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "getenv" => { { - let name_ptr = args[0].read_ptr(&self.memory)?; + let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let name = self.memory.read_c_str(name_ptr)?; info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); } @@ -684,7 +680,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write" => { let fd = self.value_to_primval(args[0], usize)?.to_u64()?; - let buf = args[1].read_ptr(&self.memory)?; + let buf = args[1].read_ptr(&self.memory)?.to_ptr()?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); let result = if fd == 1 || fd == 2 { // stdout/stderr @@ -718,7 +714,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "mmap" => { // This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value let addr = args[0].read_ptr(&self.memory)?; - self.write_primval(dest, PrimVal::Ptr(addr), dest_ty)?; + self.write_primval(dest, addr, dest_ty)?; } // Hook pthread calls that go to the thread-local storage memory subsystem @@ -726,8 +722,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let key_ptr = args[0].read_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor_ptr = args[1].read_ptr(&self.memory)?; - let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) }; + let dtor = match args[1].read_ptr(&self.memory)? { + PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr.alloc_id)?), + PrimVal::Bytes(0) => None, + PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), + PrimVal::Undef => return Err(EvalError::ReadUndefBytes), + }; // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t. let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference) @@ -743,7 +743,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::OutOfTls); } // TODO: Does this need checking for alignment? - self.memory.write_uint(key_ptr, key, key_size.bytes())?; + self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?; // Return success (0) self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -759,7 +759,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; let ptr = self.memory.load_tls(key)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + self.write_primval(dest, ptr, dest_ty)?; } "pthread_setspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t diff --git a/src/traits.rs b/src/traits.rs index 9c4e60a299f4a..3862e8c631a6e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,6 +2,7 @@ use rustc::traits::{self, Reveal}; use eval_context::EvalContext; use memory::Pointer; +use value::{Value, PrimVal}; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; @@ -9,7 +10,7 @@ use rustc::ty::{self, Ty}; use syntax::codemap::DUMMY_SP; use syntax::ast; -use error::EvalResult; +use error::{EvalResult, EvalError}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -73,16 +74,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { - let drop_fn = self.memory.read_ptr(vtable)?; - - // just a sanity check - assert_eq!(drop_fn.offset, 0); - - // some values don't need to call a drop impl, so the value is null - if drop_fn == Pointer::from_int(0) { - Ok(None) - } else { - self.memory.get_fn(drop_fn.alloc_id).map(Some) + // we don't care about the pointee type, we just want a pointer + match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? { + // some values don't need to call a drop impl, so the value is null + Value::ByVal(PrimVal::Bytes(0)) => Ok(None), + Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn.alloc_id).map(Some), + _ => Err(EvalError::ReadBytesAsPointer), } } diff --git a/src/value.rs b/src/value.rs index 387002eee7bc2..ef11c7a8e4752 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,6 +2,7 @@ #![allow(float_cmp)] use std::mem::transmute; +use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer}; @@ -47,7 +48,7 @@ pub enum Value { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u128), @@ -74,33 +75,33 @@ pub enum PrimValKind { } impl<'a, 'tcx: 'a> Value { - pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr), - ByVal(ptr) | ByValPair(ptr, _) => ptr.to_ptr(), + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr), } } pub(super) fn expect_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> - ) -> EvalResult<'tcx, (Pointer, Pointer)> { + ) -> EvalResult<'tcx, (PrimVal, Pointer)> { use self::Value::*; match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; - Ok((ptr, vtable)) + Ok((ptr, vtable.to_ptr()?)) } - ByValPair(ptr, vtable) => Ok((ptr.to_ptr()?, vtable.to_ptr()?)), + ByValPair(ptr, vtable) => Ok((ptr, vtable.to_ptr()?)), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, u64)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -111,7 +112,7 @@ impl<'a, 'tcx: 'a> Value { ByValPair(ptr, val) => { let len = val.to_u128()?; assert_eq!(len as u64 as u128, len); - Ok((ptr.to_ptr()?, len as u64)) + Ok((ptr, len as u64)) }, _ => unimplemented!(), } @@ -146,14 +147,14 @@ impl<'tcx> PrimVal { pub fn to_bytes(self) -> EvalResult<'tcx, u128> { match self { PrimVal::Bytes(b) => Ok(b), - PrimVal::Ptr(p) => p.to_int().map(|b| b as u128), + PrimVal::Ptr(_) => Err(EvalError::ReadPointerAsBytes), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { match self { - PrimVal::Bytes(b) => Ok(Pointer::from_int(b as u64)), + PrimVal::Bytes(_) => Err(EvalError::ReadBytesAsPointer), PrimVal::Ptr(p) => Ok(p), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } @@ -203,6 +204,67 @@ impl<'tcx> PrimVal { _ => Err(EvalError::InvalidBool), } } + + pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + match self { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128)) + }, + PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(PrimVal::Ptr), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + + pub fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + match self { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(PrimVal::Bytes(offset(b as u64, i, layout)? as u128)) + }, + PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(PrimVal::Ptr), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + + pub fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + match self { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128)) + }, + PrimVal::Ptr(ptr) => Ok(PrimVal::Ptr(ptr.wrapping_signed_offset(i, layout))), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } +} + +pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { + // FIXME: is it possible to over/underflow here? + if i < 0 { + // trickery to ensure that i64::min_value() works fine + // this formula only works for true negative values, it panics for zero! + let n = u64::max_value() - (i as u64) + 1; + val.checked_sub(n).ok_or(EvalError::OverflowingMath) + } else { + offset(val, i as u64, layout) + } +} + +pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { + if let Some(res) = val.checked_add(i) { + if res as u128 >= (1u128 << layout.pointer_size.bits()) { + Err(EvalError::OverflowingMath) + } else { + Ok(res) + } + } else { + Err(EvalError::OverflowingMath) + } +} + +pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 { + (val.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64 } impl PrimValKind { diff --git a/tests/compile-fail/cast_box_int_to_fn_ptr.rs b/tests/compile-fail/cast_box_int_to_fn_ptr.rs index 030bed6a35298..96469814be29b 100644 --- a/tests/compile-fail/cast_box_int_to_fn_ptr.rs +++ b/tests/compile-fail/cast_box_int_to_fn_ptr.rs @@ -4,5 +4,5 @@ fn main() { std::mem::transmute::<&usize, &fn(i32)>(&b) }; - (*g)(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer + (*g)(42) //~ ERROR a memory access tried to interpret some bytes as a pointer } diff --git a/tests/compile-fail/cast_int_to_fn_ptr.rs b/tests/compile-fail/cast_int_to_fn_ptr.rs index dc39f7dda1b63..28d56a2cb6271 100644 --- a/tests/compile-fail/cast_int_to_fn_ptr.rs +++ b/tests/compile-fail/cast_int_to_fn_ptr.rs @@ -3,5 +3,5 @@ fn main() { std::mem::transmute::(42) }; - g(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer + g(42) //~ ERROR a memory access tried to interpret some bytes as a pointer } diff --git a/tests/compile-fail/null_pointer_deref.rs b/tests/compile-fail/null_pointer_deref.rs index fcf34ed44c93a..20b93aab1607d 100644 --- a/tests/compile-fail/null_pointer_deref.rs +++ b/tests/compile-fail/null_pointer_deref.rs @@ -1,4 +1,4 @@ fn main() { - let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: tried to access memory through an invalid pointer + let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: a memory access tried to interpret some bytes as a pointer panic!("this should never print: {}", x); } diff --git a/tests/compile-fail/wild_pointer_deref.rs b/tests/compile-fail/wild_pointer_deref.rs index 937546fdc350a..373e308e1c02d 100644 --- a/tests/compile-fail/wild_pointer_deref.rs +++ b/tests/compile-fail/wild_pointer_deref.rs @@ -1,5 +1,5 @@ fn main() { let p = 42 as *const i32; - let x = unsafe { *p }; //~ ERROR: tried to access memory through an invalid pointer + let x = unsafe { *p }; //~ ERROR: a memory access tried to interpret some bytes as a pointer panic!("this should never print: {}", x); } From 43afa20dc7d91421e5e20ab92c75cc139bfc5795 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 22:25:21 +0200 Subject: [PATCH 0983/1096] Add pthread docs --- src/memory.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 9e33a162b5923..4fcdb07fa423b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -392,6 +392,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } /// Returns a dtor and its argument, if one is supposed to run + /// + /// An optional destructor function may be associated with each key value. + /// At thread exit, if a key value has a non-NULL destructor pointer, + /// and the thread has a non-NULL value associated with that key, + /// the value of the key is set to NULL, and then the function pointed + /// to is called with the previously associated value as its sole argument. + /// The order of destructor calls is unspecified if more than one destructor + /// exists for a thread when it exits. + /// + /// If, after all the destructors have been called for all non-NULL values + /// with associated destructors, there are still some non-NULL values with + /// associated destructors, then the process is repeated. + /// If, after at least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor + /// calls for outstanding non-NULL values, there are still some non-NULL values + /// with associated destructors, implementations may stop calling destructors, + /// or they may continue calling destructors until no non-NULL values with + /// associated destructors exist, even though this might result in an infinite loop. + pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, PrimVal)> { for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() { if *data != PrimVal::Bytes(0) { From a2baeb516cd24c78bdedb8a6ddcbfb9d82a79b50 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 23:32:53 +0200 Subject: [PATCH 0984/1096] Run the tls destructors in the correct order --- src/eval_context.rs | 34 +++++++++++++++++++++++++++++----- src/memory.rs | 18 +++++++++++------- src/step.rs | 17 ----------------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 55a031b310a58..69469c30e4815 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -17,7 +17,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; -use memory::{Memory, Pointer}; +use memory::{Memory, Pointer, TlsKey}; use operator; use value::{PrimVal, PrimValKind, Value}; @@ -100,6 +100,11 @@ pub enum StackPopCleanup { /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), + /// After finishing a tls destructor, find the next one instead of starting from the beginning + /// and thus just rerunning the first one until its `data` argument is null + /// + /// The index is the current tls destructor's index + Tls(Option), /// The main function and diverging functions have nowhere to return to None, } @@ -351,6 +356,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, + StackPopCleanup::Tls(key) => { + // either fetch the next dtor or start new from the beginning, if any are left with a non-null data + if let Some((instance, ptr, key)) = self.memory.fetch_tls_dtor(key).or_else(|| self.memory.fetch_tls_dtor(None)) { + trace!("Running TLS dtor {:?} on {:?}", instance, ptr); + // TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs + let mir = self.load_mir(instance.def)?; + self.push_stack_frame( + instance, + mir.span, + mir, + Lvalue::zst(), + StackPopCleanup::Tls(Some(key)), + )?; + let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + self.write_primval(dest, ptr, ty)?; + } + } } // deallocate all locals that are backed by an allocation for local in frame.locals { @@ -1630,8 +1654,8 @@ pub fn eval_main<'a, 'tcx: 'a>( start_instance, start_mir.span, start_mir, - Some(Lvalue::from_ptr(ret_ptr)), - StackPopCleanup::None, + Lvalue::from_ptr(ret_ptr), + StackPopCleanup::Tls(None), )?; let mut args = ecx.frame().mir.args_iter(); @@ -1657,8 +1681,8 @@ pub fn eval_main<'a, 'tcx: 'a>( main_instance, main_mir.span, main_mir, - Some(Lvalue::zst()), - StackPopCleanup::None, + Lvalue::zst(), + StackPopCleanup::Tls(None), )?; } diff --git a/src/memory.rs b/src/memory.rs index 4fcdb07fa423b..ea1d8695d5988 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -141,7 +141,7 @@ pub struct Memory<'a, 'tcx> { literal_alloc_cache: HashMap, AllocId>, /// pthreads-style thread-local storage. - thread_local: HashMap>, + thread_local: BTreeMap>, /// The Key to use for the next thread-local allocation. next_thread_local: TlsKey, @@ -162,7 +162,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { packed: BTreeSet::new(), static_alloc: HashSet::new(), literal_alloc_cache: HashMap::new(), - thread_local: HashMap::new(), + thread_local: BTreeMap::new(), next_thread_local: 0, } } @@ -391,7 +391,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - /// Returns a dtor and its argument, if one is supposed to run + /// Returns a dtor, its argument and its index, if one is supposed to run /// /// An optional destructor function may be associated with each key value. /// At thread exit, if a key value has a non-NULL destructor pointer, @@ -409,12 +409,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// with associated destructors, implementations may stop calling destructors, /// or they may continue calling destructors until no non-NULL values with /// associated destructors exist, even though this might result in an infinite loop. - - pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, PrimVal)> { - for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() { + pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> Option<(ty::Instance<'tcx>, PrimVal, TlsKey)> { + use std::collections::Bound::*; + let start = match key { + Some(key) => Excluded(key), + None => Unbounded, + }; + for (&key, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.range_mut((start, Unbounded)) { if *data != PrimVal::Bytes(0) { if let Some(dtor) = dtor { - let ret = Some((dtor, *data)); + let ret = Some((dtor, *data, key)); *data = PrimVal::Bytes(0); return ret; } diff --git a/src/step.rs b/src/step.rs index 0fd3f0b3a43ed..a1a9409e2e6a7 100644 --- a/src/step.rs +++ b/src/step.rs @@ -32,23 +32,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.clear_packed(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { - if let Some((instance, ptr)) = self.memory.fetch_tls_dtor() { - trace!("Running TLS dtor {:?} on {:?}", instance, ptr); - // TODO: Potientially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs - let mir = self.load_mir(instance.def)?; - self.push_stack_frame( - instance, - mir.span, - mir, - Some(Lvalue::zst()), - StackPopCleanup::None, - )?; - let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_primval(dest, ptr, ty)?; - return Ok(true); - } return Ok(false); } From 75fddee700c67ccef35e9496451fbe15e29084d8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 13:29:04 +0200 Subject: [PATCH 0985/1096] Simplify the return lvalue --- src/eval_context.rs | 7 +++---- src/lvalue.rs | 10 +++++++++- src/step.rs | 4 ++-- src/terminator/drop.rs | 2 +- src/terminator/mod.rs | 8 ++++---- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 69469c30e4815..185f4e1709b22 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -66,8 +66,7 @@ pub struct Frame<'tcx> { pub return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to. - /// None if the function is a diverging function - pub return_lvalue: Option>, + pub return_lvalue: Lvalue<'tcx>, /// The list of locals for this stack frame, stored in order as /// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option`s. @@ -272,7 +271,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance: ty::Instance<'tcx>, span: codemap::Span, mir: &'tcx mir::Mir<'tcx>, - return_lvalue: Option>, + return_lvalue: Lvalue<'tcx>, return_to_block: StackPopCleanup, ) -> EvalResult<'tcx> { ::log_settings::settings().indentation += 1; @@ -329,7 +328,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ::log_settings::settings().indentation -= 1; let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none"); match frame.return_to_block { - StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue.expect("diverging static") { + StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue { let global_value = self.globals.get_mut(&id) .expect("global should have been cached (static)"); match global_value.value { diff --git a/src/lvalue.rs b/src/lvalue.rs index b61c18274aeb0..0205472254ce6 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -64,6 +64,14 @@ pub struct Global<'tcx> { } impl<'tcx> Lvalue<'tcx> { + /// Produces an Lvalue that will error if attempted to be read from + pub fn undef() -> Self { + Lvalue::Ptr { + ptr: PrimVal::Undef, + extra: LvalueExtra::None, + } + } + pub fn zst() -> Self { Self::from_ptr(Pointer::zst_ptr()) } @@ -148,7 +156,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { - Local(mir::RETURN_POINTER) => self.frame().return_lvalue.expect("diverging function returned"), + Local(mir::RETURN_POINTER) => self.frame().return_lvalue, Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(ref static_) => { diff --git a/src/step.rs b/src/step.rs index a1a9409e2e6a7..48b9fecd3d39e 100644 --- a/src/step.rs +++ b/src/step.rs @@ -192,7 +192,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { instance, span, mir, - Some(Lvalue::Global(cid)), + Lvalue::Global(cid), cleanup, ) }); @@ -235,7 +235,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { this.ecx.push_stack_frame(this.instance, constant.span, mir, - Some(Lvalue::Global(cid)), + Lvalue::Global(cid), StackPopCleanup::MarkStatic(false), ) }); diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 463d9b3388b53..663d05254e5cc 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -49,7 +49,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance, span, mir, - Some(Lvalue::zst()), + Lvalue::zst(), StackPopCleanup::None, )?; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index b9182a532e40f..6ec607295dc72 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -30,7 +30,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::TerminatorKind::*; match terminator.kind { Return => { - self.dump_local(self.frame().return_lvalue.expect("diverging function returned")); + self.dump_local(self.frame().return_lvalue); self.pop_stack_frame()? } @@ -430,8 +430,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(other) => return Err(other), }; let (return_lvalue, return_to_block) = match destination { - Some((lvalue, block)) => (Some(lvalue), StackPopCleanup::Goto(block)), - None => (None, StackPopCleanup::None), + Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), + None => (Lvalue::undef(), StackPopCleanup::None), }; self.push_stack_frame( @@ -606,7 +606,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { f_instance, mir.span, mir, - Some(Lvalue::zst()), + Lvalue::zst(), StackPopCleanup::Goto(dest_block), )?; From a6734cd89030b352f7cfcd765e451af5f1961985 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 16:54:31 +0200 Subject: [PATCH 0986/1096] Fix unions --- src/lvalue.rs | 10 ++-- tests/run-pass/union-overwrite.rs | 81 +++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/union-overwrite.rs diff --git a/src/lvalue.rs b/src/lvalue.rs index 0205472254ce6..f344a4e3b2d1a 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -182,7 +182,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base_layout = self.type_layout(base_ty)?; - use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { Univariant { ref variant, .. } => { @@ -255,8 +254,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, Value::ByVal(_) => { - assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); - return Ok(base); + if self.get_field_count(base_ty)? == 1 { + assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + } + // this branch is taken when a union creates a large ByVal which is then + // accessed as a struct with multiple small fields + (PrimVal::Ptr(self.force_allocation(base)?.to_ptr()?), LvalueExtra::None) }, Value::ByValPair(_, _) => { let field_count = self.get_field_count(base_ty)?; diff --git a/tests/run-pass/union-overwrite.rs b/tests/run-pass/union-overwrite.rs new file mode 100644 index 0000000000000..df2ff6e51a593 --- /dev/null +++ b/tests/run-pass/union-overwrite.rs @@ -0,0 +1,81 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] +#![allow(unions_with_drop_fields)] + +#[repr(C)] +struct Pair(T, U); +#[repr(C)] +struct Triple(T, T, T); + +#[repr(C)] +union U { + a: Pair, + b: B, +} + +#[repr(C)] +union W { + a: A, + b: B, +} + +#[cfg(target_endian = "little")] +unsafe fn check() { + let mut u = U:: { b: 0xDE_DE }; + u.a.0 = 0xBE; + assert_eq!(u.b, 0xDE_BE); + + let mut u = U:: { b: 0xDEAD_DEAD }; + u.a.0 = 0xBEEF; + assert_eq!(u.b, 0xDEAD_BEEF); + + let mut u = U:: { b: 0xDEADBEEF_DEADBEEF }; + u.a.0 = 0xBAADF00D; + assert_eq!(u.b, 0xDEADBEEF_BAADF00D); + + let mut w = W::, u8>, u32> { b: 0xDEAD_DEAD }; + w.a.0 = Triple(0, 0, 0); + assert_eq!(w.b, 0xDE00_0000); + + let mut w = W::>, u32> { b: 0xDEAD_DEAD }; + w.a.1 = Triple(0, 0, 0); + assert_eq!(w.b, 0x0000_00AD); +} + +#[cfg(target_endian = "big")] +unsafe fn check() { + let mut u = U:: { b: 0xDE_DE }; + u.a.0 = 0xBE; + assert_eq!(u.b, 0xBE_DE); + + let mut u = U:: { b: 0xDEAD_DEAD }; + u.a.0 = 0xBEEF; + assert_eq!(u.b, 0xBEEF_DEAD); + + let mut u = U:: { b: 0xDEADBEEF_DEADBEEF }; + u.a.0 = 0xBAADF00D; + assert_eq!(u.b, 0xBAADF00D_DEADBEEF); + + let mut w = W::, u8>, u32> { b: 0xDEAD_DEAD }; + w.a.0 = Triple(0, 0, 0); + assert_eq!(w.b, 0x0000_00AD); + + let mut w = W::>, u32> { b: 0xDEAD_DEAD }; + w.a.1 = Triple(0, 0, 0); + assert_eq!(w.b, 0xDE00_0000); +} + +fn main() { + unsafe { + check(); + } +} From ea6f6079ca6958c1b74f8255b55c2d76e9c121d8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Jun 2017 14:26:50 +0200 Subject: [PATCH 0987/1096] Use PrimVal instead of Pointer where applicable --- src/eval_context.rs | 30 +++++----- src/lvalue.rs | 11 ++-- src/memory.rs | 15 +++-- src/terminator/intrinsic.rs | 14 ++--- tests/run-pass/slice-of-zero-size-elements.rs | 58 +++++++++++++++++++ tests/run-pass/zero-sized-binary-heap-push.rs | 28 +++++++++ 6 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 tests/run-pass/slice-of-zero-size-elements.rs create mode 100644 tests/run-pass/zero-sized-binary-heap-push.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 185f4e1709b22..30a4be7b4b866 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -637,7 +637,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr()?; + let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?); for i in 0..length { let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; @@ -893,6 +893,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; + if pointee_size == 0 { + // rustc relies on offsetting pointers to zsts to be a nop + return Ok(ptr); + } return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; self.memory.check_bounds(ptr.to_ptr()?, false)?; @@ -943,7 +947,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } - fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { + fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); let align = self.type_align(ty)?; self.memory.copy(src, dest, size, align)?; @@ -969,7 +973,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr)); // it stays live - self.write_value_to_ptr(val, ptr, ty)?; + self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; let lval = Lvalue::from_ptr(ptr); if let Some((field, field_ty)) = field { self.lvalue_field(lval, field, ty, field_ty)? @@ -987,7 +991,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); - self.write_value_to_ptr(global_val.value, ptr, global_val.ty)?; + self.write_value_to_ptr(global_val.value, PrimVal::Ptr(ptr), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; @@ -1059,7 +1063,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(src_val, ptr.to_ptr()?, dest_ty) + self.write_value_to_ptr(src_val, ptr, dest_ty) } Lvalue::Local { frame, local, field } => { @@ -1090,7 +1094,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. - self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; + self.write_value_to_ptr(src_val, PrimVal::Ptr(dest_ptr), dest_ty)?; } else if let Value::ByRef(src_ptr) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it @@ -1108,7 +1112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { write_dest(self, src_val)?; } else { let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, dest_ptr, dest_ty)?; + self.copy(PrimVal::Ptr(src_ptr), PrimVal::Ptr(dest_ptr), dest_ty)?; write_dest(self, Value::ByRef(dest_ptr))?; } @@ -1123,16 +1127,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn write_value_to_ptr( &mut self, value: Value, - dest: Pointer, + dest: PrimVal, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { - Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), + Value::ByRef(ptr) => self.copy(PrimVal::Ptr(ptr), dest, dest_ty), Value::ByVal(primval) => { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(dest, primval, size) } - Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest, dest_ty), + Value::ByValPair(a, b) => self.write_pair_to_ptr(a, b, dest.to_ptr()?, dest_ty), } } @@ -1153,8 +1157,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?, a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?, b, field_1_size)?; + self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?; + self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?; Ok(()) } @@ -1457,7 +1461,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { - self.copy(src_f_ptr, dst_f_ptr, src_fty)?; + self.copy(PrimVal::Ptr(src_f_ptr), PrimVal::Ptr(dst_f_ptr), src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } diff --git a/src/lvalue.rs b/src/lvalue.rs index f344a4e3b2d1a..f409f3734848a 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -66,10 +66,11 @@ pub struct Global<'tcx> { impl<'tcx> Lvalue<'tcx> { /// Produces an Lvalue that will error if attempted to be read from pub fn undef() -> Self { - Lvalue::Ptr { - ptr: PrimVal::Undef, - extra: LvalueExtra::None, - } + Self::from_primval_ptr(PrimVal::Undef) + } + + fn from_primval_ptr(ptr: PrimVal) -> Self { + Lvalue::Ptr { ptr, extra: LvalueExtra::None } } pub fn zst() -> Self { @@ -77,7 +78,7 @@ impl<'tcx> Lvalue<'tcx> { } pub fn from_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra: LvalueExtra::None } + Self::from_primval_ptr(PrimVal::Ptr(ptr)) } pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) { diff --git a/src/memory.rs b/src/memory.rs index ea1d8695d5988..8aab55e02394c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -618,7 +618,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } self.clear_relocations(ptr, size)?; - self.mark_definedness(ptr, size, true)?; + self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) } } @@ -671,10 +671,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64) -> EvalResult<'tcx> { + pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64) -> EvalResult<'tcx> { if size == 0 { return Ok(()); } + let src = src.to_ptr()?; + let dest = dest.to_ptr()?; self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked(src, size, align)?.as_ptr(); @@ -755,14 +757,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_primval( &mut self, - dest: Pointer, + dest: PrimVal, val: PrimVal, size: u64, ) -> EvalResult<'tcx> { match val { PrimVal::Ptr(ptr) => { assert_eq!(size, self.pointer_size()); - self.write_ptr(dest, ptr) + self.write_ptr(dest.to_ptr()?, ptr) } PrimVal::Bytes(bytes) => { @@ -776,7 +778,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 16 => !0, _ => bug!("unexpected PrimVal::Bytes size"), }; - self.write_uint(dest, bytes & mask, size) + self.write_uint(dest.to_ptr()?, bytes & mask, size) } PrimVal::Undef => self.mark_definedness(dest, size, false), @@ -962,13 +964,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness( &mut self, - ptr: Pointer, + ptr: PrimVal, size: u64, new_state: bool ) -> EvalResult<'tcx> { if size == 0 { return Ok(()) } + let ptr = ptr.to_ptr()?; let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a69380f1b18da..f6ef46ecbfb8e 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); - let dest = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let dest = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -145,8 +145,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); if elem_size != 0 { let elem_align = self.type_align(elem_ty)?; - let src = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; - let dest = arg_vals[1].read_ptr(&self.memory)?.to_ptr()?; + let src = arg_vals[0].read_ptr(&self.memory)?; + let dest = arg_vals[1].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; self.memory.copy(src, dest, count * elem_size, elem_align)?; } @@ -284,7 +284,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } @@ -392,7 +392,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if dest_align < src_align { let ptr = self.force_allocation(dest)?.to_ptr()?; self.memory.mark_packed(ptr, size); - self.write_value_to_ptr(arg_vals[0], ptr, dest_ty)?; + self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), dest_ty)?; } else { self.write_value(arg_vals[0], dest, dest_ty)?; } @@ -403,7 +403,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let uninit = |this: &mut Self, val: Value| { match val { Value::ByRef(ptr) => { - this.memory.mark_definedness(ptr, size, false)?; + this.memory.mark_definedness(PrimVal::Ptr(ptr), size, false)?; Ok(Value::ByRef(ptr)) }, _ => Ok(Value::ByVal(PrimVal::Undef)), @@ -412,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => - self.memory.mark_definedness(ptr.to_ptr()?, size, false)?, + self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, uninit)?, } diff --git a/tests/run-pass/slice-of-zero-size-elements.rs b/tests/run-pass/slice-of-zero-size-elements.rs new file mode 100644 index 0000000000000..dbe8ec9addacc --- /dev/null +++ b/tests/run-pass/slice-of-zero-size-elements.rs @@ -0,0 +1,58 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -C debug-assertions + +use std::slice; + +fn foo(v: &[T]) -> Option<&[T]> { + let mut it = v.iter(); + for _ in 0..5 { + let _ = it.next(); + } + Some(it.as_slice()) +} + +fn foo_mut(v: &mut [T]) -> Option<&mut [T]> { + let mut it = v.iter_mut(); + for _ in 0..5 { + let _ = it.next(); + } + Some(it.into_slice()) +} + +pub fn main() { + // In a slice of zero-size elements the pointer is meaningless. + // Ensure iteration still works even if the pointer is at the end of the address space. + let slice: &[()] = unsafe { slice::from_raw_parts(-5isize as *const (), 10) }; + assert_eq!(slice.len(), 10); + assert_eq!(slice.iter().count(), 10); + + // .nth() on the iterator should also behave correctly + let mut it = slice.iter(); + assert!(it.nth(5).is_some()); + assert_eq!(it.count(), 4); + + // Converting Iter to a slice should never have a null pointer + assert!(foo(slice).is_some()); + + // Test mutable iterators as well + let slice: &mut [()] = unsafe { slice::from_raw_parts_mut(-5isize as *mut (), 10) }; + assert_eq!(slice.len(), 10); + assert_eq!(slice.iter_mut().count(), 10); + + { + let mut it = slice.iter_mut(); + assert!(it.nth(5).is_some()); + assert_eq!(it.count(), 4); + } + + assert!(foo_mut(slice).is_some()) +} diff --git a/tests/run-pass/zero-sized-binary-heap-push.rs b/tests/run-pass/zero-sized-binary-heap-push.rs new file mode 100644 index 0000000000000..63a0d65f017de --- /dev/null +++ b/tests/run-pass/zero-sized-binary-heap-push.rs @@ -0,0 +1,28 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::BinaryHeap; +use std::iter::Iterator; + +fn main() { + const N: usize = 8; + + for len in 0..N { + let mut tester = BinaryHeap::with_capacity(len); + assert_eq!(tester.len(), 0); + assert!(tester.capacity() >= len); + for _ in 0..len { + tester.push(()); + } + assert_eq!(tester.len(), len); + assert_eq!(tester.iter().count(), len); + tester.clear(); + } +} From a82fe9ae0c770d540e9028596035d72b42c8d0e7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Jun 2017 16:26:53 +0200 Subject: [PATCH 0988/1096] Enable more zst writes and reads --- src/memory.rs | 21 +++-- src/terminator/mod.rs | 14 +-- tests/run-pass/loop-break-value.rs | 141 +++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 tests/run-pass/loop-break-value.rs diff --git a/src/memory.rs b/src/memory.rs index 8aab55e02394c..ac2d391f4217e 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -603,9 +603,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { - if size == 0 { - return Ok(&[]); - } + assert_ne!(size, 0); if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -614,9 +612,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { - if size == 0 { - return Ok(&mut []); - } + assert_ne!(size, 0); self.clear_relocations(ptr, size)?; self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) @@ -716,17 +712,26 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { - self.get_bytes(ptr, size, 1) + pub fn read_bytes(&self, ptr: PrimVal, size: u64) -> EvalResult<'tcx, &[u8]> { + if size == 0 { + return Ok(&[]); + } + self.get_bytes(ptr.to_ptr()?, size, 1) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> { + if src.is_empty() { + return Ok(()); + } let bytes = self.get_bytes_mut(ptr, src.len() as u64, 1)?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { + if count == 0 { + return Ok(()); + } let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 6ec607295dc72..d4e169cbcfb63 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -626,8 +626,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memcmp" => { - let left = args[0].read_ptr(&self.memory)?.to_ptr()?; - let right = args[1].read_ptr(&self.memory)?.to_ptr()?; + let left = args[0].read_ptr(&self.memory)?; + let right = args[1].read_ptr(&self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; let result = { @@ -646,24 +646,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memrchr" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].read_ptr(&self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + self.write_primval(dest, new_ptr, dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } "memchr" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].read_ptr(&self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + self.write_primval(dest, new_ptr, dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } @@ -680,7 +680,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write" => { let fd = self.value_to_primval(args[0], usize)?.to_u64()?; - let buf = args[1].read_ptr(&self.memory)?.to_ptr()?; + let buf = args[1].read_ptr(&self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); let result = if fd == 1 || fd == 2 { // stdout/stderr diff --git a/tests/run-pass/loop-break-value.rs b/tests/run-pass/loop-break-value.rs new file mode 100644 index 0000000000000..8631909a2a966 --- /dev/null +++ b/tests/run-pass/loop-break-value.rs @@ -0,0 +1,141 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![allow(unreachable_code)] + +#[allow(unused)] +fn never_returns() { + loop { + break loop {}; + } +} + +pub fn main() { + let value = 'outer: loop { + if 1 == 1 { + break 13; + } else { + let _never: ! = loop { + break loop { + break 'outer panic!(); + } + }; + } + }; + assert_eq!(value, 13); + + let x = [1, 3u32, 5]; + let y = [17]; + let z = []; + let coerced: &[_] = loop { + match 2 { + 1 => break &x, + 2 => break &y, + 3 => break &z, + _ => (), + } + }; + assert_eq!(coerced, &[17u32]); + + let trait_unified = loop { + break if true { + break Default::default() + } else { + break [13, 14] + }; + }; + assert_eq!(trait_unified, [0, 0]); + + let trait_unified_2 = loop { + if false { + break [String::from("Hello")] + } else { + break Default::default() + }; + }; + assert_eq!(trait_unified_2, [""]); + + let trait_unified_3 = loop { + break if false { + break [String::from("Hello")] + } else { + ["Yes".into()] + }; + }; + assert_eq!(trait_unified_3, ["Yes"]); + + let regular_break = loop { + if true { + break; + } else { + break break Default::default(); + } + }; + assert_eq!(regular_break, ()); + + let regular_break_2 = loop { + if true { + break Default::default(); + } else { + break; + } + }; + assert_eq!(regular_break_2, ()); + + let regular_break_3 = loop { + break if true { + Default::default() + } else { + break; + } + }; + assert_eq!(regular_break_3, ()); + + let regular_break_4 = loop { + break (); + break; + }; + assert_eq!(regular_break_4, ()); + + let regular_break_5 = loop { + break; + break (); + }; + assert_eq!(regular_break_5, ()); + + let nested_break_value = 'outer2: loop { + let _a: u32 = 'inner: loop { + if true { + break 'outer2 "hello"; + } else { + break 'inner 17; + } + }; + panic!(); + }; + assert_eq!(nested_break_value, "hello"); + + let break_from_while_cond = loop { + 'inner_loop: while break 'inner_loop { + panic!(); + } + break 123; + }; + assert_eq!(break_from_while_cond, 123); + + let break_from_while_to_outer = 'outer_loop: loop { + while break 'outer_loop 567 { + panic!("from_inner"); + } + panic!("from outer"); + }; + assert_eq!(break_from_while_to_outer, 567); +} From ef29e6a30be4600043ea3bb27a15e34bbaccac0b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 08:55:32 +0200 Subject: [PATCH 0989/1096] Add fullmir tests --- .travis.yml | 11 ++++++----- tests/compiletest.rs | 10 ++++++++-- .../loop-break-value.rs | 0 3 files changed, 14 insertions(+), 7 deletions(-) rename tests/{run-pass => run-pass-fullmir}/loop-break-value.rs (100%) diff --git a/.travis.yml b/.travis.yml index 5a2a893196dc3..5bc7f291d7629 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,11 @@ before_script: - cargo install xargo - export RUST_SYSROOT=$HOME/rust script: +- | + # get ourselves a MIR-ful libstd + cd xargo && + RUSTFLAGS='-Zalways-encode-mir' xargo build && + cd .. - | # Test plain miri cargo build && @@ -22,11 +27,7 @@ script: cargo miri test && cd .. - | - # get ourselves a MIR-ful libstd - cd xargo && - RUSTFLAGS='-Zalways-encode-mir' xargo build && - cd .. && - # and run the tests with it + # and run all tests with full mir MIRI_SYSROOT=~/.xargo/HOST cargo test notifications: email: diff --git a/tests/compiletest.rs b/tests/compiletest.rs index e6535ef0212b5..fe9cbd6479063 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -27,13 +27,17 @@ fn run_pass() { compiletest::run_tests(&config); } -fn miri_pass(path: &str, target: &str, host: &str) { +fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool) { let mut config = compiletest::default_config(); config.mode = "mir-opt".parse().expect("Invalid mode"); config.src_base = PathBuf::from(path); config.target = target.to_owned(); config.host = host.to_owned(); config.rustc_path = PathBuf::from("target/debug/miri"); + if fullmir { + let sysroot = Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST"); + config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap())); + } // don't actually execute the final binary, it might be for other targets and we only care // about running miri, not the binary. config.runtool = Some("echo \"\" || ".to_owned()); @@ -116,6 +120,7 @@ fn compile_test() { let sysroot = libs.join("rustlib").join(&host).join("lib"); let paths = std::env::join_paths(&[libs, sysroot]).unwrap(); cmd.env(compiletest::procsrv::dylib_env_var(), paths); + cmd.env("MIRI_SYSROOT", Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST")); match cmd.output() { Ok(ref output) if output.status.success() => { @@ -197,8 +202,9 @@ fn compile_test() { } else { run_pass(); for_all_targets(sysroot, |target| { - miri_pass("tests/run-pass", &target, host); + miri_pass("tests/run-pass", &target, host, false); }); + miri_pass("tests/run-pass-fullmir", host, host, true); compile_fail(sysroot); } } diff --git a/tests/run-pass/loop-break-value.rs b/tests/run-pass-fullmir/loop-break-value.rs similarity index 100% rename from tests/run-pass/loop-break-value.rs rename to tests/run-pass-fullmir/loop-break-value.rs From b001b5531b46056fe36af053420c922d3d763e71 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 09:44:10 +0200 Subject: [PATCH 0990/1096] The latest nightly doesn't expose as much MIR anymore as it used to --- tests/{run-pass => run-pass-fullmir}/heap.rs | 0 tests/{run-pass => run-pass-fullmir}/issue-15080.rs | 0 tests/{run-pass => run-pass-fullmir}/move-arg-2-unique.rs | 0 tests/{run-pass => run-pass-fullmir}/vecs.rs | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/{run-pass => run-pass-fullmir}/heap.rs (100%) rename tests/{run-pass => run-pass-fullmir}/issue-15080.rs (100%) rename tests/{run-pass => run-pass-fullmir}/move-arg-2-unique.rs (100%) rename tests/{run-pass => run-pass-fullmir}/vecs.rs (100%) diff --git a/tests/run-pass/heap.rs b/tests/run-pass-fullmir/heap.rs similarity index 100% rename from tests/run-pass/heap.rs rename to tests/run-pass-fullmir/heap.rs diff --git a/tests/run-pass/issue-15080.rs b/tests/run-pass-fullmir/issue-15080.rs similarity index 100% rename from tests/run-pass/issue-15080.rs rename to tests/run-pass-fullmir/issue-15080.rs diff --git a/tests/run-pass/move-arg-2-unique.rs b/tests/run-pass-fullmir/move-arg-2-unique.rs similarity index 100% rename from tests/run-pass/move-arg-2-unique.rs rename to tests/run-pass-fullmir/move-arg-2-unique.rs diff --git a/tests/run-pass/vecs.rs b/tests/run-pass-fullmir/vecs.rs similarity index 100% rename from tests/run-pass/vecs.rs rename to tests/run-pass-fullmir/vecs.rs From a6cd7a2e085eeaf7d928a463a76126cd6542b314 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 15:07:40 +0200 Subject: [PATCH 0991/1096] Also move some compile-fail tests to fullmir-only --- .../undefined_byte_read.rs | 0 tests/compiletest.rs | 38 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) rename tests/{compile-fail => compile-fail-fullmir}/undefined_byte_read.rs (100%) diff --git a/tests/compile-fail/undefined_byte_read.rs b/tests/compile-fail-fullmir/undefined_byte_read.rs similarity index 100% rename from tests/compile-fail/undefined_byte_read.rs rename to tests/compile-fail-fullmir/undefined_byte_read.rs diff --git a/tests/compiletest.rs b/tests/compiletest.rs index fe9cbd6479063..56435bbc4ac36 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,19 +3,24 @@ extern crate compiletest_rs as compiletest; use std::path::{PathBuf, Path}; use std::io::Write; -fn compile_fail(sysroot: &Path) { - let flags = format!("--sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path")); - for_all_targets(sysroot, |target| { - let mut config = compiletest::default_config(); - config.host_rustcflags = Some(flags.clone()); - config.mode = "compile-fail".parse().expect("Invalid mode"); - config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib"); - config.rustc_path = "target/debug/miri".into(); - config.src_base = PathBuf::from("tests/compile-fail".to_string()); - config.target = target.to_owned(); - config.target_rustcflags = Some(flags.clone()); - compiletest::run_tests(&config); - }); +fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) { + let mut config = compiletest::default_config(); + config.mode = "compile-fail".parse().expect("Invalid mode"); + config.rustc_path = "target/debug/miri".into(); + if fullmir { + if host != target { + // skip fullmir on nonhost + return; + } + let sysroot = Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST"); + config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap())); + config.src_base = PathBuf::from(path.to_string()); + } else { + config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap())); + config.src_base = PathBuf::from(path.to_string()); + } + config.target = target.to_owned(); + compiletest::run_tests(&config); } fn run_pass() { @@ -35,6 +40,10 @@ fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool) { config.host = host.to_owned(); config.rustc_path = PathBuf::from("target/debug/miri"); if fullmir { + if host != target { + // skip fullmir on nonhost + return; + } let sysroot = Path::new(&std::env::var("HOME").unwrap()).join(".xargo").join("HOST"); config.target_rustcflags = Some(format!("--sysroot {}", sysroot.to_str().unwrap())); } @@ -203,9 +212,10 @@ fn compile_test() { run_pass(); for_all_targets(sysroot, |target| { miri_pass("tests/run-pass", &target, host, false); + compile_fail(sysroot, "tests/compile-fail", &target, host, false); }); miri_pass("tests/run-pass-fullmir", host, host, true); - compile_fail(sysroot); + compile_fail(sysroot, "tests/compile-fail-fullmir", host, host, true); } } From 5414825f09b9f5d132cdb71c62700afdfab43d57 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 09:06:35 +0200 Subject: [PATCH 0992/1096] Simplify numeric intrinsics --- src/terminator/intrinsic.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index f6ef46ecbfb8e..af5f83b5fd521 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -541,12 +541,11 @@ fn numeric_intrinsic<'tcx>( kind: PrimValKind ) -> EvalResult<'tcx, PrimVal> { macro_rules! integer_intrinsic { - ($name:expr, $val:expr, $kind:expr, $method:ident) => ({ - let val = $val; + ($method:ident) => ({ let bytes = val.to_bytes()?; use value::PrimValKind::*; - let result_bytes = match $kind { + let result_bytes = match kind { I8 => (bytes as i8).$method() as u128, U8 => (bytes as u8).$method() as u128, I16 => (bytes as i16).$method() as u128, @@ -557,7 +556,7 @@ fn numeric_intrinsic<'tcx>( U64 => (bytes as u64).$method() as u128, I128 => (bytes as i128).$method() as u128, U128 => bytes.$method() as u128, - _ => bug!("invalid `{}` argument: {:?}", $name, val), + _ => bug!("invalid `{}` argument: {:?}", name, val), }; PrimVal::Bytes(result_bytes) @@ -565,10 +564,10 @@ fn numeric_intrinsic<'tcx>( } let result_val = match name { - "bswap" => integer_intrinsic!("bswap", val, kind, swap_bytes), - "ctlz" => integer_intrinsic!("ctlz", val, kind, leading_zeros), - "ctpop" => integer_intrinsic!("ctpop", val, kind, count_ones), - "cttz" => integer_intrinsic!("cttz", val, kind, trailing_zeros), + "bswap" => integer_intrinsic!(swap_bytes), + "ctlz" => integer_intrinsic!(leading_zeros), + "ctpop" => integer_intrinsic!(count_ones), + "cttz" => integer_intrinsic!(trailing_zeros), _ => bug!("not a numeric intrinsic: {}", name), }; From f22c7e43df0e69f2e7c15f0bcaca4834cfdea715 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 14:06:23 +0200 Subject: [PATCH 0993/1096] Store env vars where necessary --- src/eval_context.rs | 5 ++ src/terminator/mod.rs | 68 +++++++++++++++++-- src/value.rs | 8 +++ tests/run-pass-fullmir/foreign-fn-linkname.rs | 38 +++++++++++ 4 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 tests/run-pass-fullmir/foreign-fn-linkname.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 30a4be7b4b866..8995a199f1370 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -41,6 +41,10 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// This prevents infinite loops and huge computations from freezing up const eval. /// Remove once halting problem is solved. pub(crate) steps_remaining: u64, + + /// Environment variables set by `setenv` + /// Miri does not expose env vars from the host to the emulated program + pub(crate) env_vars: HashMap, Pointer>, } /// A stack frame. @@ -134,6 +138,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { stack: Vec::new(), stack_limit: limits.stack_limit, steps_remaining: limits.step_limit, + env_vars: HashMap::new(), } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d4e169cbcfb63..ee7e3a388198c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -670,12 +670,63 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "getenv" => { - { + let result = { let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let name = self.memory.read_c_str(name_ptr)?; - info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); + match self.env_vars.get(name) { + Some(&var) => PrimVal::Ptr(var), + None => PrimVal::Bytes(0), + } + }; + self.write_primval(dest, result, dest_ty)?; + } + + "unsetenv" => { + let mut success = None; + { + let name_ptr = args[0].read_ptr(&self.memory)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + success = Some(self.env_vars.remove(name)); + } + } + } + if let Some(old) = success { + if let Some(var) = old { + self.memory.deallocate(var)?; + } + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; + } + } + + "setenv" => { + let mut new = None; + { + let name_ptr = args[0].read_ptr(&self.memory)?; + let value_ptr = args[1].read_ptr(&self.memory)?.to_ptr()?; + let value = self.memory.read_c_str(value_ptr)?; + if !name_ptr.is_null()? { + let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; + if !name.is_empty() && !name.contains(&b'=') { + new = Some((name.to_owned(), value.to_owned())); + } + } + } + if let Some((name, value)) = new { + // +1 for the null terminator + let value_copy = self.memory.allocate((value.len() + 1) as u64, 1)?; + self.memory.write_bytes(value_copy, &value)?; + self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?, &[0])?; + if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { + self.memory.deallocate(var)?; + } + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } else { + self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } "write" => { @@ -696,6 +747,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; } + "strlen" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let n = self.memory.read_c_str(ptr)?.len(); + self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; + } + // Some things needed for sys::thread initialization to go through "signal" | "sigaction" | "sigaltstack" => { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -705,10 +762,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); let result = match name { - 30 => 4096, // _SC_PAGESIZE + 30 => PrimVal::Bytes(4096), // _SC_PAGESIZE + 70 => PrimVal::from_i128(-1), // _SC_GETPW_R_SIZE_MAX _ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))) }; - self.write_primval(dest, PrimVal::Bytes(result), dest_ty)?; + self.write_primval(dest, result, dest_ty)?; } "mmap" => { diff --git a/src/value.rs b/src/value.rs index ef11c7a8e4752..37cfea8058f47 100644 --- a/src/value.rs +++ b/src/value.rs @@ -205,6 +205,14 @@ impl<'tcx> PrimVal { } } + pub fn is_null(self) -> EvalResult<'tcx, bool> { + match self { + PrimVal::Bytes(b) => Ok(b == 0), + PrimVal::Ptr(_) => Ok(false), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { diff --git a/tests/run-pass-fullmir/foreign-fn-linkname.rs b/tests/run-pass-fullmir/foreign-fn-linkname.rs new file mode 100644 index 0000000000000..a9001a3cdcf6e --- /dev/null +++ b/tests/run-pass-fullmir/foreign-fn-linkname.rs @@ -0,0 +1,38 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + + +#![feature(std_misc, libc)] + +extern crate libc; +use std::ffi::CString; + +mod mlibc { + use libc::{c_char, size_t}; + + extern { + #[link_name = "strlen"] + pub fn my_strlen(str: *const c_char) -> size_t; + } +} + +fn strlen(str: String) -> usize { + // C string is terminated with a zero + let s = CString::new(str).unwrap(); + unsafe { + mlibc::my_strlen(s.as_ptr()) as usize + } +} + +pub fn main() { + let len = strlen("Rust".to_string()); + assert_eq!(len, 4); +} From ecc44fec7e702013d413c79614b7eddddddb4275 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 15:51:42 +0200 Subject: [PATCH 0994/1096] Implement `malloc` and `free` --- src/terminator/mod.rs | 12 ++++ tests/run-pass-fullmir/regions-mock-trans.rs | 61 ++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/run-pass-fullmir/regions-mock-trans.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ee7e3a388198c..fa4a5e5b5f1e3 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -559,6 +559,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; match &link_name[..] { + "malloc" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.memory.pointer_size(); + let ptr = self.memory.allocate(size, align)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + + "free" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + self.memory.deallocate(ptr)?; + } + "__rust_allocate" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; let align = self.value_to_primval(args[1], usize)?.to_u64()?; diff --git a/tests/run-pass-fullmir/regions-mock-trans.rs b/tests/run-pass-fullmir/regions-mock-trans.rs new file mode 100644 index 0000000000000..b67612c94b009 --- /dev/null +++ b/tests/run-pass-fullmir/regions-mock-trans.rs @@ -0,0 +1,61 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-expanded FIXME #23616 + +#![feature(libc)] + +extern crate libc; +use std::mem; + +struct arena(()); + +struct Bcx<'a> { + fcx: &'a Fcx<'a> +} + +struct Fcx<'a> { + arena: &'a arena, + ccx: &'a Ccx +} + +struct Ccx { + x: isize +} + +fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { + unsafe { + mem::transmute(libc::malloc(mem::size_of::>() + as libc::size_t)) + } +} + +fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { + return alloc(bcx.fcx.arena); +} + +fn g(fcx : &Fcx) { + let bcx = Bcx { fcx: fcx }; + let bcx2 = h(&bcx); + unsafe { + libc::free(mem::transmute(bcx2)); + } +} + +fn f(ccx : &Ccx) { + let a = arena(()); + let fcx = Fcx { arena: &a, ccx: ccx }; + return g(&fcx); +} + +pub fn main() { + let ccx = Ccx { x: 0 }; + f(&ccx); +} From a630677b01e881bf540a977daba12a7158ac162e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Jun 2017 16:34:40 +0200 Subject: [PATCH 0995/1096] Report better errors on random numbers and threads --- src/terminator/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index fa4a5e5b5f1e3..4986d830e283a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -571,6 +571,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.deallocate(ptr)?; } + "syscall" => { + match self.value_to_primval(args[0], usize)?.to_u64()? { + 511 => return Err(EvalError::Unimplemented("miri does not support random number generators".to_owned())), + id => return Err(EvalError::Unimplemented(format!("miri does not support syscall id {}", id))), + } + } + + "dlsym" => { + let handle = args[0].read_ptr(&self.memory)?; + { + let symbol = args[1].read_ptr(&self.memory)?.to_ptr()?; + let symbol_name = self.memory.read_c_str(symbol)?; + let err = format!("bad c unicode symbol: {:?}", symbol_name); + let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); + return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); + } + } + "__rust_allocate" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; let align = self.value_to_primval(args[1], usize)?.to_u64()?; From 8101592ab2cf91db6e4d30071912d75cef7c8e67 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 14:28:13 -0700 Subject: [PATCH 0996/1096] run fullmir tests against rustc; add output explaining what is being tested --- tests/compiletest.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 56435bbc4ac36..fbf8dbd8c92cc 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -3,7 +3,15 @@ extern crate compiletest_rs as compiletest; use std::path::{PathBuf, Path}; use std::io::Write; +macro_rules! eprintln { + ($($arg:tt)*) => { + let stderr = std::io::stderr(); + writeln!(stderr.lock(), $($arg)*).unwrap(); + } +} + fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: bool) { + eprintln!("## Running compile-fail tests in {} against miri for target {}", path, target); let mut config = compiletest::default_config(); config.mode = "compile-fail".parse().expect("Invalid mode"); config.rustc_path = "target/debug/miri".into(); @@ -23,16 +31,18 @@ fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, fullmir: b compiletest::run_tests(&config); } -fn run_pass() { +fn run_pass(path: &str) { + eprintln!("## Running run-pass tests in {} against rustc", path); let mut config = compiletest::default_config(); config.mode = "run-pass".parse().expect("Invalid mode"); - config.src_base = PathBuf::from("tests/run-pass".to_string()); + config.src_base = PathBuf::from(path); config.target_rustcflags = Some("-Dwarnings".to_string()); config.host_rustcflags = Some("-Dwarnings".to_string()); compiletest::run_tests(&config); } fn miri_pass(path: &str, target: &str, host: &str, fullmir: bool) { + eprintln!("## Running run-pass tests in {} against miri for target {}", path, target); let mut config = compiletest::default_config(); config.mode = "mir-opt".parse().expect("Invalid mode"); config.src_base = PathBuf::from(path); @@ -65,13 +75,10 @@ fn is_target_dir>(path: P) -> bool { fn for_all_targets(sysroot: &Path, mut f: F) { let target_dir = sysroot.join("lib").join("rustlib"); - println!("target dir: {}", target_dir.to_str().unwrap()); for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") { let entry = entry.unwrap(); if !is_target_dir(entry.path()) { continue; } let target = entry.file_name().into_string().unwrap(); - let stderr = std::io::stderr(); - writeln!(stderr.lock(), "running tests for target {}", target).unwrap(); f(target); } } @@ -209,7 +216,8 @@ fn compile_test() { panic!("ran miri on rustc test suite. Test failing for convenience"); } else { - run_pass(); + run_pass("tests/run-pass"); + run_pass("tests/run-pass-fullmir"); for_all_targets(sysroot, |target| { miri_pass("tests/run-pass", &target, host, false); compile_fail(sysroot, "tests/compile-fail", &target, host, false); From 184d3b3badb9072aee9a439386dcc8e905d1f800 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 19:24:21 -0700 Subject: [PATCH 0997/1096] expand thread-local storage tests to cover dtor order and re-running dtors --- src/terminator/mod.rs | 2 +- tests/run-pass/thread-local-no-dtor.rs | 18 ------- tests/run-pass/thread-local.rs | 67 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 19 deletions(-) delete mode 100644 tests/run-pass/thread-local-no-dtor.rs create mode 100644 tests/run-pass/thread-local.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d4e169cbcfb63..636cf2ad0b5e1 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_primval(arg_dest, data, u8_ptr_ty)?; - // We ourselbes return 0 + // We ourselves return 0 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; // Don't fall through diff --git a/tests/run-pass/thread-local-no-dtor.rs b/tests/run-pass/thread-local-no-dtor.rs deleted file mode 100644 index 4fb43793eaec5..0000000000000 --- a/tests/run-pass/thread-local-no-dtor.rs +++ /dev/null @@ -1,18 +0,0 @@ -//ignore-windows - -#![feature(libc)] -extern crate libc; - -use std::mem; - -pub type Key = libc::pthread_key_t; - -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -fn main() { - let _ = unsafe { create(None) }; -} diff --git a/tests/run-pass/thread-local.rs b/tests/run-pass/thread-local.rs new file mode 100644 index 0000000000000..003fd1ad4c00f --- /dev/null +++ b/tests/run-pass/thread-local.rs @@ -0,0 +1,67 @@ +//ignore-windows + +#![feature(libc)] +extern crate libc; + +use std::mem; + +pub type Key = libc::pthread_key_t; + +static mut RECORD : usize = 0; +static mut KEYS : [Key; 2] = [0; 2]; +static mut GLOBALS : [u64; 2] = [1, 0]; + +static mut CANNARY : *mut u64 = 0 as *mut _; // this serves as a cannary: if TLS dtors are not run properly, this will not get deallocated, making the test fail. + +pub unsafe fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +pub unsafe fn set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + assert_eq!(r, 0); +} + +pub fn record(r: usize) { + assert!(r < 10); + unsafe { RECORD = RECORD*10 + r }; +} + +unsafe extern fn dtor(mut ptr: *mut u64) { + assert!(CANNARY != 0 as *mut _); // make sure we do not get run too often + let val = *ptr; + + let which_key = GLOBALS.iter().position(|global| global as *const _ == ptr).expect("Should find my global"); + record(which_key); + + if val > 0 { + *ptr = val-1; + set(KEYS[which_key], ptr as *mut _); + } + + // Check if the records matches what we expect. If yes, clear the cannary. + // If the record is wrong, the cannary will ever get cleared, leading to a leak -> test fails. + // If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails. + // The correct sequence is: First key 0, then key 1, then key 0. + if RECORD == 0_1_0 { + drop(Box::from_raw(CANNARY)); + CANNARY = 0 as *mut _; + } +} + +fn main() { + unsafe { + create(None); // check that the no-dtor case works + + // Initialize the keys we use to check destructor ordering + for (key, global) in KEYS.iter_mut().zip(GLOBALS.iter()) { + *key = create(Some(mem::transmute(dtor as unsafe extern fn(*mut u64)))); + set(*key, global as *const _ as *mut _); + } + + // Initialize cannary + CANNARY = Box::into_raw(Box::new(0u64)); + } +} From b8c5e7fd0eead08be1c6ba7bbb20104b1c266cd8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 20:18:42 -0700 Subject: [PATCH 0998/1096] refactor pointer handling in binops --- src/operator.rs | 112 +++++++++++++++++++----------------------------- src/value.rs | 40 ++++++++++++++--- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index 0303fbe1f79f6..1c8c1a5d490cb 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -146,45 +146,52 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; - // Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers. - if bin_op == Offset { - if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) { - let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; - return Ok((ptr, false)); - } else { - bug!("Offset used with wrong type"); - } - } - - // unrelated pointer ops - let op: Option bool> = match bin_op { - Eq => Some(PrimVal::eq), - Ne => Some(PrimVal::ne), - _ => None, - }; - if let Some(op) = op { - // only floats can't be binary compared - let ok = left_kind != F32 && left_kind != F64; - let ok = ok && right_kind != F32 && right_kind != F64; - if ok { - return Ok((PrimVal::from_bool(op(&left, &right)), false)); - } - } - - - if let (Ok(left), Ok(right)) = (left.to_ptr(), right.to_ptr()) { - if left.alloc_id == right.alloc_id { - return self.ptr_ops( - bin_op, - left.offset, - right.offset, - ); - } else { - return Err(EvalError::InvalidPointerMath); + // I: Handle operations that support pointers + let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); + let isize = PrimValKind::from_int_size(self.memory.pointer_size()); + if !left_kind.is_float() && !right_kind.is_float() { + match bin_op { + Offset if left_kind == Ptr && right_kind == usize => { + let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; + let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; + return Ok((ptr, false)); + }, + // These work on anything + Eq if left_kind == right_kind => { + return Ok((PrimVal::from_bool(left == right), false)); + } + Ne if left_kind == right_kind => { + return Ok((PrimVal::from_bool(left != right), false)); + } + // These need both pointers to be in the same allocation + Lt | Le | Gt | Ge | Sub + if left_kind == right_kind + && (left_kind == Ptr || left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_ptr() => { + let left = left.to_ptr()?; + let right = right.to_ptr()?; + if left.alloc_id == right.alloc_id { + let res = match bin_op { + Lt => left.offset < right.offset, + Le => left.offset <= right.offset, + Gt => left.offset > right.offset, + Ge => left.offset >= right.offset, + Sub => { + return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset); + } + _ => bug!("We already established it has to be one of these operators."), + }; + return Ok((PrimVal::from_bool(res), false)); + } else { + // Both are pointers, but from different allocations. + return Err(EvalError::InvalidPointerMath); + } + } + _ => {} } } + // II: From now on, everything must be bytes, no pointers let l = left.to_bytes()?; let r = right.to_bytes()?; @@ -229,8 +236,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Div, F64) => f64_arithmetic!(/, l, r), (Rem, F64) => f64_arithmetic!(%, l, r), - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), (Lt, _) => PrimVal::from_bool(l < r), (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), @@ -258,37 +263,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((val, false)) } - - fn ptr_ops( - &self, - bin_op: mir::BinOp, - left: u64, - right: u64, - ) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::BinOp::*; - - let val = match bin_op { - Eq => PrimVal::from_bool(left == right), - Ne => PrimVal::from_bool(left != right), - Lt | Le | Gt | Ge => { - PrimVal::from_bool(match bin_op { - Lt => left < right, - Le => left <= right, - Gt => left > right, - Ge => left >= right, - _ => bug!("We already established it has to be a comparison operator."), - }) - } - Sub => { - let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); - return int_arithmetic!(usize, overflowing_sub, left, right); - } - _ => { - return Err(EvalError::ReadPointerAsBytes); - } - }; - Ok((val, false)) - } } pub fn unary_op<'tcx>( diff --git a/src/value.rs b/src/value.rs index ef11c7a8e4752..2f00d7eb1dff1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -160,6 +160,27 @@ impl<'tcx> PrimVal { } } + pub fn is_bytes(self) -> bool { + match self { + PrimVal::Bytes(_) => true, + _ => false, + } + } + + pub fn is_ptr(self) -> bool { + match self { + PrimVal::Ptr(_) => true, + _ => false, + } + } + + pub fn is_undef(self) -> bool { + match self { + PrimVal::Undef => true, + _ => false, + } + } + pub fn to_u128(self) -> EvalResult<'tcx, u128> { self.to_bytes() } @@ -252,14 +273,11 @@ pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalR } pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - if let Some(res) = val.checked_add(i) { - if res as u128 >= (1u128 << layout.pointer_size.bits()) { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } - } else { + let res = val.checked_add(i).ok_or(EvalError::OverflowingMath)?; + if res as u128 >= (1u128 << layout.pointer_size.bits()) { Err(EvalError::OverflowingMath) + } else { + Ok(res) } } @@ -284,6 +302,14 @@ impl PrimValKind { } } + pub fn is_float(self) -> bool { + use self::PrimValKind::*; + match self { + F32 | F64 => true, + _ => false, + } + } + pub fn from_uint_size(size: u64) -> Self { match size { 1 => PrimValKind::U8, From 7b1582b383ba98ad458001c332004b500dd39eed Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:38:43 -0700 Subject: [PATCH 0999/1096] permit integer addition and subtraction on ptr-integers --- src/memory.rs | 10 +++++++++ src/operator.rs | 37 +++++++++++++++++++++++++++++++++ src/value.rs | 28 +++++++++++++++++++------ tests/run-pass/ptr_int_casts.rs | 19 ++++++++++++++++- 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index ac2d391f4217e..b6a8237368754 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -64,10 +64,20 @@ impl Pointer { Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) } + pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) { + let (res, over) = value::overflowing_signed_offset(self.offset, i, layout); + (Pointer::new(self.alloc_id, res), over) + } + pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) } + pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) { + let (res, over) = value::overflowing_offset(self.offset, i, layout); + (Pointer::new(self.alloc_id, res), over) + } + pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) } diff --git a/src/operator.rs b/src/operator.rs index 1c8c1a5d490cb..adcd237e819ff 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -130,6 +130,19 @@ macro_rules! f64_arithmetic { ) } +macro_rules! ptr_add { + ($signed:expr, $ptr:expr, $int:expr, $layout:expr) => ({ + let ptr = $ptr; + let int = $int; + let (res, over) = if $signed { + ptr.overflowing_signed_offset(int as i128, $layout) + } else { + ptr.overflowing_offset(int as u64, $layout) + }; + (PrimVal::Ptr(res), over) + }) +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -145,6 +158,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; + //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); @@ -187,6 +201,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::InvalidPointerMath); } } + // These work if one operand is a pointer, the other an integer + Sub + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_bytes() => { + let left = left.to_ptr()?; + let right = right.to_bytes()? as i128; // this cast is fine as the kind is max. 64bit + let (res, over) = left.overflowing_signed_offset(-right, self.memory.layout); + return Ok((PrimVal::Ptr(res), over)) + } + Add + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_bytes() => { + let left = left.to_ptr()?; + let right = right.to_bytes()?; + return Ok(ptr_add!(left_kind == isize, left, right, self.memory.layout)); + } + Add + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_bytes() && right.is_ptr() => { + let left = left.to_bytes()?; + let right = right.to_ptr()?; + return Ok(ptr_add!(left_kind == isize, right, left, self.memory.layout)); + } _ => {} } } diff --git a/src/value.rs b/src/value.rs index 2f00d7eb1dff1..519c048f3ef8d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -260,21 +260,37 @@ impl<'tcx> PrimVal { } } -pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { +// Overflow checking only works properly on the range from -u64 to +u64. +pub fn overflowing_signed_offset<'tcx>(val: u64, i: i128, layout: &TargetDataLayout) -> (u64, bool) { // FIXME: is it possible to over/underflow here? if i < 0 { // trickery to ensure that i64::min_value() works fine // this formula only works for true negative values, it panics for zero! let n = u64::max_value() - (i as u64) + 1; - val.checked_sub(n).ok_or(EvalError::OverflowingMath) + val.overflowing_sub(n) + } else { + overflowing_offset(val, i as u64, layout) + } +} + +pub fn overflowing_offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> (u64, bool) { + let (res, over) = val.overflowing_add(i); + ((res as u128 % (1u128 << layout.pointer_size.bits())) as u64, + over || res as u128 >= (1u128 << layout.pointer_size.bits())) +} + +pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { + let (res, over) = overflowing_signed_offset(val, i as i128, layout); + if over { + Err(EvalError::OverflowingMath) } else { - offset(val, i as u64, layout) + Ok(res) } } pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let res = val.checked_add(i).ok_or(EvalError::OverflowingMath)?; - if res as u128 >= (1u128 << layout.pointer_size.bits()) { + let (res, over) = overflowing_offset(val, i, layout); + if over { Err(EvalError::OverflowingMath) } else { Ok(res) @@ -282,7 +298,7 @@ pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<' } pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 { - (val.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64 + overflowing_signed_offset(val, i as i128, layout).0 } impl PrimValKind { diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs index e245cb22475d5..b7b17089efc7d 100644 --- a/tests/run-pass/ptr_int_casts.rs +++ b/tests/run-pass/ptr_int_casts.rs @@ -1,15 +1,32 @@ +use std::mem; + fn eq_ref(x: &T, y: &T) -> bool { x as *const _ == y as *const _ } +fn f() -> i32 { 42 } + fn main() { // int-ptr-int assert_eq!(1 as *const i32 as usize, 1); + assert_eq!((1 as *const i32).wrapping_offset(4) as usize, 1 + 4*4); { // ptr-int-ptr let x = 13; - let y = &x as *const _ as usize; + let mut y = &x as *const _ as usize; + y += 13; + y -= 13; let y = y as *const _; assert!(eq_ref(&x, unsafe { &*y })); } + + { // fnptr-int-fnptr + let x : fn() -> i32 = f; + let y : *mut u8 = unsafe { mem::transmute(x) }; + let mut y = y as usize; + y += 13; + y -= 13; + let x : fn() -> i32 = unsafe { mem::transmute(y as *mut u8) }; + assert_eq!(x(), 42); + } } From 78aa93fa10ae3c0d34e8178c810f89f79d879b9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:45:51 -0700 Subject: [PATCH 1000/1096] correctly reject functions pointers that had arithmetic done to them --- src/memory.rs | 11 +++++++---- src/terminator/mod.rs | 8 ++++---- src/traits.rs | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index b6a8237368754..4cf3ecb8215ae 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -480,11 +480,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, ty::Instance<'tcx>> { - debug!("reading fn ptr: {}", id); - match self.functions.get(&id) { + pub fn get_fn(&self, ptr: Pointer) -> EvalResult<'tcx, ty::Instance<'tcx>> { + if ptr.offset != 0 { + return Err(EvalError::InvalidFunctionPointer); + } + debug!("reading fn ptr: {}", ptr.alloc_id); + match self.functions.get(&ptr.alloc_id) { Some(&fndef) => Ok(fndef), - None => match self.alloc_map.get(&id) { + None => match self.alloc_map.get(&ptr.alloc_id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 636cf2ad0b5e1..22b1bf309ce8b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + let instance = self.memory.get_fn(fn_ptr)?; let instance_ty = instance.def.def_ty(self.tcx); let instance_ty = self.monomorphize(instance_ty, instance.substs); match instance_ty.sty { @@ -388,7 +388,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; - let instance = self.memory.get_fn(fn_ptr.to_ptr()?.alloc_id)?; + let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); let ty = self.get_field_ty(ty, 0)?; @@ -596,7 +596,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); let f = args[0].read_ptr(&self.memory)?.to_ptr()?; let data = args[1].read_ptr(&self.memory)?; - let f_instance = self.memory.get_fn(f.alloc_id)?; + let f_instance = self.memory.get_fn(f)?; self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, @@ -723,7 +723,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Extract the function type out of the signature (that seems easier than constructing it ourselves...) let dtor = match args[1].read_ptr(&self.memory)? { - PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr.alloc_id)?), + PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), PrimVal::Undef => return Err(EvalError::ReadUndefBytes), diff --git a/src/traits.rs b/src/traits.rs index 3862e8c631a6e..680776968f989 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? { // some values don't need to call a drop impl, so the value is null Value::ByVal(PrimVal::Bytes(0)) => Ok(None), - Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn.alloc_id).map(Some), + Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn).map(Some), _ => Err(EvalError::ReadBytesAsPointer), } } From 6eafb10b870b4b7fad784402a76d6cef046f5928 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:54:42 -0700 Subject: [PATCH 1001/1096] add test for function pointer offsets --- tests/compile-fail/fn_ptr_offset.rs | 11 +++++++++++ tests/run-pass/thread-local.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/fn_ptr_offset.rs diff --git a/tests/compile-fail/fn_ptr_offset.rs b/tests/compile-fail/fn_ptr_offset.rs new file mode 100644 index 0000000000000..2d240b6a55ade --- /dev/null +++ b/tests/compile-fail/fn_ptr_offset.rs @@ -0,0 +1,11 @@ +use std::mem; + +fn f() {} + +fn main() { + let x : fn() = f; + let y : *mut u8 = unsafe { mem::transmute(x) }; + let y = y.wrapping_offset(1); + let x : fn() = unsafe { mem::transmute(y) }; + x(); //~ ERROR: tried to use an integer pointer or a dangling pointer as a function pointer +} diff --git a/tests/run-pass/thread-local.rs b/tests/run-pass/thread-local.rs index 003fd1ad4c00f..34aeef23b1ad4 100644 --- a/tests/run-pass/thread-local.rs +++ b/tests/run-pass/thread-local.rs @@ -42,7 +42,7 @@ unsafe extern fn dtor(mut ptr: *mut u64) { } // Check if the records matches what we expect. If yes, clear the cannary. - // If the record is wrong, the cannary will ever get cleared, leading to a leak -> test fails. + // If the record is wrong, the cannary will never get cleared, leading to a leak -> test fails. // If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails. // The correct sequence is: First key 0, then key 1, then key 0. if RECORD == 0_1_0 { From 894306e47d1bf5d7c4328d762e0e2c94dbeea2fe Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 22:59:47 -0700 Subject: [PATCH 1002/1096] refactor pointer arithmetic handling --- src/operator.rs | 57 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index adcd237e819ff..7cba12594f9b6 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -3,6 +3,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; +use memory::Pointer; use lvalue::Lvalue; use value::{ PrimVal, @@ -130,19 +131,6 @@ macro_rules! f64_arithmetic { ) } -macro_rules! ptr_add { - ($signed:expr, $ptr:expr, $int:expr, $layout:expr) => ({ - let ptr = $ptr; - let int = $int; - let (res, over) = if $signed { - ptr.overflowing_signed_offset(int as i128, $layout) - } else { - ptr.overflowing_offset(int as u64, $layout) - }; - (PrimVal::Ptr(res), over) - }) -} - impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -202,27 +190,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } // These work if one operand is a pointer, the other an integer - Sub + Add | Sub if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_ptr() && right.is_bytes() => { - let left = left.to_ptr()?; - let right = right.to_bytes()? as i128; // this cast is fine as the kind is max. 64bit - let (res, over) = left.overflowing_signed_offset(-right, self.memory.layout); - return Ok((PrimVal::Ptr(res), over)) - } - Add - if left_kind == right_kind && (left_kind == usize || left_kind == isize) - && left.is_ptr() && right.is_bytes() => { - let left = left.to_ptr()?; - let right = right.to_bytes()?; - return Ok(ptr_add!(left_kind == isize, left, right, self.memory.layout)); + // Cast to i128 is fine as we checked the kind to be ptr-sized + let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?; + return Ok((PrimVal::Ptr(res), over)); } Add if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_bytes() && right.is_ptr() => { - let left = left.to_bytes()?; - let right = right.to_ptr()?; - return Ok(ptr_add!(left_kind == isize, right, left, self.memory.layout)); + // This is a commutative operation, just swap the operands + let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?; + return Ok((PrimVal::Ptr(res), over)); } _ => {} } @@ -300,6 +280,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((val, false)) } + + fn ptr_int_arithmetic( + &self, + bin_op: mir::BinOp, + left: Pointer, + right: i128, + signed: bool, + ) -> EvalResult<'tcx, (Pointer, bool)> { + use rustc::mir::BinOp::*; + + Ok(match bin_op { + Sub => + // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter + left.overflowing_signed_offset(-right, self.memory.layout), + Add if signed => + left.overflowing_signed_offset(right, self.memory.layout), + Add if !signed => + left.overflowing_offset(right as u64, self.memory.layout), + _ => bug!("ptr_int_arithmetic called on unsupported operation") + }) + } } pub fn unary_op<'tcx>( From 7b2b0dd56c2ab8d5dc4e9c7e2bbd0c4128ba4f5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 23:09:34 -0700 Subject: [PATCH 1003/1096] test HashMap creation in libstd-MIR, and make it work again --- src/terminator/intrinsic.rs | 3 ++- tests/{run-pass => run-pass-fullmir}/hashmap.rs | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) rename tests/{run-pass => run-pass-fullmir}/hashmap.rs (73%) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index f6ef46ecbfb8e..8ede9fa7559b9 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -424,9 +424,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { + let ptr = ptr.to_ptr()?; self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass-fullmir/hashmap.rs similarity index 73% rename from tests/run-pass/hashmap.rs rename to tests/run-pass-fullmir/hashmap.rs index 775dee252f625..c0c396240733a 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass-fullmir/hashmap.rs @@ -1,10 +1,6 @@ use std::collections::{self, HashMap}; use std::hash::BuildHasherDefault; -// This disables the test completely: -// ignore-stage1 -// TODO: The tests actually passes against rustc and miri with MIR-libstd, but right now, we cannot express that in the test flags - fn main() { let map : HashMap> = Default::default(); assert_eq!(map.values().fold(0, |x, y| x+y), 0); From a805606d45443badecb265588e1574148ce22ef9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Jun 2017 08:52:53 +0200 Subject: [PATCH 1004/1096] malloc should return null for zst allocs --- src/terminator/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4986d830e283a..4aae47ba96c0f 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -561,14 +561,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "malloc" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.memory.pointer_size(); - let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + if size == 0 { + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } else { + let align = self.memory.pointer_size(); + let ptr = self.memory.allocate(size, align)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } } "free" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - self.memory.deallocate(ptr)?; + let ptr = args[0].read_ptr(&self.memory)?; + if !ptr.is_null() { + self.memory.deallocate(ptr.to_ptr()?)?; + } } "syscall" => { From 86d31fa09c16f880b4be52f063e3eee757d3b87c Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Thu, 22 Jun 2017 17:44:20 +0900 Subject: [PATCH 1005/1096] update xargo usage example for new location of vecs.rs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd9599f1c068f..8edaba77fb3fa 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ RUSTFLAGS='-Zalways-encode-mir' xargo build Now you can run miri against the libstd compiled by xargo: ```sh -MIRI_SYSROOT=~/.xargo/HOST cargo run --bin miri tests/run-pass/vecs.rs +MIRI_SYSROOT=~/.xargo/HOST cargo run --bin miri tests/run-pass-fullmir/vecs.rs ``` Notice that you will have to re-run the last step of the preparations above when From 8733bd0e7c10dc5aebba372c21861d15767e709a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Jun 2017 11:40:06 +0200 Subject: [PATCH 1006/1096] Update mod.rs --- src/terminator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4aae47ba96c0f..188104ef9eb9d 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -572,7 +572,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "free" => { let ptr = args[0].read_ptr(&self.memory)?; - if !ptr.is_null() { + if !ptr.is_null()? { self.memory.deallocate(ptr.to_ptr()?)?; } } From 1883aac8c221fdabd0e2664494898c28412e1bc4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Jun 2017 12:27:18 +0200 Subject: [PATCH 1007/1096] Update foreign-fn-linkname.rs --- tests/run-pass-fullmir/foreign-fn-linkname.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-pass-fullmir/foreign-fn-linkname.rs b/tests/run-pass-fullmir/foreign-fn-linkname.rs index a9001a3cdcf6e..b569cd0a66291 100644 --- a/tests/run-pass-fullmir/foreign-fn-linkname.rs +++ b/tests/run-pass-fullmir/foreign-fn-linkname.rs @@ -10,7 +10,7 @@ -#![feature(std_misc, libc)] +#![feature(libc)] extern crate libc; use std::ffi::CString; From 2b9cfb68500ec0e2ada1fe698950606f2322d983 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Jun 2017 12:30:02 +0200 Subject: [PATCH 1008/1096] Update regions-mock-trans.rs --- tests/run-pass-fullmir/regions-mock-trans.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/run-pass-fullmir/regions-mock-trans.rs b/tests/run-pass-fullmir/regions-mock-trans.rs index b67612c94b009..7d9d31b0dda19 100644 --- a/tests/run-pass-fullmir/regions-mock-trans.rs +++ b/tests/run-pass-fullmir/regions-mock-trans.rs @@ -12,17 +12,19 @@ #![feature(libc)] +#![allow(dead_code)] + extern crate libc; use std::mem; -struct arena(()); +struct Arena(()); struct Bcx<'a> { fcx: &'a Fcx<'a> } struct Fcx<'a> { - arena: &'a arena, + arena: &'a Arena, ccx: &'a Ccx } @@ -30,7 +32,7 @@ struct Ccx { x: isize } -fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { +fn alloc<'a>(_bcx : &'a Arena) -> &'a Bcx<'a> { unsafe { mem::transmute(libc::malloc(mem::size_of::>() as libc::size_t)) @@ -50,7 +52,7 @@ fn g(fcx : &Fcx) { } fn f(ccx : &Ccx) { - let a = arena(()); + let a = Arena(()); let fcx = Fcx { arena: &a, ccx: ccx }; return g(&fcx); } From 3637aa86c881cab5eca4d0c21bfcb780a7a3a279 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 14:05:25 -0700 Subject: [PATCH 1009/1096] allow any offset on integer pointers --- src/eval_context.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 8995a199f1370..aefe65ab22d36 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -892,19 +892,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { - if offset == 0 { - // rustc relies on Offset-by-0 to be well-defined even for "bad" pointers like Unique::empty(). - return Ok(ptr); + if ptr == PrimVal::from_u128(0) { // rule out NULL pointers + return Err(EvalError::InvalidPointerMath); } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; - if pointee_size == 0 { - // rustc relies on offsetting pointers to zsts to be a nop - return Ok(ptr); - } return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; - self.memory.check_bounds(ptr.to_ptr()?, false)?; + // Do not do bounds-checking for integers or ZST; they can never alias a normal pointer anyway. + if let PrimVal::Ptr(ptr) = ptr { + if !ptr.points_to_zst() { + self.memory.check_bounds(ptr, false)?; + } + } Ok(ptr) } else { Err(EvalError::OverflowingMath) From 6512fa7be896e48ef349b8505cf7a561169a3cb3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 11:33:31 -0700 Subject: [PATCH 1010/1096] use PrimVal::is_null --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index aefe65ab22d36..80f2181122126 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -892,7 +892,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { - if ptr == PrimVal::from_u128(0) { // rule out NULL pointers + if ptr.is_null()? { // rule out NULL pointers return Err(EvalError::InvalidPointerMath); } // FIXME: assuming here that type size is < i64::max_value() From 57391bab1098cb75bc9a1cfaeb4a3ecb05386555 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 11:33:39 -0700 Subject: [PATCH 1011/1096] fix unused variable warning --- src/terminator/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 7cb1a3cd62993..90f0aed7f10a8 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -585,14 +585,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "dlsym" => { - let handle = args[0].read_ptr(&self.memory)?; - { - let symbol = args[1].read_ptr(&self.memory)?.to_ptr()?; - let symbol_name = self.memory.read_c_str(symbol)?; - let err = format!("bad c unicode symbol: {:?}", symbol_name); - let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); - return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); - } + let _handle = args[0].read_ptr(&self.memory)?; + let symbol = args[1].read_ptr(&self.memory)?.to_ptr()?; + let symbol_name = self.memory.read_c_str(symbol)?; + let err = format!("bad c unicode symbol: {:?}", symbol_name); + let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); + return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); } "__rust_allocate" => { From 2f6135685852433aef7f524718e5880528e0cbe5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 13:38:17 -0700 Subject: [PATCH 1012/1096] permit offsetting a NULL-ptr by 0, to fix hashmap test --- src/error.rs | 5 ++++- src/eval_context.rs | 14 +++++++++++--- ...ers_to_different_allocations_are_unorderable.rs | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index e7e446e93ad7c..ee805695c510f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,6 +21,7 @@ pub enum EvalError<'tcx> { access: bool, allocation_size: u64, }, + NullPointerOutOfBounds, ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, @@ -80,12 +81,14 @@ impl<'tcx> Error for EvalError<'tcx> { "invalid enum discriminant value read", EvalError::PointerOutOfBounds { .. } => "pointer offset outside bounds of allocation", + EvalError::NullPointerOutOfBounds => + "invalid NULL pointer offset", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::ReadBytesAsPointer => "a memory access tried to interpret some bytes as a pointer", EvalError::InvalidPointerMath => - "attempted to do math or a comparison on pointers into different allocations", + "attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. compating pointers into different allocations", EvalError::ReadUndefBytes => "attempted to read undefined bytes", EvalError::DeadLocal => diff --git a/src/eval_context.rs b/src/eval_context.rs index 80f2181122126..f4e7c19d935cc 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -892,8 +892,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { - if ptr.is_null()? { // rule out NULL pointers - return Err(EvalError::InvalidPointerMath); + // This function raises an error if the offset moves the pointer outside of its allocation. We consider + // ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0). + // We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own + // allocation. + + if ptr.is_null()? { // NULL pointers must only be offset by 0 + return if offset == 0 { Ok(ptr) } else { Err(EvalError::NullPointerOutOfBounds) }; } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; @@ -901,9 +906,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = ptr.signed_offset(offset, self.memory.layout)?; // Do not do bounds-checking for integers or ZST; they can never alias a normal pointer anyway. if let PrimVal::Ptr(ptr) = ptr { - if !ptr.points_to_zst() { + if !(ptr.points_to_zst() && pointee_size == 0) { self.memory.check_bounds(ptr, false)?; } + } else if ptr.is_null()? { + // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. + return Err(EvalError::NullPointerOutOfBounds); } Ok(ptr) } else { diff --git a/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs b/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs index be478c8213246..245b7527c55b2 100644 --- a/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs +++ b/tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs @@ -1,7 +1,7 @@ fn main() { let x: *const u8 = &1; let y: *const u8 = &2; - if x < y { //~ ERROR: attempted to do math or a comparison on pointers into different allocations + if x < y { //~ ERROR: attempted to do invalid arithmetic on pointers unreachable!() } } From c1a6df941e040cf2e6f524a78fc407f9cc56c02c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 14:26:17 -0700 Subject: [PATCH 1013/1096] permit all kinds of 0-offsets on ZSTs --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index f4e7c19d935cc..9300ae3dbc235 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -906,7 +906,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = ptr.signed_offset(offset, self.memory.layout)?; // Do not do bounds-checking for integers or ZST; they can never alias a normal pointer anyway. if let PrimVal::Ptr(ptr) = ptr { - if !(ptr.points_to_zst() && pointee_size == 0) { + if !(ptr.points_to_zst() && (offset == 0 || pointee_size == 0)) { self.memory.check_bounds(ptr, false)?; } } else if ptr.is_null()? { From 269667e152e9377dfe8894b661e110fbd773bdd2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 00:08:19 -0700 Subject: [PATCH 1014/1096] implement _nonzero intrinsics --- src/error.rs | 5 ++++ src/terminator/intrinsic.rs | 19 +++++++++----- tests/run-pass/intrinsics-integer.rs | 37 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/error.rs b/src/error.rs index ee805695c510f..496cefad33d55 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), + Intrinsic(String), OverflowingMath, InvalidChar(u128), OutOfMemory { @@ -104,6 +105,8 @@ impl<'tcx> Error for EvalError<'tcx> { "array index out of bounds", EvalError::Math(..) => "mathematical operation failed", + EvalError::Intrinsic(..) => + "intrinsic failed", EvalError::OverflowingMath => "attempted to do overflowing math", EvalError::NoMirFor(..) => @@ -168,6 +171,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), EvalError::Math(span, ref err) => write!(f, "{:?} at {:?}", err, span), + EvalError::Intrinsic(ref err) => + write!(f, "{}", err), EvalError::InvalidChar(c) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 0b9a37512ede8..5fd0cc5802203 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -154,12 +154,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "ctpop" | "cttz" | + "cttz_nonzero" | "ctlz" | + "ctlz_nonzero" | "bswap" => { let ty = substs.type_at(0); - let num = self.value_to_primval(arg_vals[0], ty)?; + let num = self.value_to_primval(arg_vals[0], ty)?.to_bytes()?; let kind = self.ty_to_primval_kind(ty)?; - let num = numeric_intrinsic(intrinsic_name, num, kind)?; + let num = if intrinsic_name.ends_with("_nonzero") { + if num == 0 { + return Err(EvalError::Intrinsic(format!("{} called on 0", intrinsic_name))) + } + numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)? + } else { + numeric_intrinsic(intrinsic_name, num, kind)? + }; self.write_primval(dest, num, ty)?; } @@ -538,13 +547,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn numeric_intrinsic<'tcx>( name: &str, - val: PrimVal, + bytes: u128, kind: PrimValKind ) -> EvalResult<'tcx, PrimVal> { macro_rules! integer_intrinsic { ($method:ident) => ({ - let bytes = val.to_bytes()?; - use value::PrimValKind::*; let result_bytes = match kind { I8 => (bytes as i8).$method() as u128, @@ -557,7 +564,7 @@ fn numeric_intrinsic<'tcx>( U64 => (bytes as u64).$method() as u128, I128 => (bytes as i128).$method() as u128, U128 => bytes.$method() as u128, - _ => bug!("invalid `{}` argument: {:?}", name, val), + _ => bug!("invalid `{}` argument: {:?}", name, bytes), }; PrimVal::Bytes(result_bytes) diff --git a/tests/run-pass/intrinsics-integer.rs b/tests/run-pass/intrinsics-integer.rs index 759dc515456de..4896f02da20b0 100644 --- a/tests/run-pass/intrinsics-integer.rs +++ b/tests/run-pass/intrinsics-integer.rs @@ -14,7 +14,9 @@ mod rusti { extern "rust-intrinsic" { pub fn ctpop(x: T) -> T; pub fn ctlz(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> T; pub fn cttz(x: T) -> T; + pub fn cttz_nonzero(x: T) -> T; pub fn bswap(x: T) -> T; } } @@ -68,6 +70,21 @@ pub fn main() { assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25); assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57); + assert_eq!(ctlz_nonzero(1u8), 7); assert_eq!(ctlz_nonzero(1i8), 7); + assert_eq!(ctlz_nonzero(1u16), 15); assert_eq!(ctlz_nonzero(1i16), 15); + assert_eq!(ctlz_nonzero(1u32), 31); assert_eq!(ctlz_nonzero(1i32), 31); + assert_eq!(ctlz_nonzero(1u64), 63); assert_eq!(ctlz_nonzero(1i64), 63); + + assert_eq!(ctlz_nonzero(10u8), 4); assert_eq!(ctlz_nonzero(10i8), 4); + assert_eq!(ctlz_nonzero(10u16), 12); assert_eq!(ctlz_nonzero(10i16), 12); + assert_eq!(ctlz_nonzero(10u32), 28); assert_eq!(ctlz_nonzero(10i32), 28); + assert_eq!(ctlz_nonzero(10u64), 60); assert_eq!(ctlz_nonzero(10i64), 60); + + assert_eq!(ctlz_nonzero(100u8), 1); assert_eq!(ctlz_nonzero(100i8), 1); + assert_eq!(ctlz_nonzero(100u16), 9); assert_eq!(ctlz_nonzero(100i16), 9); + assert_eq!(ctlz_nonzero(100u32), 25); assert_eq!(ctlz_nonzero(100i32), 25); + assert_eq!(ctlz_nonzero(100u64), 57); assert_eq!(ctlz_nonzero(100i64), 57); + assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0); assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0); assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0); @@ -93,6 +110,26 @@ pub fn main() { assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2); assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2); + assert_eq!(cttz_nonzero(-1i8 as u8), 0); assert_eq!(cttz_nonzero(-1i8), 0); + assert_eq!(cttz_nonzero(-1i16 as u16), 0); assert_eq!(cttz_nonzero(-1i16), 0); + assert_eq!(cttz_nonzero(-1i32 as u32), 0); assert_eq!(cttz_nonzero(-1i32), 0); + assert_eq!(cttz_nonzero(-1i64 as u64), 0); assert_eq!(cttz_nonzero(-1i64), 0); + + assert_eq!(cttz_nonzero(1u8), 0); assert_eq!(cttz_nonzero(1i8), 0); + assert_eq!(cttz_nonzero(1u16), 0); assert_eq!(cttz_nonzero(1i16), 0); + assert_eq!(cttz_nonzero(1u32), 0); assert_eq!(cttz_nonzero(1i32), 0); + assert_eq!(cttz_nonzero(1u64), 0); assert_eq!(cttz_nonzero(1i64), 0); + + assert_eq!(cttz_nonzero(10u8), 1); assert_eq!(cttz_nonzero(10i8), 1); + assert_eq!(cttz_nonzero(10u16), 1); assert_eq!(cttz_nonzero(10i16), 1); + assert_eq!(cttz_nonzero(10u32), 1); assert_eq!(cttz_nonzero(10i32), 1); + assert_eq!(cttz_nonzero(10u64), 1); assert_eq!(cttz_nonzero(10i64), 1); + + assert_eq!(cttz_nonzero(100u8), 2); assert_eq!(cttz_nonzero(100i8), 2); + assert_eq!(cttz_nonzero(100u16), 2); assert_eq!(cttz_nonzero(100i16), 2); + assert_eq!(cttz_nonzero(100u32), 2); assert_eq!(cttz_nonzero(100i32), 2); + assert_eq!(cttz_nonzero(100u64), 2); assert_eq!(cttz_nonzero(100i64), 2); + assert_eq!(bswap(0x0Au8), 0x0A); // no-op assert_eq!(bswap(0x0Ai8), 0x0A); // no-op assert_eq!(bswap(0x0A0Bu16), 0x0B0A); From 14cb31fb88328a5f9a07e00b06d6b0a014fbe197 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 00:12:47 -0700 Subject: [PATCH 1015/1096] permit bit-anding in pointer values below the alignment This makes HashMap work! --- src/memory.rs | 2 +- src/operator.rs | 42 ++++++++++++++----- src/value.rs | 2 +- tests/run-pass-fullmir/hashmap.rs | 22 +++++++--- .../tag-align-dyn-u64.rs | 2 +- 5 files changed, 50 insertions(+), 20 deletions(-) rename tests/{compile-fail => run-pass}/tag-align-dyn-u64.rs (87%) diff --git a/src/memory.rs b/src/memory.rs index 4cf3ecb8215ae..4cb66f2acdc87 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -546,7 +546,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { StaticKind::Immutable => " (immutable)", StaticKind::NotStatic => "", }; - trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable); + trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable); if !relocations.is_empty() { msg.clear(); diff --git a/src/operator.rs b/src/operator.rs index 7cba12594f9b6..58003331e6376 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -190,19 +190,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } // These work if one operand is a pointer, the other an integer - Add | Sub + Add | BitAnd | Sub if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_ptr() && right.is_bytes() => { // Cast to i128 is fine as we checked the kind to be ptr-sized - let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?; - return Ok((PrimVal::Ptr(res), over)); + return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize); } - Add + Add | BitAnd if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_bytes() && right.is_ptr() => { // This is a commutative operation, just swap the operands - let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?; - return Ok((PrimVal::Ptr(res), over)); + return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize); } _ => {} } @@ -287,18 +285,40 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { left: Pointer, right: i128, signed: bool, - ) -> EvalResult<'tcx, (Pointer, bool)> { + ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; + fn map_to_primval((res, over) : (Pointer, bool)) -> (PrimVal, bool) { + (PrimVal::Ptr(res), over) + } + Ok(match bin_op { Sub => // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter - left.overflowing_signed_offset(-right, self.memory.layout), + map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)), Add if signed => - left.overflowing_signed_offset(right, self.memory.layout), + map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)), Add if !signed => - left.overflowing_offset(right as u64, self.memory.layout), - _ => bug!("ptr_int_arithmetic called on unsupported operation") + map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)), + + BitAnd if !signed => { + let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1); + let right = right as u64; + if right & base_mask == base_mask { + // Case 1: The base address bits are all preserved, i.e., right is all-1 there + (PrimVal::Ptr(Pointer::new(left.alloc_id, left.offset & right)), false) + } else if right & base_mask == 0 { + // Case 2: The base address bits are all taken away, i.e., right is all-0 there + (PrimVal::from_u128((left.offset & right) as u128), false) + } else { + return Err(EvalError::ReadPointerAsBytes); + } + } + + _ => { + let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" }); + return Err(EvalError::Unimplemented(msg)); + } }) } } diff --git a/src/value.rs b/src/value.rs index 66c3049b9c7cd..99630f6006f85 100644 --- a/src/value.rs +++ b/src/value.rs @@ -25,7 +25,7 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { pub(super) fn bytes_to_bool(n: u128) -> bool { // FIXME(solson): Can we reach here due to user error? - debug_assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n); + assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n); n & 1 == 1 } diff --git a/tests/run-pass-fullmir/hashmap.rs b/tests/run-pass-fullmir/hashmap.rs index c0c396240733a..f4a358174f555 100644 --- a/tests/run-pass-fullmir/hashmap.rs +++ b/tests/run-pass-fullmir/hashmap.rs @@ -2,14 +2,24 @@ use std::collections::{self, HashMap}; use std::hash::BuildHasherDefault; fn main() { - let map : HashMap> = Default::default(); + let mut map : HashMap> = Default::default(); + map.insert(0, 0); assert_eq!(map.values().fold(0, |x, y| x+y), 0); - // TODO: This performs bit operations on the least significant bit of a pointer -// for i in 0..33 { -// map.insert(format!("key_{}", i), i); -// assert_eq!(map.values().fold(0, |x, y| x+y), i*(i+1)/2); -// } + let table_base = map.get(&0).unwrap() as *const _; + + let num = 22; // large enough to trigger a resize + for i in 1..num { + map.insert(i, i); + } + assert!(table_base != map.get(&0).unwrap() as *const _); // make sure relocation happened + assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2); // check the right things are in the table now + + // Inserting again replaces the existing entries + for i in 0..num { + map.insert(i, num-1-i); + } + assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2); // TODO: Test Entry API } diff --git a/tests/compile-fail/tag-align-dyn-u64.rs b/tests/run-pass/tag-align-dyn-u64.rs similarity index 87% rename from tests/compile-fail/tag-align-dyn-u64.rs rename to tests/run-pass/tag-align-dyn-u64.rs index dc93965b7e552..81c19022ab080 100644 --- a/tests/compile-fail/tag-align-dyn-u64.rs +++ b/tests/run-pass/tag-align-dyn-u64.rs @@ -28,7 +28,7 @@ fn mk_rec() -> Rec { fn is_u64_aligned(u: &Tag) -> bool { let p: usize = unsafe { mem::transmute(u) }; let u64_align = std::mem::align_of::(); - return (p & (u64_align - 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes + return (p & (u64_align - 1)) == 0; } pub fn main() { From 12935b6514c5e09fe67083da3e50dba18b2ae43d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 14:42:23 -0700 Subject: [PATCH 1016/1096] add some compile-fail tests --- tests/compile-fail/bitop-beyond-alignment.rs | 37 ++++++++++++++++++++ tests/compile-fail/ctlz_nonzero.rs | 15 ++++++++ tests/compile-fail/cttz_nonzero.rs | 15 ++++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/compile-fail/bitop-beyond-alignment.rs create mode 100644 tests/compile-fail/ctlz_nonzero.rs create mode 100644 tests/compile-fail/cttz_nonzero.rs diff --git a/tests/compile-fail/bitop-beyond-alignment.rs b/tests/compile-fail/bitop-beyond-alignment.rs new file mode 100644 index 0000000000000..a30c054ab5d04 --- /dev/null +++ b/tests/compile-fail/bitop-beyond-alignment.rs @@ -0,0 +1,37 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use std::mem; + +enum Tag { + Tag2(A) +} + +struct Rec { + c8: u8, + t: Tag +} + +fn mk_rec() -> Rec { + return Rec { c8:0, t:Tag::Tag2(0) }; +} + +fn is_u64_aligned(u: &Tag) -> bool { + let p: usize = unsafe { mem::transmute(u) }; + let u64_align = std::mem::align_of::(); + return (p & (u64_align + 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes +} + +pub fn main() { + let x = mk_rec(); + assert!(is_u64_aligned(&x.t)); +} diff --git a/tests/compile-fail/ctlz_nonzero.rs b/tests/compile-fail/ctlz_nonzero.rs new file mode 100644 index 0000000000000..704c4d4b7d462 --- /dev/null +++ b/tests/compile-fail/ctlz_nonzero.rs @@ -0,0 +1,15 @@ +#![feature(intrinsics)] + +mod rusti { + extern "rust-intrinsic" { + pub fn ctlz_nonzero(x: T) -> T; + } +} + +pub fn main() { + unsafe { + use rusti::*; + + ctlz_nonzero(0u8); //~ ERROR: ctlz_nonzero called on 0 + } +} diff --git a/tests/compile-fail/cttz_nonzero.rs b/tests/compile-fail/cttz_nonzero.rs new file mode 100644 index 0000000000000..eda25c6615214 --- /dev/null +++ b/tests/compile-fail/cttz_nonzero.rs @@ -0,0 +1,15 @@ +#![feature(intrinsics)] + +mod rusti { + extern "rust-intrinsic" { + pub fn cttz_nonzero(x: T) -> T; + } +} + +pub fn main() { + unsafe { + use rusti::*; + + cttz_nonzero(0u8); //~ ERROR: cttz_nonzero called on 0 + } +} From d5c031640f3835ae96f6dcf52a6845cddbc80eac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 20:20:26 -0700 Subject: [PATCH 1017/1096] use PrimVal::to_bool rather than bytes_to_bool --- src/operator.rs | 3 +-- src/value.rs | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index 58003331e6376..ed69c8043939b 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -13,7 +13,6 @@ use value::{ bytes_to_f64, f32_to_bytes, f64_to_bytes, - bytes_to_bool, }; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -334,7 +333,7 @@ pub fn unary_op<'tcx>( let bytes = val.to_bytes()?; let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !bytes_to_bool(bytes) as u128, + (Not, Bool) => !val.to_bool()? as u128, (Not, U8) => !(bytes as u8) as u128, (Not, U16) => !(bytes as u16) as u128, diff --git a/src/value.rs b/src/value.rs index 99630f6006f85..9f7d3eafe1fb7 100644 --- a/src/value.rs +++ b/src/value.rs @@ -23,12 +23,6 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { unsafe { transmute::(f) as u128 } } -pub(super) fn bytes_to_bool(n: u128) -> bool { - // FIXME(solson): Can we reach here due to user error? - assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n); - n & 1 == 1 -} - /// A `Value` represents a single self-contained Rust value. /// /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve From 9be3e9185b3c6676ce5e8dd16499915251bd7242 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Jun 2017 12:55:49 +0200 Subject: [PATCH 1018/1096] Remove the zst allocation --- src/error.rs | 6 ++++++ src/eval_context.rs | 20 ++++++++++++-------- src/lvalue.rs | 4 ---- src/memory.rs | 36 +++++++----------------------------- src/terminator/drop.rs | 2 +- src/terminator/mod.rs | 30 +++++++++++++++++++++++++++--- tests/compile-fail/zst.rs | 2 +- tests/compile-fail/zst2.rs | 8 ++++++++ tests/compile-fail/zst3.rs | 8 ++++++++ tests/run-pass/zst.rs | 2 -- 10 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 tests/compile-fail/zst2.rs create mode 100644 tests/compile-fail/zst3.rs diff --git a/src/error.rs b/src/error.rs index 496cefad33d55..91bd0d959ff29 100644 --- a/src/error.rs +++ b/src/error.rs @@ -59,6 +59,8 @@ pub enum EvalError<'tcx> { ReallocatedStaticMemory, DeallocatedStaticMemory, Layout(layout::LayoutError<'tcx>), + HeapAllocZeroBytes, + HeapAllocNonPowerOfTwoAlignment(u64), Unreachable, Panic, } @@ -146,6 +148,10 @@ impl<'tcx> Error for EvalError<'tcx> { "rustc layout computation failed", EvalError::UnterminatedCString(_) => "attempted to get length of a null terminated string, but no null found before end of allocation", + EvalError::HeapAllocZeroBytes => + "tried to re-, de- or allocate zero bytes on the heap", + EvalError::HeapAllocNonPowerOfTwoAlignment(_) => + "tried to re-, de-, or allocate heap memory with alignment that is not a power of two", EvalError::Unreachable => "entered unreachable code", EvalError::Panic => diff --git a/src/eval_context.rs b/src/eval_context.rs index 9300ae3dbc235..0a2bce922df50 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -370,7 +370,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance, mir.span, mir, - Lvalue::zst(), + Lvalue::undef(), StackPopCleanup::Tls(Some(key)), )?; let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; @@ -673,8 +673,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } NullaryOp(mir::NullOp::Box, ty) => { - let ptr = self.alloc_ptr(ty)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + // FIXME: call the `exchange_malloc` lang item if available + if self.type_size(ty)?.expect("box only works with sized types") == 0 { + let align = self.type_align(ty)?; + self.write_primval(dest, PrimVal::Bytes(align.into()), dest_ty)?; + } else { + let ptr = self.alloc_ptr(ty)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } } NullaryOp(mir::NullOp::SizeOf, ty) => { @@ -904,11 +910,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; - // Do not do bounds-checking for integers or ZST; they can never alias a normal pointer anyway. + // Do not do bounds-checking for integers; they can never alias a normal pointer anyway. if let PrimVal::Ptr(ptr) = ptr { - if !(ptr.points_to_zst() && (offset == 0 || pointee_size == 0)) { - self.memory.check_bounds(ptr, false)?; - } + self.memory.check_bounds(ptr, false)?; } else if ptr.is_null()? { // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. return Err(EvalError::NullPointerOutOfBounds); @@ -1697,7 +1701,7 @@ pub fn eval_main<'a, 'tcx: 'a>( main_instance, main_mir.span, main_mir, - Lvalue::zst(), + Lvalue::undef(), StackPopCleanup::Tls(None), )?; } diff --git a/src/lvalue.rs b/src/lvalue.rs index f409f3734848a..9205e0c299bd2 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -73,10 +73,6 @@ impl<'tcx> Lvalue<'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::None } } - pub fn zst() -> Self { - Self::from_ptr(Pointer::zst_ptr()) - } - pub fn from_ptr(ptr: Pointer) -> Self { Self::from_primval_ptr(PrimVal::Ptr(ptr)) } diff --git a/src/memory.rs b/src/memory.rs index 4cb66f2acdc87..e34737f464de4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -81,14 +81,6 @@ impl Pointer { pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) } - - pub fn points_to_zst(&self) -> bool { - self.alloc_id == ZST_ALLOC_ID - } - - pub fn zst_ptr() -> Self { - Pointer::new(ZST_ALLOC_ID, 0) - } } pub type TlsKey = usize; @@ -157,15 +149,13 @@ pub struct Memory<'a, 'tcx> { next_thread_local: TlsKey, } -const ZST_ALLOC_ID: AllocId = AllocId(0); - impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), - next_id: AllocId(2), + next_id: AllocId(0), layout, memory_size: max_memory, memory_usage: 0, @@ -206,10 +196,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, Pointer> { - if size == 0 { - return Ok(Pointer::zst_ptr()); - } assert_ne!(align, 0); + assert!(align.is_power_of_two()); if self.memory_size - self.memory_usage < size { return Err(EvalError::OutOfMemory { @@ -236,13 +224,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 { return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } - if ptr.points_to_zst() { - return self.allocate(new_size, align); - } if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { return Err(EvalError::ReallocatedStaticMemory); } @@ -253,6 +239,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let amount = new_size - size; self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; + // FIXME: check alignment here assert_eq!(amount as usize as u64, amount); alloc.bytes.extend(iter::repeat(0).take(amount as usize)); alloc.undef_mask.grow(amount, false); @@ -260,6 +247,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.memory_usage -= size - new_size; self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; + // FIXME: check alignment here // `as usize` is fine here, since it is smaller than `size`, which came from a usize alloc.bytes.truncate(new_size as usize); alloc.bytes.shrink_to_fit(); @@ -271,9 +259,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): See comment on `reallocate`. pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> { - if ptr.points_to_zst() { - return Ok(()); - } if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); @@ -459,7 +444,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -474,7 +458,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }, None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), - None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -508,7 +491,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let mut allocs_seen = HashSet::new(); while let Some(id) = allocs_to_print.pop_front() { - if id == ZST_ALLOC_ID { continue; } let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); let prefix_len = msg.len(); let mut relocations = vec![]; @@ -556,10 +538,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { for (i, target_id) in relocations { // this `as usize` is fine, since we can't print more chars than `usize::MAX` write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap(); - let target = match target_id { - ZST_ALLOC_ID => String::from("zst"), - _ => format!("({})", target_id), - }; + let target = format!("({})", target_id); // this `as usize` is fine, since we can't print more chars than `usize::MAX` write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap(); pos = i + self.pointer_size(); @@ -637,7 +616,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as being the entry point to a static (see `static_alloc` field) pub fn mark_static(&mut self, alloc_id: AllocId) { trace!("mark_static: {:?}", alloc_id); - if alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { + if !self.static_alloc.insert(alloc_id) { bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); } } @@ -667,7 +646,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // mark recursively mem::replace(relocations, Default::default()) }, - None if alloc_id == ZST_ALLOC_ID => return Ok(()), None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref), _ => return Ok(()), }; diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 663d05254e5cc..072a5d16a1bea 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -49,7 +49,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { instance, span, mir, - Lvalue::zst(), + Lvalue::undef(), StackPopCleanup::None, )?; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 90f0aed7f10a8..c773620cbb581 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -596,6 +596,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_allocate" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } let ptr = self.memory.allocate(size, align)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } @@ -603,6 +609,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_allocate_zeroed" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } let ptr = self.memory.allocate(size, align)?; self.memory.write_repeat(ptr, 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; @@ -611,8 +623,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_deallocate" => { let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; // FIXME: insert sanity check for size and align? - let _old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let _align = self.value_to_primval(args[2], usize)?.to_u64()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let align = self.value_to_primval(args[2], usize)?.to_u64()?; + if old_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } self.memory.deallocate(ptr)?; }, @@ -620,6 +638,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; let size = self.value_to_primval(args[2], usize)?.to_u64()?; let align = self.value_to_primval(args[3], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } let new_ptr = self.memory.reallocate(ptr, size, align)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } @@ -640,7 +664,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { f_instance, mir.span, mir, - Lvalue::zst(), + Lvalue::undef(), StackPopCleanup::Goto(dest_block), )?; diff --git a/tests/compile-fail/zst.rs b/tests/compile-fail/zst.rs index 970cc9abc9daf..3439824047943 100644 --- a/tests/compile-fail/zst.rs +++ b/tests/compile-fail/zst.rs @@ -1,4 +1,4 @@ fn main() { let x = &() as *const () as *const i32; - let _ = unsafe { *x }; //~ ERROR: tried to access memory through an invalid pointer + let _ = unsafe { *x }; //~ ERROR: tried to access memory with alignment 1, but alignment 4 is required } diff --git a/tests/compile-fail/zst2.rs b/tests/compile-fail/zst2.rs new file mode 100644 index 0000000000000..dd619377b9f66 --- /dev/null +++ b/tests/compile-fail/zst2.rs @@ -0,0 +1,8 @@ +// error-pattern: the evaluated program panicked + +#[derive(Debug)] +struct A; + +fn main() { + assert_eq!(&A as *const A as *const (), &() as *const _); +} diff --git a/tests/compile-fail/zst3.rs b/tests/compile-fail/zst3.rs new file mode 100644 index 0000000000000..561938e4e9dbf --- /dev/null +++ b/tests/compile-fail/zst3.rs @@ -0,0 +1,8 @@ +// error-pattern: the evaluated program panicked + +#[derive(Debug)] +struct A; + +fn main() { + assert_eq!(&A as *const A, &A as *const A); +} diff --git a/tests/run-pass/zst.rs b/tests/run-pass/zst.rs index 06e41e59e6023..c1c88875c5c80 100644 --- a/tests/run-pass/zst.rs +++ b/tests/run-pass/zst.rs @@ -13,8 +13,6 @@ fn use_zst() -> A { fn main() { assert_eq!(zst_ret(), A); assert_eq!(use_zst(), A); - assert_eq!(&A as *const A as *const (), &() as *const _); - assert_eq!(&A as *const A, &A as *const A); let x = 42 as *mut (); unsafe { *x = (); } } From c4fc6c677d4de82e67e1df7e5cf4314dde514510 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Jun 2017 13:30:31 +0200 Subject: [PATCH 1019/1096] Typo --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 91bd0d959ff29..0c23038c3c354 100644 --- a/src/error.rs +++ b/src/error.rs @@ -91,7 +91,7 @@ impl<'tcx> Error for EvalError<'tcx> { EvalError::ReadBytesAsPointer => "a memory access tried to interpret some bytes as a pointer", EvalError::InvalidPointerMath => - "attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. compating pointers into different allocations", + "attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations", EvalError::ReadUndefBytes => "attempted to read undefined bytes", EvalError::DeadLocal => From 5ee4fdcd15cc404f6d195027b11c24bdb0905204 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Jun 2017 13:31:00 +0200 Subject: [PATCH 1020/1096] fetch_tls_dtor "read" an `Undef` as nonzero --- src/eval_context.rs | 6 +++++- src/memory.rs | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 0a2bce922df50..7052a411f7e2c 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -362,7 +362,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StackPopCleanup::None => {}, StackPopCleanup::Tls(key) => { // either fetch the next dtor or start new from the beginning, if any are left with a non-null data - if let Some((instance, ptr, key)) = self.memory.fetch_tls_dtor(key).or_else(|| self.memory.fetch_tls_dtor(None)) { + let dtor = match self.memory.fetch_tls_dtor(key)? { + dtor @ Some(_) => dtor, + None => self.memory.fetch_tls_dtor(None)?, + }; + if let Some((instance, ptr, key)) = dtor { trace!("Running TLS dtor {:?} on {:?}", instance, ptr); // TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs let mir = self.load_mir(instance.def)?; diff --git a/src/memory.rs b/src/memory.rs index e34737f464de4..5794dc783cfed 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -404,22 +404,22 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// with associated destructors, implementations may stop calling destructors, /// or they may continue calling destructors until no non-NULL values with /// associated destructors exist, even though this might result in an infinite loop. - pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> Option<(ty::Instance<'tcx>, PrimVal, TlsKey)> { + pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, PrimVal, TlsKey)>> { use std::collections::Bound::*; let start = match key { Some(key) => Excluded(key), None => Unbounded, }; for (&key, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.range_mut((start, Unbounded)) { - if *data != PrimVal::Bytes(0) { + if !data.is_null()? { if let Some(dtor) = dtor { let ret = Some((dtor, *data, key)); *data = PrimVal::Bytes(0); - return ret; + return Ok(ret); } } } - return None; + return Ok(None); } } From eca9e3429ad55bdc55fa12e69cab8c2cf54d0ccd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Jun 2017 13:32:15 +0200 Subject: [PATCH 1021/1096] PrimVal used to allow comparing `Undef` --- src/lvalue.rs | 2 +- src/operator.rs | 16 ++++++++++++++-- src/value.rs | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 9205e0c299bd2..8492019a9bf89 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -8,7 +8,7 @@ use eval_context::{EvalContext}; use memory::Pointer; use value::{PrimVal, Value}; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug)] pub enum Lvalue<'tcx> { /// An lvalue referring to a value allocated in the `Memory` system. Ptr { diff --git a/src/operator.rs b/src/operator.rs index ed69c8043939b..e5ed99b24341a 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -159,10 +159,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, // These work on anything Eq if left_kind == right_kind => { - return Ok((PrimVal::from_bool(left == right), false)); + let result = match (left, right) { + (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right, + (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right, + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), + _ => false, + }; + return Ok((PrimVal::from_bool(result), false)); } Ne if left_kind == right_kind => { - return Ok((PrimVal::from_bool(left != right), false)); + let result = match (left, right) { + (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right, + (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right, + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), + _ => true, + }; + return Ok((PrimVal::from_bool(result), false)); } // These need both pointers to be in the same allocation Lt | Le | Gt | Ge | Sub diff --git a/src/value.rs b/src/value.rs index 9f7d3eafe1fb7..85f79b7831e18 100644 --- a/src/value.rs +++ b/src/value.rs @@ -42,7 +42,7 @@ pub enum Value { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes /// of a simple value, a pointer into another `Allocation`, or be undefined. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug)] pub enum PrimVal { /// The raw bytes of a simple value. Bytes(u128), From 377fcce9b8ab548f8b42dd7b39461de2aa9ad38a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Jun 2017 16:41:56 +0200 Subject: [PATCH 1022/1096] Ensure tests run with and without full MIR --- tests/compile-fail/zst2.rs | 5 ++++- tests/compile-fail/zst3.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/compile-fail/zst2.rs b/tests/compile-fail/zst2.rs index dd619377b9f66..1bf42062de6c2 100644 --- a/tests/compile-fail/zst2.rs +++ b/tests/compile-fail/zst2.rs @@ -4,5 +4,8 @@ struct A; fn main() { - assert_eq!(&A as *const A as *const (), &() as *const _); + // can't use assert_eq, b/c that will try to print the pointer addresses with full MIR enabled + if &A as *const A as *const () != &() as *const _ { + panic!() + } } diff --git a/tests/compile-fail/zst3.rs b/tests/compile-fail/zst3.rs index 561938e4e9dbf..55cdd661d3d43 100644 --- a/tests/compile-fail/zst3.rs +++ b/tests/compile-fail/zst3.rs @@ -4,5 +4,8 @@ struct A; fn main() { - assert_eq!(&A as *const A, &A as *const A); + // can't use assert_eq, b/c that will try to print the pointer addresses with full MIR enabled + if &A as *const A != &A as *const A { + panic!(); + } } From 4aca1d0e0bb880377eabd662510eb9d0170511dd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Sat, 24 Jun 2017 12:46:35 +0200 Subject: [PATCH 1023/1096] Make zst compile-fail tests more readable --- tests/compile-fail/zst2.rs | 4 +--- tests/compile-fail/zst3.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/compile-fail/zst2.rs b/tests/compile-fail/zst2.rs index 1bf42062de6c2..dd826c2fd74eb 100644 --- a/tests/compile-fail/zst2.rs +++ b/tests/compile-fail/zst2.rs @@ -5,7 +5,5 @@ struct A; fn main() { // can't use assert_eq, b/c that will try to print the pointer addresses with full MIR enabled - if &A as *const A as *const () != &() as *const _ { - panic!() - } + assert!(&A as *const A as *const () == &() as *const _) } diff --git a/tests/compile-fail/zst3.rs b/tests/compile-fail/zst3.rs index 55cdd661d3d43..53c42995b8a12 100644 --- a/tests/compile-fail/zst3.rs +++ b/tests/compile-fail/zst3.rs @@ -5,7 +5,5 @@ struct A; fn main() { // can't use assert_eq, b/c that will try to print the pointer addresses with full MIR enabled - if &A as *const A != &A as *const A { - panic!(); - } + assert!(&A as *const A == &A as *const A); } From 7b7f690274683777e810bfb61aae2af2e36aaab8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 17:47:09 -0700 Subject: [PATCH 1024/1096] Make sure that casting a ptr-integer down to u8 makes it unusable --- tests/compile-fail/ptr_int_cast.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/compile-fail/ptr_int_cast.rs diff --git a/tests/compile-fail/ptr_int_cast.rs b/tests/compile-fail/ptr_int_cast.rs new file mode 100644 index 0000000000000..b004a18ff4658 --- /dev/null +++ b/tests/compile-fail/ptr_int_cast.rs @@ -0,0 +1,7 @@ +fn main() { + let x = &1; + // Casting down to u8 and back up to a pointer loses too much precision; this must not work. + let x = x as *const i32 as u8; + let x = x as *const i32; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes + let _ = unsafe { *x }; +} From f0c8df2291f2f727a2886cbb3f239d6261884722 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 18:41:10 -0700 Subject: [PATCH 1025/1096] handle ptr-int casts explicitly in cast.rs --- src/cast.rs | 23 +++++++++++++++++++---- src/error.rs | 6 +++--- src/eval_context.rs | 20 +++++--------------- tests/compile-fail/ptr_int_cast.rs | 5 +++-- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index aa24de944a7c0..cb0b112170987 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -19,9 +19,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { F32 => self.cast_float(val.to_f32()? as f64, dest_ty), F64 => self.cast_float(val.to_f64()?, dest_ty), - I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty), - - Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false), + I8 | I16 | I32 | I64 | I128 => { + if val.is_ptr() { + self.cast_ptr(val, dest_ty) + } else { + self.cast_signed_int(val.to_i128()?, dest_ty) + } + }, + + Bool | Char | U8 | U16 | U32 | U64 | U128 => { + if val.is_ptr() { + self.cast_ptr(val, dest_ty) + } else { + self.cast_int(val.to_u128()?, dest_ty, false) + } + }, FnPtr | Ptr => self.cast_ptr(val, dest_ty), } @@ -70,6 +82,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), + // No alignment check needed for raw pointers TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), @@ -94,8 +107,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { - TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) => + // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here. + TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) => Ok(ptr), + TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/error.rs b/src/error.rs index 0c23038c3c354..919232d9eef70 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum EvalError<'tcx> { access: bool, allocation_size: u64, }, - NullPointerOutOfBounds, + InvalidNullPointerUsage, ReadPointerAsBytes, ReadBytesAsPointer, InvalidPointerMath, @@ -84,8 +84,8 @@ impl<'tcx> Error for EvalError<'tcx> { "invalid enum discriminant value read", EvalError::PointerOutOfBounds { .. } => "pointer offset outside bounds of allocation", - EvalError::NullPointerOutOfBounds => - "invalid NULL pointer offset", + EvalError::InvalidNullPointerUsage => + "invalid use of NULL pointer", EvalError::ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", EvalError::ReadBytesAsPointer => diff --git a/src/eval_context.rs b/src/eval_context.rs index 7052a411f7e2c..ece2b37209c5d 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -717,19 +717,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { - // First, try casting - let dest_val = self.value_to_primval(src, src_ty).and_then( - |src_val| { self.cast_primval(src_val, src_ty, dest_ty) }) - // Alternatively, if the sizes are equal, try just reading at the target type - .or_else(|err| { - let size = self.type_size(src_ty)?; - if size.is_some() && size == self.type_size(dest_ty)? { - self.value_to_primval(src, dest_ty) - } else { - Err(err) - } - }); - self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?; + let src_val = self.value_to_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?; + self.write_value(Value::ByVal(dest_val), dest, dest_ty)?; } } @@ -908,7 +898,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // allocation. if ptr.is_null()? { // NULL pointers must only be offset by 0 - return if offset == 0 { Ok(ptr) } else { Err(EvalError::NullPointerOutOfBounds) }; + return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) }; } // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; @@ -919,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.check_bounds(ptr, false)?; } else if ptr.is_null()? { // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. - return Err(EvalError::NullPointerOutOfBounds); + return Err(EvalError::InvalidNullPointerUsage); } Ok(ptr) } else { diff --git a/tests/compile-fail/ptr_int_cast.rs b/tests/compile-fail/ptr_int_cast.rs index b004a18ff4658..396c71ebb03d1 100644 --- a/tests/compile-fail/ptr_int_cast.rs +++ b/tests/compile-fail/ptr_int_cast.rs @@ -1,7 +1,8 @@ fn main() { let x = &1; // Casting down to u8 and back up to a pointer loses too much precision; this must not work. - let x = x as *const i32 as u8; - let x = x as *const i32; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes + let x = x as *const i32; + let x = x as u8; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes + let x = x as *const i32; let _ = unsafe { *x }; } From b1acc130bb96fdce35ab50c0b64a8a6d470ba2a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 21:01:24 -0700 Subject: [PATCH 1026/1096] check alignment of pointers on Ref --- src/eval_context.rs | 21 +++++++++++++++++++ tests/compile-fail/int_ptr_cast.rs | 5 +++++ tests/compile-fail/int_ptr_cast2.rs | 5 +++++ tests/compile-fail/reference_to_packed.rs | 6 +++--- .../reference_to_packed_unsafe.rs | 4 ++-- tests/compile-fail/unaligned_ptr_cast.rs | 6 ++++++ 6 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/compile-fail/int_ptr_cast.rs create mode 100644 tests/compile-fail/int_ptr_cast2.rs create mode 100644 tests/compile-fail/unaligned_ptr_cast.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index ece2b37209c5d..c08fb24c03fcb 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -664,6 +664,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); + let ty = self.lvalue_ty(lvalue); let val = match extra { LvalueExtra::None => Value::ByVal(ptr), @@ -673,6 +674,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { bug!("attempted to take a reference to an enum downcast lvalue"), }; + // Check alignment and non-NULLness. + let (_, align) = self.size_and_align_of_dst(ty, val)?; + if ptr.is_ptr() { + let ptr = ptr.to_ptr()?; + if !ptr.points_to_zst() { // assume ZST pointer to be always fully alignd (and anyway ZST pointers are going to disappear soon) + self.memory.check_align(ptr, align, 0)?; + } + } else { + let v = (ptr.to_u128()? % (1 << self.memory.pointer_size())) as u64; + if v == 0 { + return Err(EvalError::InvalidNullPointerUsage); + } + if v % align != 0 { + return Err(EvalError::AlignmentCheckFailed { + has: v % align, + required: align, + }); + } + } + self.write_value(val, dest, dest_ty)?; } diff --git a/tests/compile-fail/int_ptr_cast.rs b/tests/compile-fail/int_ptr_cast.rs new file mode 100644 index 0000000000000..ae5f65a7166c6 --- /dev/null +++ b/tests/compile-fail/int_ptr_cast.rs @@ -0,0 +1,5 @@ +fn main() { + let x = 2usize as *const u32; + // This must fail because alignment is violated + let _ = unsafe { &*x }; //~ ERROR: tried to access memory with alignment 2, but alignment 4 is required +} diff --git a/tests/compile-fail/int_ptr_cast2.rs b/tests/compile-fail/int_ptr_cast2.rs new file mode 100644 index 0000000000000..1897066f7bcc3 --- /dev/null +++ b/tests/compile-fail/int_ptr_cast2.rs @@ -0,0 +1,5 @@ +fn main() { + let x = 0usize as *const u32; + // This must fail because the pointer is NULL + let _ = unsafe { &*x }; //~ ERROR: invalid use of NULL pointer +} diff --git a/tests/compile-fail/reference_to_packed.rs b/tests/compile-fail/reference_to_packed.rs index 119225f3e369d..4cf353298b9e8 100644 --- a/tests/compile-fail/reference_to_packed.rs +++ b/tests/compile-fail/reference_to_packed.rs @@ -11,6 +11,6 @@ fn main() { x: 42, y: 99, }; - let p = &foo.x; - let i = *p; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required -} \ No newline at end of file + let p = &foo.x; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required + let i = *p; +} diff --git a/tests/compile-fail/reference_to_packed_unsafe.rs b/tests/compile-fail/reference_to_packed_unsafe.rs index 5761f23b7dd41..b5893cd101971 100644 --- a/tests/compile-fail/reference_to_packed_unsafe.rs +++ b/tests/compile-fail/reference_to_packed_unsafe.rs @@ -11,6 +11,6 @@ fn main() { x: 42, y: 99, }; - let p: *const i32 = &foo.x; - let x = unsafe { *p + foo.x }; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required + let p: *const i32 = &foo.x; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required + let x = unsafe { *p + foo.x }; } diff --git a/tests/compile-fail/unaligned_ptr_cast.rs b/tests/compile-fail/unaligned_ptr_cast.rs new file mode 100644 index 0000000000000..fcab430f8fcbc --- /dev/null +++ b/tests/compile-fail/unaligned_ptr_cast.rs @@ -0,0 +1,6 @@ +fn main() { + let x = &2u16; + let x = x as *const _ as *const u32; + // This must fail because alignment is violated + let _ = unsafe { &*x }; //~ ERROR: tried to access memory with alignment 2, but alignment 4 is required +} From fbc00ddc95debb5e6ea962074cc4213d40664ddc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Jun 2017 22:56:49 -0700 Subject: [PATCH 1027/1096] make sure that 'identity casting' works --- tests/run-pass/ptr_int_casts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs index b7b17089efc7d..88fb16e069ec9 100644 --- a/tests/run-pass/ptr_int_casts.rs +++ b/tests/run-pass/ptr_int_casts.rs @@ -13,7 +13,7 @@ fn main() { { // ptr-int-ptr let x = 13; - let mut y = &x as *const _ as usize; + let mut y = &x as &_ as *const _ as usize; y += 13; y -= 13; let y = y as *const _; @@ -22,7 +22,7 @@ fn main() { { // fnptr-int-fnptr let x : fn() -> i32 = f; - let y : *mut u8 = unsafe { mem::transmute(x) }; + let y : *mut u8 = unsafe { mem::transmute(x as fn() -> i32) }; let mut y = y as usize; y += 13; y -= 13; From 38d03392fae1011ec2c5a860e65b77927081041b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 23 Jun 2017 11:12:17 -0700 Subject: [PATCH 1028/1096] Clarify pattern matching --- src/eval_context.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index c08fb24c03fcb..7f30b8b99538b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -676,21 +676,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Check alignment and non-NULLness. let (_, align) = self.size_and_align_of_dst(ty, val)?; - if ptr.is_ptr() { - let ptr = ptr.to_ptr()?; - if !ptr.points_to_zst() { // assume ZST pointer to be always fully alignd (and anyway ZST pointers are going to disappear soon) - self.memory.check_align(ptr, align, 0)?; + match ptr { + PrimVal::Ptr(ptr) => { + if !ptr.points_to_zst() { // assume ZST pointer to be always fully alignd (and anyway ZST pointers are going to disappear soon) + self.memory.check_align(ptr, align, 0)?; + } } - } else { - let v = (ptr.to_u128()? % (1 << self.memory.pointer_size())) as u64; - if v == 0 { - return Err(EvalError::InvalidNullPointerUsage); + PrimVal::Bytes(bytes) => { + let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64; + if v == 0 { + return Err(EvalError::InvalidNullPointerUsage); + } + if v % align != 0 { + return Err(EvalError::AlignmentCheckFailed { + has: v % align, + required: align, + }); + } } - if v % align != 0 { - return Err(EvalError::AlignmentCheckFailed { - has: v % align, - required: align, - }); + PrimVal::Undef => { + return Err(EvalError::ReadUndefBytes); } } From 4e90e3bcf674a38186b1edef25875466cecf1e5a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 23 Jun 2017 14:13:06 -0700 Subject: [PATCH 1029/1096] remove redundant test --- tests/compile-fail/reference_to_packed_unsafe.rs | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 tests/compile-fail/reference_to_packed_unsafe.rs diff --git a/tests/compile-fail/reference_to_packed_unsafe.rs b/tests/compile-fail/reference_to_packed_unsafe.rs deleted file mode 100644 index b5893cd101971..0000000000000 --- a/tests/compile-fail/reference_to_packed_unsafe.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![allow(dead_code, unused_variables)] - -#[repr(packed)] -struct Foo { - x: i32, - y: i32, -} - -fn main() { - let foo = Foo { - x: 42, - y: 99, - }; - let p: *const i32 = &foo.x; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required - let x = unsafe { *p + foo.x }; -} From 76a1d66e77abe7ce7e32796fcf4283dab0c5c763 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 24 Jun 2017 11:05:53 -0700 Subject: [PATCH 1030/1096] fix build after rebase --- src/eval_context.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 7f30b8b99538b..2c27dea2843b0 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -678,9 +678,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (_, align) = self.size_and_align_of_dst(ty, val)?; match ptr { PrimVal::Ptr(ptr) => { - if !ptr.points_to_zst() { // assume ZST pointer to be always fully alignd (and anyway ZST pointers are going to disappear soon) - self.memory.check_align(ptr, align, 0)?; - } + self.memory.check_align(ptr, align, 0)?; } PrimVal::Bytes(bytes) => { let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64; From ab400f3eea3dc934f924d27e5b881ff76a9b8182 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Jun 2017 17:58:47 +0200 Subject: [PATCH 1031/1096] Initial work towards checking const eval rules in miri --- src/error.rs | 10 ++++++++++ src/eval_context.rs | 15 +++++++++++++++ src/operator.rs | 7 +++++++ src/terminator/mod.rs | 14 ++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/src/error.rs b/src/error.rs index 919232d9eef70..38b64870f89b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -63,6 +63,8 @@ pub enum EvalError<'tcx> { HeapAllocNonPowerOfTwoAlignment(u64), Unreachable, Panic, + NeedsRfc(String), + NotConst(String), } pub type EvalResult<'tcx, T = ()> = Result>; @@ -156,6 +158,10 @@ impl<'tcx> Error for EvalError<'tcx> { "entered unreachable code", EvalError::Panic => "the evaluated program panicked", + EvalError::NeedsRfc(_) => + "this feature needs an rfc before being allowed inside constants", + EvalError::NotConst(_) => + "this feature is not compatible with constant evaluation", } } @@ -191,6 +197,10 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "expected primitive type, got {}", ty), EvalError::Layout(ref err) => write!(f, "rustc layout computation failed: {:?}", err), + EvalError::NeedsRfc(ref msg) => + write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), + EvalError::NotConst(ref msg) => + write!(f, "Cannot evaluate within constants: \"{}\"", msg), _ => write!(f, "{}", self.description()), } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 2c27dea2843b0..74b0e6ec0689b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -655,6 +655,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Len(ref lvalue) => { + if self.frame().const_env() { + return Err(EvalError::NeedsRfc("computing the length of arrays".to_string())); + } let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); @@ -701,6 +704,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } NullaryOp(mir::NullOp::Box, ty) => { + if self.frame().const_env() { + return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string())); + } // FIXME: call the `exchange_malloc` lang item if available if self.type_size(ty)?.expect("box only works with sized types") == 0 { let align = self.type_align(ty)?; @@ -712,6 +718,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } NullaryOp(mir::NullOp::SizeOf, ty) => { + if self.frame().const_env() { + return Err(EvalError::NeedsRfc("computing the size of types (size_of)".to_string())); + } let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type"); self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?; } @@ -1583,6 +1592,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { + pub fn const_env(&self) -> bool { + match self.return_to_block { + StackPopCleanup::MarkStatic(_) => true, + _ => false, + } + } pub fn get_local(&self, local: mir::Local, field: Option) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. if let Some(field) = field { diff --git a/src/operator.rs b/src/operator.rs index e5ed99b24341a..09058c23886e1 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -151,6 +151,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); let isize = PrimValKind::from_int_size(self.memory.pointer_size()); if !left_kind.is_float() && !right_kind.is_float() { + if (!left.is_bytes() && !right.is_bytes()) && self.frame().const_env() { + if left.is_ptr() && right.is_ptr() { + return Err(EvalError::NotConst("Comparing pointers".to_string())); + } else { + return Err(EvalError::NeedsRfc("Comparing Pointers integers with pointers".to_string())); + } + } match bin_op { Offset if left_kind == Ptr && right_kind == usize => { let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c773620cbb581..049b66c67a48e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -37,6 +37,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), SwitchInt { ref discr, ref values, ref targets, .. } => { + if self.frame().const_env() { + return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string())); + } let discr_val = self.eval_operand(discr)?; let discr_ty = self.operand_ty(discr); let discr_prim = self.value_to_primval(discr_val, discr_ty)?; @@ -92,6 +95,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Drop { ref location, target, .. } => { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); + if self.frame().const_env() { + return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string())); + } let lval = self.eval_lvalue(location)?; let ty = self.lvalue_ty(location); self.goto_block(target); @@ -424,11 +430,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { + if self.frame().const_env() { + return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path))); + } self.call_missing_fn(instance, destination, arg_operands, sig, path)?; return Ok(true); }, Err(other) => return Err(other), }; + + if self.frame().const_env() && !self.tcx.is_const_fn(instance.def_id()) { + return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance))); + } + let (return_lvalue, return_to_block) = match destination { Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)), None => (Lvalue::undef(), StackPopCleanup::None), From 5dfaacf310bcec345ec38ee76d509e3e23810a95 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Jun 2017 13:36:41 +0200 Subject: [PATCH 1032/1096] Simplify all the code --- src/eval_context.rs | 97 +++++++---------------- src/lvalue.rs | 51 +----------- src/step.rs | 2 +- src/terminator/intrinsic.rs | 4 +- tests/compile-fail/pointer_byte_read_1.rs | 2 +- 5 files changed, 37 insertions(+), 119 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 74b0e6ec0689b..958cc688f8acc 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1003,12 +1003,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lvalue: Lvalue<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { - Lvalue::Local { frame, local, field } => { + Lvalue::Local { frame, local } => { // -1 since we don't store the return value match self.stack[frame].locals[local.index() - 1] { None => return Err(EvalError::DeadLocal), Some(Value::ByRef(ptr)) => { - assert!(field.is_none()); Lvalue::from_ptr(ptr) }, Some(val) => { @@ -1018,12 +1017,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.alloc_ptr_with_substs(ty, substs)?; self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr)); // it stays live self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; - let lval = Lvalue::from_ptr(ptr); - if let Some((field, field_ty)) = field { - self.lvalue_field(lval, field, ty, field_ty)? - } else { - lval - } + Lvalue::from_ptr(ptr) } } } @@ -1110,11 +1104,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(src_val, ptr, dest_ty) } - Lvalue::Local { frame, local, field } => { - let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i))?; + Lvalue::Local { frame, local } => { + let dest = self.stack[frame].get_local(local)?; self.write_value_possibly_by_val( src_val, - |this, val| this.stack[frame].set_local(local, field.map(|(i, _)| i), val), + |this, val| this.stack[frame].set_local(local, val), dest, dest_ty, ) @@ -1353,7 +1347,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I128 => 16, Is => self.memory.pointer_size(), }; - PrimVal::from_i128(self.memory.read_int(ptr, size)?) + // if we cast a ptr to a usize reading it back into a primval shouldn't panic + // Due to read_ptr ignoring the sign, we need to jump around some hoops + match self.memory.read_int(ptr, size) { + Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr)?, + other => PrimVal::from_i128(other?), + } } ty::TyUint(uint_ty) => { @@ -1366,7 +1365,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U128 => 16, Us => self.memory.pointer_size(), }; - PrimVal::from_u128(self.memory.read_uint(ptr, size)?) + if size == self.memory.pointer_size() { + // if we cast a ptr to a usize reading it back into a primval shouldn't panic + self.memory.read_ptr(ptr)? + } else { + PrimVal::from_u128(self.memory.read_uint(ptr, size)?) + } } ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), @@ -1518,19 +1522,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { // Debug output - if let Lvalue::Local { frame, local, field } = lvalue { + if let Lvalue::Local { frame, local } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); - if let Some((field, _)) = field { - write!(msg, ".{}", field).unwrap(); - } let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } write!(msg, ":").unwrap(); - match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { + match self.stack[frame].get_local(local) { Err(EvalError::DeadLocal) => { write!(msg, " is dead").unwrap(); } @@ -1575,14 +1576,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, frame: usize, local: mir::Local, - field: Option, f: F, ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let val = self.stack[frame].get_local(local, field)?; + let val = self.stack[frame].get_local(local)?; let new_val = f(self, val)?; - self.stack[frame].set_local(local, field, new_val)?; + self.stack[frame].set_local(local, new_val)?; // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; @@ -1598,59 +1598,20 @@ impl<'tcx> Frame<'tcx> { _ => false, } } - pub fn get_local(&self, local: mir::Local, field: Option) -> EvalResult<'tcx, Value> { + pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - if let Some(field) = field { - Ok(match self.locals[local.index() - 1] { - None => return Err(EvalError::DeadLocal), - Some(Value::ByRef(_)) => bug!("can't have lvalue fields for ByRef"), - Some(val @ Value::ByVal(_)) => { - assert_eq!(field, 0); - val - }, - Some(Value::ByValPair(a, b)) => { - match field { - 0 => Value::ByVal(a), - 1 => Value::ByVal(b), - _ => bug!("ByValPair has only two fields, tried to access {}", field), - } - }, - }) - } else { - self.locals[local.index() - 1].ok_or(EvalError::DeadLocal) - } + self.locals[local.index() - 1].ok_or(EvalError::DeadLocal) } - fn set_local(&mut self, local: mir::Local, field: Option, value: Value) -> EvalResult<'tcx> { + fn set_local(&mut self, local: mir::Local, value: Value) -> EvalResult<'tcx> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - if let Some(field) = field { - match self.locals[local.index() - 1] { - None => return Err(EvalError::DeadLocal), - Some(Value::ByRef(_)) => bug!("can't have lvalue fields for ByRef"), - Some(Value::ByVal(_)) => { - assert_eq!(field, 0); - self.set_local(local, None, value)?; - }, - Some(Value::ByValPair(a, b)) => { - let prim = match value { - Value::ByRef(_) => bug!("can't set ValPair field to ByRef"), - Value::ByVal(val) => val, - Value::ByValPair(_, _) => bug!("can't set ValPair field to ValPair"), - }; - match field { - 0 => self.set_local(local, None, Value::ByValPair(prim, b))?, - 1 => self.set_local(local, None, Value::ByValPair(a, prim))?, - _ => bug!("ByValPair has only two fields, tried to access {}", field), - } - }, - } - } else { - match self.locals[local.index() - 1] { - None => return Err(EvalError::DeadLocal), - Some(ref mut local) => { *local = value; } + match self.locals[local.index() - 1] { + None => Err(EvalError::DeadLocal), + Some(ref mut local) => { + *local = value; + Ok(()) } } - return Ok(()); } pub fn storage_live(&mut self, local: mir::Local) -> EvalResult<'tcx, Option> { diff --git a/src/lvalue.rs b/src/lvalue.rs index 8492019a9bf89..93646ba531fdc 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -24,8 +24,6 @@ pub enum Lvalue<'tcx> { Local { frame: usize, local: mir::Local, - /// Optionally, this lvalue can point to a field of the stack value - field: Option<(usize, Ty<'tcx>)>, }, /// An lvalue referring to a global @@ -141,8 +139,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(extra, LvalueExtra::None); Ok(Value::ByRef(ptr.to_ptr()?)) } - Lvalue::Local { frame, local, field } => { - self.stack[frame].get_local(local, field.map(|(i, _)| i)) + Lvalue::Local { frame, local } => { + self.stack[frame].get_local(local) } Lvalue::Global(cid) => { Ok(self.globals.get(&cid).expect("global not cached").value) @@ -154,7 +152,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, + Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local }, Static(ref static_) => { let instance = ty::Instance::mono(self.tcx, static_.def_id); @@ -235,48 +233,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let (base_ptr, base_extra) = match base { - Lvalue::Ptr { ptr, extra } => (ptr, extra), - Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i))? { - Value::ByRef(ptr) => { - assert!(field.is_none(), "local can't be ByRef and have a field offset"); - (PrimVal::Ptr(ptr), LvalueExtra::None) - }, - Value::ByVal(PrimVal::Undef) => { - // FIXME: allocate in fewer cases - if self.ty_to_primval_kind(base_ty).is_ok() { - return Ok(base); - } else { - (PrimVal::Ptr(self.force_allocation(base)?.to_ptr()?), LvalueExtra::None) - } - }, - Value::ByVal(_) => { - if self.get_field_count(base_ty)? == 1 { - assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); - return Ok(base); - } - // this branch is taken when a union creates a large ByVal which is then - // accessed as a struct with multiple small fields - (PrimVal::Ptr(self.force_allocation(base)?.to_ptr()?), LvalueExtra::None) - }, - Value::ByValPair(_, _) => { - let field_count = self.get_field_count(base_ty)?; - if field_count == 1 { - assert_eq!(field_index, 0, "{:?} has only one field", base_ty); - return Ok(base); - } - assert_eq!(field_count, 2); - assert!(field_index < 2); - return Ok(Lvalue::Local { - frame, - local, - field: Some((field_index, field_ty)), - }); - }, - }, - // FIXME: do for globals what we did for locals - Lvalue::Global(_) => self.force_allocation(base)?.to_ptr_and_extra(), - }; + let (base_ptr, base_extra) = self.force_allocation(base)?.to_ptr_and_extra(); let offset = match base_extra { LvalueExtra::Vtable(tab) => { diff --git a/src/step.rs b/src/step.rs index 48b9fecd3d39e..e2e0d81d0607a 100644 --- a/src/step.rs +++ b/src/step.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Mark locals as dead or alive. StorageLive(ref lvalue) | StorageDead(ref lvalue)=> { let (frame, local) = match self.eval_lvalue(lvalue)? { - Lvalue::Local{ frame, local, field: None } if self.stack.len() == frame+1 => (frame, local), + Lvalue::Local{ frame, local } if self.stack.len() == frame+1 => (frame, local), _ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type }; let old_val = match stmt.kind { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 5fd0cc5802203..360842ac5475a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -269,7 +269,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(zero_val) }; match dest { - Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?, + Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr.to_ptr()?, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, @@ -419,7 +419,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; match dest { - Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, + Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), diff --git a/tests/compile-fail/pointer_byte_read_1.rs b/tests/compile-fail/pointer_byte_read_1.rs index 285a0684a93ea..342eb28a970fc 100644 --- a/tests/compile-fail/pointer_byte_read_1.rs +++ b/tests/compile-fail/pointer_byte_read_1.rs @@ -3,5 +3,5 @@ fn main() { let y = &x; let z = &y as *const &i32 as *const usize; let ptr_bytes = unsafe { *z }; // the actual deref is fine, because we read the entire pointer at once - let _ = ptr_bytes == 15; //~ ERROR: tried to access part of a pointer value as raw bytes + let _ = ptr_bytes % 432; //~ ERROR: tried to access part of a pointer value as raw bytes } From 54821102b41b79ca57206073faccf8e9930ff046 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Tue, 27 Jun 2017 21:31:38 -0400 Subject: [PATCH 1033/1096] update tests for new error message --- tests/compile-fail/cast_fn_ptr_unsafe.rs | 2 +- tests/compile-fail/cast_fn_ptr_unsafe2.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compile-fail/cast_fn_ptr_unsafe.rs b/tests/compile-fail/cast_fn_ptr_unsafe.rs index 225cd1391bc36..568681da3c5d3 100644 --- a/tests/compile-fail/cast_fn_ptr_unsafe.rs +++ b/tests/compile-fail/cast_fn_ptr_unsafe.rs @@ -2,7 +2,7 @@ fn main() { fn f() {} - let g = f as fn() as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `unsafe fn(i32)` + let g = f as fn() as unsafe fn(i32); //~ERROR: non-primitive cast: `fn()` as `unsafe fn(i32)` unsafe { g(42); diff --git a/tests/compile-fail/cast_fn_ptr_unsafe2.rs b/tests/compile-fail/cast_fn_ptr_unsafe2.rs index c3a2fb9556f82..314365939fe80 100644 --- a/tests/compile-fail/cast_fn_ptr_unsafe2.rs +++ b/tests/compile-fail/cast_fn_ptr_unsafe2.rs @@ -2,7 +2,7 @@ fn main() { fn f() {} - let g = f as fn() as fn(i32) as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `fn(i32)` + let g = f as fn() as fn(i32) as unsafe fn(i32); //~ERROR: non-primitive cast: `fn()` as `fn(i32)` unsafe { g(42); From a724a393235a215864951b90d1ce143ac7b5e4b6 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 09:46:41 +0200 Subject: [PATCH 1034/1096] Reword comments --- src/eval_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 958cc688f8acc..9db5a566bd89b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1347,7 +1347,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I128 => 16, Is => self.memory.pointer_size(), }; - // if we cast a ptr to a usize reading it back into a primval shouldn't panic + // if we cast a ptr to an isize, reading it back into a primval shouldn't panic // Due to read_ptr ignoring the sign, we need to jump around some hoops match self.memory.read_int(ptr, size) { Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr)?, @@ -1366,7 +1366,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Us => self.memory.pointer_size(), }; if size == self.memory.pointer_size() { - // if we cast a ptr to a usize reading it back into a primval shouldn't panic + // if we cast a ptr to an usize, reading it back into a primval shouldn't panic self.memory.read_ptr(ptr)? } else { PrimVal::from_u128(self.memory.read_uint(ptr, size)?) From c8079c652ce6bffd26dd3c410af432e656245d00 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 09:40:25 +0200 Subject: [PATCH 1035/1096] Address comments --- src/eval_context.rs | 26 +++++++++++++++++--------- src/operator.rs | 8 ++------ src/terminator/mod.rs | 8 ++++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9db5a566bd89b..023886bd790ce 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -169,6 +169,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.stack } + /// Returns true if the current frame or any parent frame is part of a ctfe. + /// + /// Used to disable features in const eval, which do not have a rfc enabling + /// them or which can't be written in a way that they produce the same output + /// that evaluating the code at runtime would produce. + pub fn const_env(&self) -> bool { + for frame in self.stack.iter().rev() { + if let StackPopCleanup::MarkStatic(_) = frame.return_to_block { + return true; + } + } + false + } + pub(crate) fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { let ptr = self.memory.allocate_cached(s.as_bytes())?; Ok(Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::from_u128(s.len() as u128))) @@ -655,7 +669,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Len(ref lvalue) => { - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc("computing the length of arrays".to_string())); } let src = self.eval_lvalue(lvalue)?; @@ -704,7 +718,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } NullaryOp(mir::NullOp::Box, ty) => { - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string())); } // FIXME: call the `exchange_malloc` lang item if available @@ -718,7 +732,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } NullaryOp(mir::NullOp::SizeOf, ty) => { - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc("computing the size of types (size_of)".to_string())); } let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type"); @@ -1592,12 +1606,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { - pub fn const_env(&self) -> bool { - match self.return_to_block { - StackPopCleanup::MarkStatic(_) => true, - _ => false, - } - } pub fn get_local(&self, local: mir::Local) -> EvalResult<'tcx, Value> { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. self.locals[local.index() - 1].ok_or(EvalError::DeadLocal) diff --git a/src/operator.rs b/src/operator.rs index 09058c23886e1..5386fa588a4b2 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -151,12 +151,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); let isize = PrimValKind::from_int_size(self.memory.pointer_size()); if !left_kind.is_float() && !right_kind.is_float() { - if (!left.is_bytes() && !right.is_bytes()) && self.frame().const_env() { - if left.is_ptr() && right.is_ptr() { - return Err(EvalError::NotConst("Comparing pointers".to_string())); - } else { - return Err(EvalError::NeedsRfc("Comparing Pointers integers with pointers".to_string())); - } + if (!left.is_bytes() && !right.is_bytes()) && self.const_env() { + return Err(EvalError::NeedsRfc("Pointer arithmetic or comparison".to_string())); } match bin_op { Offset if left_kind == Ptr && right_kind == usize => { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 049b66c67a48e..7d0d8fb161ed7 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -37,7 +37,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Goto { target } => self.goto_block(target), SwitchInt { ref discr, ref values, ref targets, .. } => { - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string())); } let discr_val = self.eval_operand(discr)?; @@ -95,7 +95,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Drop { ref location, target, .. } => { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string())); } let lval = self.eval_lvalue(location)?; @@ -430,7 +430,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { - if self.frame().const_env() { + if self.const_env() { return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path))); } self.call_missing_fn(instance, destination, arg_operands, sig, path)?; @@ -439,7 +439,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(other) => return Err(other), }; - if self.frame().const_env() && !self.tcx.is_const_fn(instance.def_id()) { + if self.const_env() && !self.tcx.is_const_fn(instance.def_id()) { return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance))); } From 91409f1d765487790a2d9223b25fc96d70ae513f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 13:10:50 +0200 Subject: [PATCH 1036/1096] Code nits --- src/memory.rs | 2 +- src/value.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 5794dc783cfed..638a8d2ff27c8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -772,7 +772,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { 4 => !0u32 as u128, 8 => !0u64 as u128, 16 => !0, - _ => bug!("unexpected PrimVal::Bytes size"), + n => bug!("unexpected PrimVal::Bytes size: {}", n), }; self.write_uint(dest.to_ptr()?, bytes & mask, size) } diff --git a/src/value.rs b/src/value.rs index 85f79b7831e18..90278d63e7f1c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -108,7 +108,7 @@ impl<'a, 'tcx: 'a> Value { assert_eq!(len as u64 as u128, len); Ok((ptr, len as u64)) }, - _ => unimplemented!(), + ByVal(_) => unimplemented!(), } } } From 917c89e6973ec7ea60896cb89ca298d636537ce9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 13:37:23 +0200 Subject: [PATCH 1037/1096] Optimize lvalue reads from Value::ByVal and Value::ByValPair --- src/error.rs | 3 ++ src/eval_context.rs | 22 ++++++++++--- src/lvalue.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 38b64870f89b9..f827784629d0f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,6 +65,7 @@ pub enum EvalError<'tcx> { Panic, NeedsRfc(String), NotConst(String), + ReadFromReturnPointer, } pub type EvalResult<'tcx, T = ()> = Result>; @@ -162,6 +163,8 @@ impl<'tcx> Error for EvalError<'tcx> { "this feature needs an rfc before being allowed inside constants", EvalError::NotConst(_) => "this feature is not compatible with constant evaluation", + EvalError::ReadFromReturnPointer => + "tried to read from the return pointer", } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 9db5a566bd89b..cbc2bf47f7a69 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -807,7 +807,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { + pub(super) fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ref tam) | ty::TyRef(_, ref tam) => !self.type_is_sized(tam.ty), @@ -868,6 +868,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match ty.sty { ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index), + ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { + use rustc::ty::layout::Layout::*; + match *self.type_layout(ty)? { + RawNullablePointer { nndiscr, .. } | + StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)), + _ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))), + } + } ty::TyAdt(adt_def, substs) => { Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) } @@ -876,6 +884,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index), + + ty::TyArray(ref inner, _) => Ok(inner), + _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } } @@ -902,14 +913,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { + pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> { let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; match *layout { - Univariant { ref variant, .. } => Ok(variant.offsets.len()), + Univariant { ref variant, .. } => Ok(variant.offsets.len() as u64), FatPointer { .. } => Ok(2), - StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()), + StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len() as u64), + Vector { count , .. } | + Array { count, .. } => Ok(count), + Scalar { .. } => Ok(0), _ => { let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout); Err(EvalError::Unimplemented(msg)) diff --git a/src/lvalue.rs b/src/lvalue.rs index 93646ba531fdc..d2d54b3d90297 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -126,8 +126,63 @@ impl<'tcx> Global<'tcx> { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { + /// Reads a value from the lvalue without going through the intermediate step of obtaining + /// a `miri::Lvalue` + pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option> { + use rustc::mir::Lvalue::*; + match *lvalue { + // Might allow this in the future, right now there's no way to do this from Rust code anyway + Local(mir::RETURN_POINTER) => Err(EvalError::ReadFromReturnPointer), + // Directly reading a local will always succeed + Local(local) => self.frame().get_local(local).map(Some), + // Directly reading a static will always succeed + Static(ref static_) => { + let instance = ty::Instance::mono(self.tcx, static_.def_id); + let cid = GlobalId { instance, promoted: None }; + Ok(Some(self.globals.get(&cid).expect("global not cached").value)) + }, + Projection(ref proj) => self.try_read_lvalue_projection(proj), + } + } + + fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, Option> { + use rustc::mir::ProjectionElem::*; + let base = match self.try_read_lvalue(&proj.base)? { + Some(base) => base, + None => return Ok(None), + }; + let base_ty = self.lvalue_ty(&proj.base); + match proj.elem { + Field(field, _) => match (field.index(), base) { + // the only field of a struct + (0, Value::ByVal(val)) => Ok(Some(Value::ByVal(val))), + // split fat pointers, 2 element tuples, ... + (0...1, Value::ByValPair(a, b)) if self.get_field_count(base_ty)? == 2 => { + let val = [a, b][field.index()]; + Ok(Some(Value::ByVal(val))) + }, + // the only field of a struct is a fat pointer + (0, Value::ByValPair(..)) => Ok(Some(base)), + _ => Ok(None), + }, + // The NullablePointer cases should work fine, need to take care for normal enums + Downcast(..) | + Subslice { .. } | + // reading index 0 or index 1 from a ByVal or ByVal pair could be optimized + ConstantIndex { .. } | Index(_) | + // No way to optimize this projection any better than the normal lvalue path + Deref => Ok(None), + } + } + pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { let ty = self.lvalue_ty(lvalue); + // Shortcut for things like accessing a fat pointer's field, + // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory + // and returning an `Lvalue::Ptr` to it + if let Some(val) = self.try_read_lvalue(lvalue)? { + return Ok(val); + } let lvalue = self.eval_lvalue(lvalue)?; if ty.is_never() { @@ -233,7 +288,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let (base_ptr, base_extra) = self.force_allocation(base)?.to_ptr_and_extra(); + // Do not allocate in trivial cases + let (base_ptr, base_extra) = match base { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? { + // in case the type has a single field, just return the value + Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => { + assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + }, + Value::ByRef(_) | + Value::ByValPair(..) | + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(), + }, + Lvalue::Global(cid) => match self.globals.get(&cid).expect("uncached global").value { + // in case the type has a single field, just return the value + Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => { + assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + }, + Value::ByRef(_) | + Value::ByValPair(..) | + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(), + }, + }; let offset = match base_extra { LvalueExtra::Vtable(tab) => { From 51b43215a48b79fc94661c32ad0e86497f18a94b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Jun 2017 11:37:15 -0700 Subject: [PATCH 1038/1096] cast -> transmute --- src/eval_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 9db5a566bd89b..0dc373f35e302 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1347,7 +1347,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { I128 => 16, Is => self.memory.pointer_size(), }; - // if we cast a ptr to an isize, reading it back into a primval shouldn't panic + // if we transmute a ptr to an isize, reading it back into a primval shouldn't panic // Due to read_ptr ignoring the sign, we need to jump around some hoops match self.memory.read_int(ptr, size) { Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr)?, @@ -1366,7 +1366,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Us => self.memory.pointer_size(), }; if size == self.memory.pointer_size() { - // if we cast a ptr to an usize, reading it back into a primval shouldn't panic + // if we transmute a ptr to an usize, reading it back into a primval shouldn't panic self.memory.read_ptr(ptr)? } else { PrimVal::from_u128(self.memory.read_uint(ptr, size)?) From 32e7dcb6fb22e0b33e7bc18c1f24b4ea2b5d719d Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Wed, 28 Jun 2017 21:24:17 -0400 Subject: [PATCH 1039/1096] update for upstream changes to TyFnDef --- src/eval_context.rs | 14 ++++++++------ src/terminator/mod.rs | 5 +++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index cdf2715995cea..dbad616f9196b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -771,7 +771,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ReifyFnPointer => match self.operand_ty(operand).sty { - ty::TyFnDef(def_id, substs, _) => { + ty::TyFnDef(def_id, substs) => { let instance = resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?; @@ -1686,7 +1686,7 @@ pub fn eval_main<'a, 'tcx: 'a>( let main_ptr = ecx.memory.create_fn_alloc(main_instance); let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; let main_ty = main_instance.def.def_ty(ecx.tcx); - let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig()); + let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig(ecx.tcx)); ecx.write_value(Value::ByVal(PrimVal::Ptr(main_ptr)), dest, main_ptr_ty)?; // Second argument (argc): 0 @@ -1830,7 +1830,7 @@ fn fn_once_adapter_instance<'a, 'tcx>( let self_ty = tcx.mk_closure_from_closure_substs( closure_did, substs); - let sig = tcx.closure_type(closure_did).subst(tcx, substs.substs); + let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs); let sig = tcx.erase_late_bound_regions_and_normalize(&sig); assert_eq!(sig.inputs().len(), 1); let substs = tcx.mk_substs([ @@ -1891,9 +1891,11 @@ pub fn resolve<'a, 'tcx>( } else { let item_type = def_ty(tcx, def_id, substs); let def = match item_type.sty { - ty::TyFnDef(_, _, f) if - f.abi() == Abi::RustIntrinsic || - f.abi() == Abi::PlatformIntrinsic => + ty::TyFnDef(..) if { + let f = item_type.fn_sig(tcx); + f.abi() == Abi::RustIntrinsic || + f.abi() == Abi::PlatformIntrinsic + } => { debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 7d0d8fb161ed7..534eb777e4145 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -72,7 +72,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let instance_ty = instance.def.def_ty(self.tcx); let instance_ty = self.monomorphize(instance_ty, instance.substs); match instance_ty.sty { - ty::TyFnDef(_, _, real_sig) => { + ty::TyFnDef(..) => { + let real_sig = instance_ty.fn_sig(self.tcx); let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); if !self.check_sig_compat(sig, real_sig)? { @@ -83,7 +84,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } (instance, sig) }, - ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig), + ty::TyFnDef(def_id, substs) => (::eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)), _ => { let msg = format!("can't handle callee of type {:?}", func_ty); return Err(EvalError::Unimplemented(msg)); From 73ab5c77c23a31e9009b06b1b3b0e452abc581be Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Thu, 29 Jun 2017 07:58:22 -0400 Subject: [PATCH 1040/1096] normalize signature before passing to check_sig_compat --- src/terminator/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 534eb777e4145..68d03b2aaffaa 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -76,6 +76,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let real_sig = instance_ty.fn_sig(self.tcx); let sig = self.erase_lifetimes(&sig); let real_sig = self.erase_lifetimes(&real_sig); + let real_sig = self.tcx.normalize_associated_type(&real_sig); if !self.check_sig_compat(sig, real_sig)? { return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig)); } From e3fa4fb84950a5ace71f587168642343206a6393 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Thu, 29 Jun 2017 13:06:36 -0400 Subject: [PATCH 1041/1096] get cargo-miri to work --- src/bin/cargo-miri.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs index f67bbd39a7482..6eff6650fa9c4 100644 --- a/src/bin/cargo-miri.rs +++ b/src/bin/cargo-miri.rs @@ -44,8 +44,6 @@ fn main() { return; } - let dep_path = std::env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps"); - if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) { // this arm is when `cargo miri` is called @@ -84,13 +82,11 @@ fn main() { let args = std::env::args().skip(skip); let kind = target.kind.get(0).expect("badly formatted cargo metadata: target::kind is an empty array"); if test && kind == "test" { - if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args), - &dep_path) { + if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args)) { std::process::exit(code); } } else if !test && kind == "bin" { - if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args), - &dep_path) { + if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args)) { std::process::exit(code); } } @@ -117,7 +113,7 @@ fn main() { .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust") }; - // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly + // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly // without having to pass --sysroot or anything let mut args: Vec = if std::env::args().any(|s| s == "--sysroot") { std::env::args().skip(1).collect() @@ -129,25 +125,29 @@ fn main() { // interpreted but not built let miri_enabled = std::env::args().any(|s| s == "-Zno-trans"); - if miri_enabled { - args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]); - } + let mut command = if miri_enabled { + let mut path = std::env::current_exe().expect("current executable path invalid"); + path.set_file_name("miri"); + Command::new(path) + } else { + Command::new("rustc") + }; - let mut path = std::env::current_exe().expect("current executable path invalid"); - path.set_file_name("miri"); + args.extend_from_slice(&["-Z".to_owned(), "always-encode-mir".to_owned()]); + args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]); - match Command::new(path).args(&args).status() { + match command.args(&args).status() { Ok(exit) => if !exit.success() { std::process::exit(exit.code().unwrap_or(42)); }, - Err(e) => panic!("error during miri run: {:?}", e), + Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e), + Err(ref e) => panic!("error during rustc call: {:?}", e), } } } -fn process(old_args: I, dep_path: P) -> Result<(), i32> - where P: AsRef, - I: Iterator +fn process(old_args: I) -> Result<(), i32> + where I: Iterator { let mut args = vec!["rustc".to_owned()]; @@ -159,8 +159,6 @@ fn process(old_args: I, dep_path: P) -> Result<(), i32> if !found_dashes { args.push("--".to_owned()); } - args.push("-L".to_owned()); - args.push(dep_path.as_ref().to_string_lossy().into_owned()); args.push("-Zno-trans".to_owned()); args.push("--cfg".to_owned()); args.push(r#"feature="cargo-miri""#.to_owned()); From 7a755ce8f948ebacb0157a1213d9dc4039f87807 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Thu, 29 Jun 2017 13:59:47 -0400 Subject: [PATCH 1042/1096] add a dependency to cargo-miri-test --- cargo-miri-test/Cargo.lock | 10 ++++++++++ cargo-miri-test/Cargo.toml | 1 + cargo-miri-test/src/main.rs | 8 +++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cargo-miri-test/Cargo.lock b/cargo-miri-test/Cargo.lock index a62bb86226cdb..8b2387fa64109 100644 --- a/cargo-miri-test/Cargo.lock +++ b/cargo-miri-test/Cargo.lock @@ -1,4 +1,14 @@ [root] name = "cargo-miri-test" version = "0.1.0" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +[[package]] +name = "byteorder" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" diff --git a/cargo-miri-test/Cargo.toml b/cargo-miri-test/Cargo.toml index 29886d99a394b..5fbe923f23d3b 100644 --- a/cargo-miri-test/Cargo.toml +++ b/cargo-miri-test/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" authors = ["Oliver Schneider "] [dependencies] +byteorder = "1.0" \ No newline at end of file diff --git a/cargo-miri-test/src/main.rs b/cargo-miri-test/src/main.rs index aa00ab84cb096..07b0e4cee4e5c 100644 --- a/cargo-miri-test/src/main.rs +++ b/cargo-miri-test/src/main.rs @@ -1,3 +1,9 @@ +extern crate byteorder; + +use byteorder::{BigEndian, ByteOrder}; + fn main() { - assert_eq!(5, 5); + let buf = &[1,2,3,4]; + let n = ::read_u32(buf); + assert_eq!(n, 0x01020304); } From 7008646bfee8d6db5bcbeadd9d91af687cea5d47 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Fri, 30 Jun 2017 12:06:49 -0400 Subject: [PATCH 1043/1096] update compiletest dependency --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96022d4655280..f7e9fe39565dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = "0.1.0" dependencies = [ "byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)", "cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "compiletest_rs" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -227,7 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)" = "" "checksum cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d84cb53c78e573aa126a4b9f963fdb2629f8183b26e235da08bb36dc7381162" -"checksum compiletest_rs 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ea3116e739370ad85431a30446b5068ba79171bc6c3d458e90adc834df71359a" +"checksum compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "617b23d0ed4f57b3bcff6b5fe0a78f0010f1efb636298317665a960b6dbc0533" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" From 823b952ef26ad0a6c44d8eb234888bc23cdbc752 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 3 Jul 2017 14:16:11 +0200 Subject: [PATCH 1044/1096] Only check pointers when dereferencing Before we also checked whether pointers had alloc_ids when we created rvalue references --- src/eval_context.rs | 78 ++++++++++++++-------------------- src/lvalue.rs | 6 +-- src/memory.rs | 85 +++++++++++++++++++++---------------- src/terminator/intrinsic.rs | 23 +++++----- src/terminator/mod.rs | 6 +-- src/value.rs | 12 +++--- 6 files changed, 104 insertions(+), 106 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 630f878f72328..1f2c9145cd210 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -351,7 +351,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let global_value = self.globals.get_mut(&id) .expect("global should have been cached (static)"); match global_value.value { - Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?, + // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions + Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; }, @@ -409,6 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn deallocate_local(&mut self, local: Option) -> EvalResult<'tcx> { if let Some(Value::ByRef(ptr)) = local { trace!("deallocating local"); + let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); match self.memory.deallocate(ptr) { // We could alternatively check whether the alloc_id is static before calling @@ -693,26 +695,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Check alignment and non-NULLness. let (_, align) = self.size_and_align_of_dst(ty, val)?; - match ptr { - PrimVal::Ptr(ptr) => { - self.memory.check_align(ptr, align, 0)?; - } - PrimVal::Bytes(bytes) => { - let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64; - if v == 0 { - return Err(EvalError::InvalidNullPointerUsage); - } - if v % align != 0 { - return Err(EvalError::AlignmentCheckFailed { - has: v % align, - required: align, - }); - } - } - PrimVal::Undef => { - return Err(EvalError::ReadUndefBytes); - } - } + self.memory.check_align(ptr, align, 0)?; self.write_value(val, dest, dest_ty)?; } @@ -1036,14 +1019,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.stack[frame].locals[local.index() - 1] { None => return Err(EvalError::DeadLocal), Some(Value::ByRef(ptr)) => { - Lvalue::from_ptr(ptr) + Lvalue::from_primval_ptr(ptr) }, Some(val) => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr)); // it stays live + self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(PrimVal::Ptr(ptr))); // it stays live self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; Lvalue::from_ptr(ptr) } @@ -1053,7 +1036,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Global(cid) => { let global_val = *self.globals.get(&cid).expect("global not cached"); match global_val.value { - Value::ByRef(ptr) => Lvalue::from_ptr(ptr), + Value::ByRef(ptr) => Lvalue::from_primval_ptr(ptr), _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); @@ -1064,7 +1047,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { - value: Value::ByRef(ptr), + value: Value::ByRef(PrimVal::Ptr(ptr)), .. global_val }; Lvalue::from_ptr(ptr) @@ -1160,7 +1143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. - self.write_value_to_ptr(src_val, PrimVal::Ptr(dest_ptr), dest_ty)?; + self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; } else if let Value::ByRef(src_ptr) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it @@ -1178,8 +1161,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { write_dest(self, src_val)?; } else { let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(PrimVal::Ptr(src_ptr), PrimVal::Ptr(dest_ptr), dest_ty)?; - write_dest(self, Value::ByRef(dest_ptr))?; + self.copy(src_ptr, PrimVal::Ptr(dest_ptr), dest_ty)?; + write_dest(self, Value::ByRef(PrimVal::Ptr(dest_ptr)))?; } } else { @@ -1197,7 +1180,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { - Value::ByRef(ptr) => self.copy(PrimVal::Ptr(ptr), dest, dest_ty), + Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), Value::ByVal(primval) => { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(dest, primval, size) @@ -1327,7 +1310,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, ty)? { Ok(val) } else { @@ -1352,13 +1335,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + fn try_read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; let val = match ty.sty { - ty::TyBool => PrimVal::from_bool(self.memory.read_bool(ptr)?), + ty::TyBool => PrimVal::from_bool(self.memory.read_bool(ptr.to_ptr()?)?), ty::TyChar => { - let c = self.memory.read_uint(ptr, 4)? as u32; + let c = self.memory.read_uint(ptr.to_ptr()?, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::from_char(ch), None => return Err(EvalError::InvalidChar(c as u128)), @@ -1377,8 +1360,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // if we transmute a ptr to an isize, reading it back into a primval shouldn't panic // Due to read_ptr ignoring the sign, we need to jump around some hoops - match self.memory.read_int(ptr, size) { - Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr)?, + match self.memory.read_int(ptr.to_ptr()?, size) { + Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?, other => PrimVal::from_i128(other?), } } @@ -1395,30 +1378,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; if size == self.memory.pointer_size() { // if we transmute a ptr to an usize, reading it back into a primval shouldn't panic - self.memory.read_ptr(ptr)? + self.memory.read_ptr(ptr.to_ptr()?)? } else { - PrimVal::from_u128(self.memory.read_uint(ptr, size)?) + PrimVal::from_u128(self.memory.read_uint(ptr.to_ptr()?, size)?) } } - ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?), - ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?), + ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr.to_ptr()?)?), + ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr.to_ptr()?)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr.to_ptr()?)?, ty::TyRef(_, ref tam) | - ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, tam.ty).map(Some), + ty::TyRawPtr(ref tam) => return self.read_ptr(ptr.to_ptr()?, tam.ty).map(Some), ty::TyAdt(def, _) => { if def.is_box() { - return self.read_ptr(ptr, ty.boxed_ty()).map(Some); + return self.read_ptr(ptr.to_ptr()?, ty.boxed_ty()).map(Some); } use rustc::ty::layout::Layout::*; if let CEnum { discr, signed, .. } = *self.type_layout(ty)? { let size = discr.size().bytes(); if signed { - PrimVal::from_i128(self.memory.read_int(ptr, size)?) + PrimVal::from_i128(self.memory.read_int(ptr.to_ptr()?, size)?) } else { - PrimVal::from_u128(self.memory.read_uint(ptr, size)?) + PrimVal::from_u128(self.memory.read_uint(ptr.to_ptr()?, size)?) } } else { return Ok(None); @@ -1537,7 +1520,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { - self.copy(PrimVal::Ptr(src_f_ptr), PrimVal::Ptr(dst_f_ptr), src_fty)?; + self.copy(src_f_ptr, PrimVal::Ptr(dst_f_ptr), src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } @@ -1566,10 +1549,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(err) => { panic!("Failed to access local: {:?}", err); } - Ok(Value::ByRef(ptr)) => { + Ok(Value::ByRef(PrimVal::Ptr(ptr))) => { write!(msg, " by ref:").unwrap(); allocs.push(ptr.alloc_id); } + Ok(Value::ByRef(ptr)) => { + write!(msg, " integral by ref: {:?}", ptr).unwrap(); + } Ok(Value::ByVal(val)) => { write!(msg, " {:?}", val).unwrap(); if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } diff --git a/src/lvalue.rs b/src/lvalue.rs index d2d54b3d90297..df048d08cfae4 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -67,11 +67,11 @@ impl<'tcx> Lvalue<'tcx> { Self::from_primval_ptr(PrimVal::Undef) } - fn from_primval_ptr(ptr: PrimVal) -> Self { + pub(crate) fn from_primval_ptr(ptr: PrimVal) -> Self { Lvalue::Ptr { ptr, extra: LvalueExtra::None } } - pub fn from_ptr(ptr: Pointer) -> Self { + pub(crate) fn from_ptr(ptr: Pointer) -> Self { Self::from_primval_ptr(PrimVal::Ptr(ptr)) } @@ -192,7 +192,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match lvalue { Lvalue::Ptr { ptr, extra } => { assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr.to_ptr()?)) + Ok(Value::ByRef(ptr)) } Lvalue::Local { frame, local } => { self.stack[frame].get_local(local) diff --git a/src/memory.rs b/src/memory.rs index 638a8d2ff27c8..1e29c2f3a1d6d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -189,7 +189,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } let ptr = self.allocate(bytes.len() as u64, 1)?; - self.write_bytes(ptr, bytes)?; + self.write_bytes(PrimVal::Ptr(ptr), bytes)?; self.mark_static_initalized(ptr.alloc_id, false)?; self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id); Ok(ptr) @@ -288,39 +288,52 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx> { - let alloc = self.get(ptr.alloc_id)?; - // check whether the memory was marked as packed - // we select all elements that have the correct alloc_id and are within - // the range given by the offset into the allocation and the length - let start = Entry { - alloc_id: ptr.alloc_id, - packed_start: 0, - packed_end: ptr.offset + len, - }; - let end = Entry { - alloc_id: ptr.alloc_id, - packed_start: ptr.offset + len, - packed_end: 0, + pub fn check_align(&self, ptr: PrimVal, align: u64, len: u64) -> EvalResult<'tcx> { + let offset = match ptr { + PrimVal::Ptr(ptr) => { + let alloc = self.get(ptr.alloc_id)?; + // check whether the memory was marked as packed + // we select all elements that have the correct alloc_id and are within + // the range given by the offset into the allocation and the length + let start = Entry { + alloc_id: ptr.alloc_id, + packed_start: 0, + packed_end: ptr.offset + len, + }; + let end = Entry { + alloc_id: ptr.alloc_id, + packed_start: ptr.offset + len, + packed_end: 0, + }; + for &Entry { packed_start, packed_end, .. } in self.packed.range(start..end) { + // if the region we are checking is covered by a region in `packed` + // ignore the actual alignment + if packed_start <= ptr.offset && (ptr.offset + len) <= packed_end { + return Ok(()); + } + } + if alloc.align < align { + return Err(EvalError::AlignmentCheckFailed { + has: alloc.align, + required: align, + }); + } + ptr.offset + }, + PrimVal::Bytes(bytes) => { + let v = ((bytes as u128) % (1 << self.pointer_size())) as u64; + if v == 0 { + return Err(EvalError::InvalidNullPointerUsage); + } + v + }, + PrimVal::Undef => return Err(EvalError::ReadUndefBytes), }; - for &Entry { packed_start, packed_end, .. } in self.packed.range(start..end) { - // if the region we are checking is covered by a region in `packed` - // ignore the actual alignment - if packed_start <= ptr.offset && (ptr.offset + len) <= packed_end { - return Ok(()); - } - } - if alloc.align < align { - return Err(EvalError::AlignmentCheckFailed { - has: alloc.align, - required: align, - }); - } - if ptr.offset % align == 0 { + if offset % align == 0 { Ok(()) } else { Err(EvalError::AlignmentCheckFailed { - has: ptr.offset % align, + has: offset % align, required: align, }) } @@ -572,7 +585,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_align(ptr, align, size)?; + self.check_align(PrimVal::Ptr(ptr), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -585,7 +598,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&mut []); } - self.check_align(ptr, align, size)?; + self.check_align(PrimVal::Ptr(ptr), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -710,20 +723,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes(ptr.to_ptr()?, size, 1) } - pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> { + pub fn write_bytes(&mut self, ptr: PrimVal, src: &[u8]) -> EvalResult<'tcx> { if src.is_empty() { return Ok(()); } - let bytes = self.get_bytes_mut(ptr, src.len() as u64, 1)?; + let bytes = self.get_bytes_mut(ptr.to_ptr()?, src.len() as u64, 1)?; bytes.clone_from_slice(src); Ok(()) } - pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { + pub fn write_repeat(&mut self, ptr: PrimVal, val: u8, count: u64) -> EvalResult<'tcx> { if count == 0 { return Ok(()); } - let bytes = self.get_bytes_mut(ptr, count, 1)?; + let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, 1)?; for b in bytes { *b = val; } Ok(()) } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 360842ac5475a..38ecccec147fe 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -60,7 +60,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -79,7 +79,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_xchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -88,12 +88,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"), }; self.write_primval(dest, old, ty)?; - self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; + self.write_primval(Lvalue::from_primval_ptr(ptr), change, ty)?; } _ if intrinsic_name.starts_with("atomic_cxchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; let old = self.read_value(ptr, ty)?; @@ -105,7 +105,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?; let dest = self.force_allocation(dest)?.to_ptr()?; self.write_pair_to_ptr(old, val, dest, dest_ty)?; - self.write_primval(Lvalue::from_ptr(ptr), change, ty)?; + self.write_primval(Lvalue::from_primval_ptr(ptr), change, ty)?; } "atomic_or" | "atomic_or_acq" | "atomic_or_rel" | "atomic_or_acqrel" | "atomic_or_relaxed" | @@ -114,7 +114,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -133,7 +133,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // FIXME: what do atomics do on overflow? let (val, _) = self.binary_op(op, old, ty, change, ty)?; - self.write_primval(Lvalue::from_ptr(ptr), val, ty)?; + self.write_primval(Lvalue::from_primval_ptr(ptr), val, ty)?; }, "breakpoint" => unimplemented!(), // halt miri @@ -258,8 +258,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; - this.memory.write_repeat(ptr, 0, size)?; - Value::ByRef(ptr) + this.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; + Value::ByRef(PrimVal::Ptr(ptr)) } }, Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)), @@ -270,7 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr.to_ptr()?, 0, size)?, + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, } @@ -412,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let uninit = |this: &mut Self, val: Value| { match val { Value::ByRef(ptr) => { - this.memory.mark_definedness(PrimVal::Ptr(ptr), size, false)?; + this.memory.mark_definedness(ptr, size, false)?; Ok(Value::ByRef(ptr)) }, _ => Ok(Value::ByVal(PrimVal::Undef)), @@ -436,7 +436,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { - let ptr = ptr.to_ptr()?; self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 68d03b2aaffaa..62446cbfa39d3 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -632,7 +632,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } let ptr = self.memory.allocate(size, align)?; - self.memory.write_repeat(ptr, 0, size)?; + self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } @@ -792,8 +792,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some((name, value)) = new { // +1 for the null terminator let value_copy = self.memory.allocate((value.len() + 1) as u64, 1)?; - self.memory.write_bytes(value_copy, &value)?; - self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?, &[0])?; + self.memory.write_bytes(PrimVal::Ptr(value_copy), &value)?; + self.memory.write_bytes(PrimVal::Ptr(value_copy.offset(value.len() as u64, self.memory.layout)?), &[0])?; if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { self.memory.deallocate(var)?; } diff --git a/src/value.rs b/src/value.rs index 90278d63e7f1c..6f531b126483a 100644 --- a/src/value.rs +++ b/src/value.rs @@ -33,7 +33,7 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug)] pub enum Value { - ByRef(Pointer), + ByRef(PrimVal), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } @@ -72,7 +72,7 @@ impl<'a, 'tcx: 'a> Value { pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_ptr(ptr), + ByRef(ptr) => mem.read_ptr(ptr.to_ptr()?), ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr), } } @@ -84,8 +84,8 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ref_ptr) => { - let ptr = mem.read_ptr(ref_ptr)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; + let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; Ok((ptr, vtable.to_ptr()?)) } @@ -99,8 +99,8 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ref_ptr) => { - let ptr = mem.read_ptr(ref_ptr)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; + let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; Ok((ptr, len)) }, ByValPair(ptr, val) => { From 030166757c01db52805a55792224c15955e8af02 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 13:57:18 -0700 Subject: [PATCH 1045/1096] Fix transmute on ByValPair Fixes #227 --- src/eval_context.rs | 4 ++++ src/terminator/intrinsic.rs | 12 +++--------- tests/compile-fail/transmute-pair-undef.rs | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 tests/compile-fail/transmute-pair-undef.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 630f878f72328..1248d74b3d92a 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1111,6 +1111,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { + // Note that it is really important that the type here is the right one, and matches the type things are read at. + // In case `src_val` is a `ByValPair`, we don't do any magic here to handle padding properly, which is only + // correct if we never look at this data with the wrong type. + match dest { Lvalue::Global(cid) => { let dest = *self.globals.get_mut(&cid).expect("global should be cached"); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 360842ac5475a..13e037f28b68a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -395,16 +395,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); let dest_ty = substs.type_at(1); - let src_align = self.type_align(src_ty)?; - let dest_align = self.type_align(dest_ty)?; let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); - if dest_align < src_align { - let ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.mark_packed(ptr, size); - self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), dest_ty)?; - } else { - self.write_value(arg_vals[0], dest, dest_ty)?; - } + let ptr = self.force_allocation(dest)?.to_ptr()?; + self.memory.mark_packed(ptr, size); + self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), src_ty)?; } "uninit" => { diff --git a/tests/compile-fail/transmute-pair-undef.rs b/tests/compile-fail/transmute-pair-undef.rs new file mode 100644 index 0000000000000..fad51215dd3b8 --- /dev/null +++ b/tests/compile-fail/transmute-pair-undef.rs @@ -0,0 +1,17 @@ +#![feature(core_intrinsics)] + +fn main() { + let x: Option> = unsafe { + let z = std::intrinsics::add_with_overflow(0usize, 0usize); + std::mem::transmute::<(usize, bool), Option>>(z) + }; + let y = &x; + // Now read this bytewise. There should be 9 def bytes followed by 7 undef bytes (the padding after the bool) in there. + let z : *const u8 = y as *const _ as *const _; + for i in 0..9 { + let byte = unsafe { *z.offset(i) }; + assert_eq!(byte, 0); + } + let v = unsafe { *z.offset(9) }; + if v == 0 {} //~ ERROR attempted to read undefined bytes +} From dc9f5a205f3878410485783beb9de0ad6d6bfe31 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 16:06:06 -0700 Subject: [PATCH 1046/1096] properly check for: double-free, use-after-reallocate --- src/error.rs | 131 ++++++++++-------- src/memory.rs | 19 ++- tests/compile-fail/deallocate-twice.rs | 14 ++ tests/compile-fail/reallocate-change-alloc.rs | 12 ++ 4 files changed, 110 insertions(+), 66 deletions(-) create mode 100644 tests/compile-fail/deallocate-twice.rs create mode 100644 tests/compile-fail/reallocate-change-alloc.rs diff --git a/src/error.rs b/src/error.rs index f827784629d0f..46a3096930555 100644 --- a/src/error.rs +++ b/src/error.rs @@ -58,6 +58,9 @@ pub enum EvalError<'tcx> { TypeNotPrimitive(Ty<'tcx>), ReallocatedStaticMemory, DeallocatedStaticMemory, + ReallocateNonBasePtr, + DeallocateNonBasePtr, + IncorrectAllocationInformation, Layout(layout::LayoutError<'tcx>), HeapAllocZeroBytes, HeapAllocNonPowerOfTwoAlignment(u64), @@ -72,98 +75,105 @@ pub type EvalResult<'tcx, T = ()> = Result>; impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { + use EvalError::*; match *self { - EvalError::FunctionPointerTyMismatch(..) => + FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", - EvalError::InvalidMemoryAccess => + InvalidMemoryAccess => "tried to access memory through an invalid pointer", - EvalError::DanglingPointerDeref => + DanglingPointerDeref => "dangling pointer was dereferenced", - EvalError::InvalidFunctionPointer => + InvalidFunctionPointer => "tried to use an integer pointer or a dangling pointer as a function pointer", - EvalError::InvalidBool => + InvalidBool => "invalid boolean value read", - EvalError::InvalidDiscriminant => + InvalidDiscriminant => "invalid enum discriminant value read", - EvalError::PointerOutOfBounds { .. } => + PointerOutOfBounds { .. } => "pointer offset outside bounds of allocation", - EvalError::InvalidNullPointerUsage => + InvalidNullPointerUsage => "invalid use of NULL pointer", - EvalError::ReadPointerAsBytes => + ReadPointerAsBytes => "a raw memory access tried to access part of a pointer value as raw bytes", - EvalError::ReadBytesAsPointer => + ReadBytesAsPointer => "a memory access tried to interpret some bytes as a pointer", - EvalError::InvalidPointerMath => + InvalidPointerMath => "attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations", - EvalError::ReadUndefBytes => + ReadUndefBytes => "attempted to read undefined bytes", - EvalError::DeadLocal => + DeadLocal => "tried to access a dead local variable", - EvalError::InvalidBoolOp(_) => + InvalidBoolOp(_) => "invalid boolean operation", - EvalError::Unimplemented(ref msg) => msg, - EvalError::DerefFunctionPointer => + Unimplemented(ref msg) => msg, + DerefFunctionPointer => "tried to dereference a function pointer", - EvalError::ExecuteMemory => + ExecuteMemory => "tried to treat a memory pointer as a function pointer", - EvalError::ArrayIndexOutOfBounds(..) => + ArrayIndexOutOfBounds(..) => "array index out of bounds", - EvalError::Math(..) => + Math(..) => "mathematical operation failed", - EvalError::Intrinsic(..) => + Intrinsic(..) => "intrinsic failed", - EvalError::OverflowingMath => + OverflowingMath => "attempted to do overflowing math", - EvalError::NoMirFor(..) => + NoMirFor(..) => "mir not found", - EvalError::InvalidChar(..) => + InvalidChar(..) => "tried to interpret an invalid 32-bit value as a char", - EvalError::OutOfMemory{..} => + OutOfMemory{..} => "could not allocate more memory", - EvalError::ExecutionTimeLimitReached => + ExecutionTimeLimitReached => "reached the configured maximum execution time", - EvalError::StackFrameLimitReached => + StackFrameLimitReached => "reached the configured maximum number of stack frames", - EvalError::OutOfTls => + OutOfTls => "reached the maximum number of representable TLS keys", - EvalError::TlsOutOfBounds => + TlsOutOfBounds => "accessed an invalid (unallocated) TLS key", - EvalError::AbiViolation(ref msg) => msg, - EvalError::AlignmentCheckFailed{..} => + AbiViolation(ref msg) => msg, + AlignmentCheckFailed{..} => "tried to execute a misaligned read or write", - EvalError::CalledClosureAsFunction => + CalledClosureAsFunction => "tried to call a closure through a function pointer", - EvalError::VtableForArgumentlessMethod => + VtableForArgumentlessMethod => "tried to call a vtable function without arguments", - EvalError::ModifiedConstantMemory => + ModifiedConstantMemory => "tried to modify constant memory", - EvalError::AssumptionNotHeld => + AssumptionNotHeld => "`assume` argument was false", - EvalError::InlineAsm => + InlineAsm => "miri does not support inline assembly", - EvalError::TypeNotPrimitive(_) => + TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", - EvalError::ReallocatedStaticMemory => + ReallocatedStaticMemory => "tried to reallocate static memory", - EvalError::DeallocatedStaticMemory => + DeallocatedStaticMemory => "tried to deallocate static memory", - EvalError::Layout(_) => + ReallocateNonBasePtr => + "tried to reallocate with a pointer not to the beginning of an existing object", + DeallocateNonBasePtr => + "tried to deallocate with a pointer not to the beginning of an existing object", + IncorrectAllocationInformation => + "tried to deallocate or reallocate using incorrect alignment or size", + Layout(_) => "rustc layout computation failed", - EvalError::UnterminatedCString(_) => + UnterminatedCString(_) => "attempted to get length of a null terminated string, but no null found before end of allocation", - EvalError::HeapAllocZeroBytes => + HeapAllocZeroBytes => "tried to re-, de- or allocate zero bytes on the heap", - EvalError::HeapAllocNonPowerOfTwoAlignment(_) => + HeapAllocNonPowerOfTwoAlignment(_) => "tried to re-, de-, or allocate heap memory with alignment that is not a power of two", - EvalError::Unreachable => + Unreachable => "entered unreachable code", - EvalError::Panic => + Panic => "the evaluated program panicked", - EvalError::NeedsRfc(_) => + NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants", - EvalError::NotConst(_) => + NotConst(_) => "this feature is not compatible with constant evaluation", - EvalError::ReadFromReturnPointer => + ReadFromReturnPointer => "tried to read from the return pointer", } } @@ -173,36 +183,37 @@ impl<'tcx> Error for EvalError<'tcx> { impl<'tcx> fmt::Display for EvalError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use EvalError::*; match *self { - EvalError::PointerOutOfBounds { ptr, access, allocation_size } => { + PointerOutOfBounds { ptr, access, allocation_size } => { write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}", if access { "memory access" } else { "pointer computed" }, ptr.offset, ptr.alloc_id, allocation_size) }, - EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func), - EvalError::FunctionPointerTyMismatch(sig, got) => + NoMirFor(ref func) => write!(f, "no mir for `{}`", func), + FunctionPointerTyMismatch(sig, got) => write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig, got), - EvalError::ArrayIndexOutOfBounds(span, len, index) => + ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), - EvalError::Math(span, ref err) => + Math(span, ref err) => write!(f, "{:?} at {:?}", err, span), - EvalError::Intrinsic(ref err) => + Intrinsic(ref err) => write!(f, "{}", err), - EvalError::InvalidChar(c) => + InvalidChar(c) => write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c), - EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => + OutOfMemory { allocation_size, memory_size, memory_usage } => write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", allocation_size, memory_size - memory_usage, memory_size), - EvalError::AlignmentCheckFailed { required, has } => + AlignmentCheckFailed { required, has } => write!(f, "tried to access memory with alignment {}, but alignment {} is required", has, required), - EvalError::TypeNotPrimitive(ty) => + TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty), - EvalError::Layout(ref err) => + Layout(ref err) => write!(f, "rustc layout computation failed: {:?}", err), - EvalError::NeedsRfc(ref msg) => + NeedsRfc(ref msg) => write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), - EvalError::NotConst(ref msg) => + NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg), _ => write!(f, "{}", self.description()), } diff --git a/src/memory.rs b/src/memory.rs index 638a8d2ff27c8..75d612d599c4b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 { - return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); + return Err(EvalError::ReallocateNonBasePtr); } if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { return Err(EvalError::ReallocatedStaticMemory); @@ -254,14 +254,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.truncate(new_size); } - Ok(Pointer::new(ptr.alloc_id, 0)) + // Change allocation ID. We do this after the above to be able to re-use methods like `clear_relocations`. + let id = { + let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("We already used this pointer above"); + let id = self.next_id; + self.next_id.0 += 1; + self.alloc_map.insert(id, alloc); + id + }; + + Ok(Pointer::new(id, 0)) } // TODO(solson): See comment on `reallocate`. pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> { if ptr.offset != 0 { // TODO(solson): Report error about non-__rust_allocate'd pointer. - return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); + return Err(EvalError::DeallocateNonBasePtr); } if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { return Err(EvalError::DeallocatedStaticMemory); @@ -271,9 +280,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.memory_usage -= alloc.bytes.len() as u64; } else { debug!("deallocated a pointer twice: {}", ptr.alloc_id); - // TODO(solson): Report error about erroneous free. This is blocked on properly tracking - // already-dropped state since this if-statement is entered even in safe code without - // it. + return Err(EvalError::DeallocateNonBasePtr); } debug!("deallocated : {}", ptr.alloc_id); diff --git a/tests/compile-fail/deallocate-twice.rs b/tests/compile-fail/deallocate-twice.rs new file mode 100644 index 0000000000000..9f0f9369a803e --- /dev/null +++ b/tests/compile-fail/deallocate-twice.rs @@ -0,0 +1,14 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +// error-pattern: tried to deallocate with a pointer not to the beginning of an existing object + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + deallocate(x, 1, 1); + deallocate(x, 1, 1); + } +} diff --git a/tests/compile-fail/reallocate-change-alloc.rs b/tests/compile-fail/reallocate-change-alloc.rs new file mode 100644 index 0000000000000..a63629388e7d6 --- /dev/null +++ b/tests/compile-fail/reallocate-change-alloc.rs @@ -0,0 +1,12 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + let _y = reallocate(x, 1, 1, 1); + let _z = *x; //~ ERROR: dangling pointer was dereferenced + } +} From bdcdb605a49d1e543aac88c1b8ff53274fe1b489 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 16:16:05 -0700 Subject: [PATCH 1047/1096] fix test on i686 --- tests/compile-fail/transmute-pair-undef.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/compile-fail/transmute-pair-undef.rs b/tests/compile-fail/transmute-pair-undef.rs index fad51215dd3b8..acc6098af7ee0 100644 --- a/tests/compile-fail/transmute-pair-undef.rs +++ b/tests/compile-fail/transmute-pair-undef.rs @@ -1,17 +1,20 @@ #![feature(core_intrinsics)] +use std::mem; + fn main() { let x: Option> = unsafe { let z = std::intrinsics::add_with_overflow(0usize, 0usize); std::mem::transmute::<(usize, bool), Option>>(z) }; let y = &x; - // Now read this bytewise. There should be 9 def bytes followed by 7 undef bytes (the padding after the bool) in there. + // Now read this bytewise. There should be (ptr_size+1) def bytes followed by (ptr_size-1) undef bytes (the padding after the bool) in there. let z : *const u8 = y as *const _ as *const _; - for i in 0..9 { + let first_undef = mem::size_of::() as isize + 1; + for i in 0..first_undef { let byte = unsafe { *z.offset(i) }; assert_eq!(byte, 0); } - let v = unsafe { *z.offset(9) }; + let v = unsafe { *z.offset(first_undef) }; if v == 0 {} //~ ERROR attempted to read undefined bytes } From 440c4778fa81779bd39dd99edbc7bd42a02c7335 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 16:47:58 -0700 Subject: [PATCH 1048/1096] validate size and alignment on reallocate and deallocate --- src/eval_context.rs | 4 +- src/memory.rs | 37 ++++++++++++------- src/terminator/mod.rs | 14 +++---- .../compile-fail/deallocate-bad-alignment.rs | 13 +++++++ tests/compile-fail/deallocate-bad-size.rs | 13 +++++++ .../compile-fail/reallocate-bad-alignment.rs | 13 +++++++ tests/compile-fail/reallocate-bad-size.rs | 13 +++++++ 7 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 tests/compile-fail/deallocate-bad-alignment.rs create mode 100644 tests/compile-fail/deallocate-bad-size.rs create mode 100644 tests/compile-fail/reallocate-bad-alignment.rs create mode 100644 tests/compile-fail/reallocate-bad-size.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 630f878f72328..b101a3967a723 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -410,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(Value::ByRef(ptr)) = local { trace!("deallocating local"); self.memory.dump_alloc(ptr.alloc_id); - match self.memory.deallocate(ptr) { + match self.memory.deallocate(ptr, None) { // We could alternatively check whether the alloc_id is static before calling // deallocate, but this is much simpler and is probably the rare case. Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, @@ -1724,7 +1724,7 @@ pub fn eval_main<'a, 'tcx: 'a>( while ecx.step()? {} if let Some(cleanup_ptr) = cleanup_ptr { - ecx.memory.deallocate(cleanup_ptr)?; + ecx.memory.deallocate(cleanup_ptr, None)?; } return Ok(()); } diff --git a/src/memory.rs b/src/memory.rs index 75d612d599c4b..bd38b737a40fe 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -223,10 +223,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. - if ptr.offset != 0 { + if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { return Err(EvalError::ReallocateNonBasePtr); } if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { @@ -234,12 +234,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } let size = self.get(ptr.alloc_id)?.bytes.len() as u64; + let real_align = self.get(ptr.alloc_id)?.align; + if size != old_size || real_align != align { + return Err(EvalError::IncorrectAllocationInformation); + } if new_size > size { let amount = new_size - size; self.memory_usage += amount; let alloc = self.get_mut(ptr.alloc_id)?; - // FIXME: check alignment here assert_eq!(amount as usize as u64, amount); alloc.bytes.extend(iter::repeat(0).take(amount as usize)); alloc.undef_mask.grow(amount, false); @@ -247,7 +250,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.memory_usage -= size - new_size; self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; - // FIXME: check alignment here // `as usize` is fine here, since it is smaller than `size`, which came from a usize alloc.bytes.truncate(new_size as usize); alloc.bytes.shrink_to_fit(); @@ -267,21 +269,28 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } // TODO(solson): See comment on `reallocate`. - pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<'tcx> { - if ptr.offset != 0 { + pub fn deallocate(&mut self, ptr: Pointer, size_and_align: Option<(u64, u64)>) -> EvalResult<'tcx> { + if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::DeallocateNonBasePtr); } - if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { - return Err(EvalError::DeallocatedStaticMemory); - } - if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) { - self.memory_usage -= alloc.bytes.len() as u64; - } else { - debug!("deallocated a pointer twice: {}", ptr.alloc_id); - return Err(EvalError::DeallocateNonBasePtr); + { + // Some code somewhere seems to rely on us *not* removing the allocation when we yield this kind of error. + // So we do this test in advance. + let alloc = self.get(ptr.alloc_id)?; + if alloc.static_kind != StaticKind::NotStatic { + return Err(EvalError::DeallocatedStaticMemory); + } + if let Some((size, align)) = size_and_align { + if size != alloc.bytes.len() as u64 || align != alloc.align { + return Err(EvalError::IncorrectAllocationInformation); + } + } } + + let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("already verified"); + self.memory_usage -= alloc.bytes.len() as u64; debug!("deallocated : {}", ptr.alloc_id); Ok(()) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 68d03b2aaffaa..4bcf7470ba894 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -589,7 +589,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "free" => { let ptr = args[0].read_ptr(&self.memory)?; if !ptr.is_null()? { - self.memory.deallocate(ptr.to_ptr()?)?; + self.memory.deallocate(ptr.to_ptr()?, None)?; } } @@ -638,7 +638,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "__rust_deallocate" => { let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - // FIXME: insert sanity check for size and align? let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let align = self.value_to_primval(args[2], usize)?.to_u64()?; if old_size == 0 { @@ -647,20 +646,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - self.memory.deallocate(ptr)?; + self.memory.deallocate(ptr, Some((old_size, align)))?; }, "__rust_reallocate" => { let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let size = self.value_to_primval(args[2], usize)?.to_u64()?; let align = self.value_to_primval(args[3], usize)?.to_u64()?; - if size == 0 { + if old_size == 0 || size == 0 { return Err(EvalError::HeapAllocZeroBytes); } if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - let new_ptr = self.memory.reallocate(ptr, size, align)?; + let new_ptr = self.memory.reallocate(ptr, old_size, size, align)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } @@ -768,7 +768,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } if let Some(old) = success { if let Some(var) = old { - self.memory.deallocate(var)?; + self.memory.deallocate(var, None)?; } self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } else { @@ -795,7 +795,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_bytes(value_copy, &value)?; self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?, &[0])?; if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { - self.memory.deallocate(var)?; + self.memory.deallocate(var, None)?; } self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } else { diff --git a/tests/compile-fail/deallocate-bad-alignment.rs b/tests/compile-fail/deallocate-bad-alignment.rs new file mode 100644 index 0000000000000..fb3c865fa2508 --- /dev/null +++ b/tests/compile-fail/deallocate-bad-alignment.rs @@ -0,0 +1,13 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + deallocate(x, 1, 2); + } +} diff --git a/tests/compile-fail/deallocate-bad-size.rs b/tests/compile-fail/deallocate-bad-size.rs new file mode 100644 index 0000000000000..fb3c865fa2508 --- /dev/null +++ b/tests/compile-fail/deallocate-bad-size.rs @@ -0,0 +1,13 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + deallocate(x, 1, 2); + } +} diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs new file mode 100644 index 0000000000000..2edc13ee1a106 --- /dev/null +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -0,0 +1,13 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + let _y = reallocate(x, 1, 1, 2); + } +} diff --git a/tests/compile-fail/reallocate-bad-size.rs b/tests/compile-fail/reallocate-bad-size.rs new file mode 100644 index 0000000000000..f7f1b48a7f243 --- /dev/null +++ b/tests/compile-fail/reallocate-bad-size.rs @@ -0,0 +1,13 @@ +#![feature(alloc, heap_api)] + +extern crate alloc; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size + +use alloc::heap::*; +fn main() { + unsafe { + let x = allocate(1, 1); + let _y = reallocate(x, 2, 1, 1); + } +} From 79ab4f0e8c618c94aae163377bcbedf9159c2489 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 19:13:58 -0700 Subject: [PATCH 1049/1096] make u128 test work (commenting out the use of checked_shl) --- tests/{run-pass => run-pass-fullmir}/u128.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) rename tests/{run-pass => run-pass-fullmir}/u128.rs (94%) diff --git a/tests/run-pass/u128.rs b/tests/run-pass-fullmir/u128.rs similarity index 94% rename from tests/run-pass/u128.rs rename to tests/run-pass-fullmir/u128.rs index 4fe40a9694d48..6cad0cf3ec4db 100644 --- a/tests/run-pass/u128.rs +++ b/tests/run-pass-fullmir/u128.rs @@ -8,9 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// This disables the test completely: -// ignore-stage1 - #![feature(i128_type)] fn b(t: T) -> T { t } @@ -57,7 +54,6 @@ fn main() { assert_eq!((l as f64) as u128, l); // formatting let j: u128 = 1 << 67; - /* assert_eq!("147573952589676412928", format!("{}", j)); assert_eq!("80000000000000000", format!("{:x}", j)); assert_eq!("20000000000000000000000", format!("{:o}", j)); @@ -66,7 +62,6 @@ fn main() { assert_eq!("340282366920938463463374607431768211455", format!("{}", u128::max_value())); assert_eq!("147573952589676412928", format!("{:?}", j)); - */ // common traits assert_eq!(x, b(x.clone())); // overflow checks @@ -77,6 +72,6 @@ fn main() { assert_eq!(l.checked_add(b(11)), None); assert_eq!(l.checked_sub(l), Some(0)); assert_eq!(o.checked_sub(b(18)), None); - assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); - assert_eq!(o.checked_shl(b(128)), None); + //assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); + //assert_eq!(o.checked_shl(b(128)), None); } From f118ff43e7208338fa44567725e7a2af90b60a86 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 19:55:15 -0700 Subject: [PATCH 1050/1096] implement the unchecked_ intrinsics; add all the doctests from one of the integer modules --- src/operator.rs | 1 + src/terminator/intrinsic.rs | 26 ++++ tests/run-pass-fullmir/integer-ops.rs | 167 ++++++++++++++++++++++++++ tests/run-pass-fullmir/u128.rs | 4 +- 4 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 tests/run-pass-fullmir/integer-ops.rs diff --git a/src/operator.rs b/src/operator.rs index 5386fa588a4b2..35d3ab213afe5 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -226,6 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { + // FIXME: The "as u32" here could hide an overflow return match bin_op { Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 360842ac5475a..ae2d0b4f5dea3 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -407,6 +407,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + "unchecked_shl" => { + // FIXME Check for too-wide shifts + self.intrinsic_overflowing(mir::BinOp::Shl, &args[0], &args[1], dest, dest_ty)?; + } + + "unchecked_shr" => { + // FIXME Check for too-wide shifts + self.intrinsic_overflowing(mir::BinOp::Shr, &args[0], &args[1], dest, dest_ty)?; + } + + "unchecked_div" => { + let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?; + if rhs == 0 { + return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_div"))); + } + self.intrinsic_overflowing(mir::BinOp::Div, &args[0], &args[1], dest, dest_ty)?; + } + + "unchecked_rem" => { + let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?; + if rhs == 0 { + return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_rem"))); + } + self.intrinsic_overflowing(mir::BinOp::Rem, &args[0], &args[1], dest, dest_ty)?; + } + "uninit" => { let size = dest_layout.size(&self.tcx.data_layout).bytes(); let uninit = |this: &mut Self, val: Value| { diff --git a/tests/run-pass-fullmir/integer-ops.rs b/tests/run-pass-fullmir/integer-ops.rs new file mode 100644 index 0000000000000..3773e699ddf36 --- /dev/null +++ b/tests/run-pass-fullmir/integer-ops.rs @@ -0,0 +1,167 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::i32; + +pub fn main() { + assert_eq!(i8::min_value(), -128); + + assert_eq!(i8::max_value(), 127); + + assert_eq!(i32::from_str_radix("A", 16), Ok(10)); + + let n = -0b1000_0000i8; + assert_eq!(n.count_ones(), 1); + + let n = -0b1000_0000i8; + assert_eq!(n.count_zeros(), 7); + + let n = -1i16; + assert_eq!(n.leading_zeros(), 0); + + let n = -4i8; + assert_eq!(n.trailing_zeros(), 2); + + let n = 0x0123456789ABCDEFi64; + let m = -0x76543210FEDCBA99i64; + assert_eq!(n.rotate_left(32), m); + + let n = 0x0123456789ABCDEFi64; + let m = -0xFEDCBA987654322i64; + assert_eq!(n.rotate_right(4), m); + + let n = 0x0123456789ABCDEFi64; + let m = -0x1032547698BADCFFi64; + assert_eq!(n.swap_bytes(), m); + + let n = 0x0123456789ABCDEFi64; + if cfg!(target_endian = "big") { + assert_eq!(i64::from_be(n), n) + } else { + assert_eq!(i64::from_be(n), n.swap_bytes()) + } + + let n = 0x0123456789ABCDEFi64; + if cfg!(target_endian = "little") { + assert_eq!(i64::from_le(n), n) + } else { + assert_eq!(i64::from_le(n), n.swap_bytes()) + } + + let n = 0x0123456789ABCDEFi64; + if cfg!(target_endian = "big") { + assert_eq!(n.to_be(), n) + } else { + assert_eq!(n.to_be(), n.swap_bytes()) + } + + let n = 0x0123456789ABCDEFi64; + if cfg!(target_endian = "little") { + assert_eq!(n.to_le(), n) + } else { + assert_eq!(n.to_le(), n.swap_bytes()) + } + + assert_eq!(7i16.checked_add(32760), Some(32767)); + assert_eq!(8i16.checked_add(32760), None); + + assert_eq!((-127i8).checked_sub(1), Some(-128)); + assert_eq!((-128i8).checked_sub(1), None); + + assert_eq!(6i8.checked_mul(21), Some(126)); + assert_eq!(6i8.checked_mul(22), None); + + assert_eq!((-127i8).checked_div(-1), Some(127)); + assert_eq!((-128i8).checked_div(-1), None); + assert_eq!((1i8).checked_div(0), None); + + assert_eq!(5i32.checked_rem(2), Some(1)); + assert_eq!(5i32.checked_rem(0), None); + assert_eq!(i32::MIN.checked_rem(-1), None); + + assert_eq!(5i32.checked_neg(), Some(-5)); + assert_eq!(i32::MIN.checked_neg(), None); + + assert_eq!(0x10i32.checked_shl(4), Some(0x100)); + assert_eq!(0x10i32.checked_shl(33), None); + + assert_eq!(0x10i32.checked_shr(4), Some(0x1)); + assert_eq!(0x10i32.checked_shr(33), None); + + assert_eq!((-5i32).checked_abs(), Some(5)); + assert_eq!(i32::MIN.checked_abs(), None); + + assert_eq!(100i8.saturating_add(1), 101); + assert_eq!(100i8.saturating_add(127), 127); + + assert_eq!(100i8.saturating_sub(127), -27); + assert_eq!((-100i8).saturating_sub(127), -128); + + assert_eq!(100i32.saturating_mul(127), 12700); + assert_eq!((1i32 << 23).saturating_mul(1 << 23), i32::MAX); + assert_eq!((-1i32 << 23).saturating_mul(1 << 23), i32::MIN); + + assert_eq!(100i8.wrapping_add(27), 127); + assert_eq!(100i8.wrapping_add(127), -29); + + assert_eq!(0i8.wrapping_sub(127), -127); + assert_eq!((-2i8).wrapping_sub(127), 127); + + assert_eq!(10i8.wrapping_mul(12), 120); + assert_eq!(11i8.wrapping_mul(12), -124); + + assert_eq!(100u8.wrapping_div(10), 10); + assert_eq!((-128i8).wrapping_div(-1), -128); + + assert_eq!(100i8.wrapping_rem(10), 0); + assert_eq!((-128i8).wrapping_rem(-1), 0); + + assert_eq!(100i8.wrapping_neg(), -100); + assert_eq!((-128i8).wrapping_neg(), -128); + + assert_eq!((-1i8).wrapping_shl(7), -128); + assert_eq!((-1i8).wrapping_shl(8), -1); + + assert_eq!((-128i8).wrapping_shr(7), -1); + assert_eq!((-128i8).wrapping_shr(8), -128); + + assert_eq!(100i8.wrapping_abs(), 100); + assert_eq!((-100i8).wrapping_abs(), 100); + assert_eq!((-128i8).wrapping_abs(), -128); + assert_eq!((-128i8).wrapping_abs() as u8, 128); + + assert_eq!(5i32.overflowing_add(2), (7, false)); + assert_eq!(i32::MAX.overflowing_add(1), (i32::MIN, true)); + + assert_eq!(5i32.overflowing_sub(2), (3, false)); + assert_eq!(i32::MIN.overflowing_sub(1), (i32::MAX, true)); + + assert_eq!(5i32.overflowing_mul(2), (10, false)); + assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true)); + + assert_eq!(5i32.overflowing_div(2), (2, false)); + assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true)); + + assert_eq!(5i32.overflowing_rem(2), (1, false)); + assert_eq!(i32::MIN.overflowing_rem(-1), (0, true)); + + assert_eq!(2i32.overflowing_neg(), (-2, false)); + assert_eq!(i32::MIN.overflowing_neg(), (i32::MIN, true)); + + assert_eq!(0x10i32.overflowing_shl(4), (0x100, false)); + assert_eq!(0x10i32.overflowing_shl(36), (0x100, true)); + + assert_eq!(0x10i32.overflowing_shr(4), (0x1, false)); + assert_eq!(0x10i32.overflowing_shr(36), (0x1, true)); + + assert_eq!(10i8.overflowing_abs(), (10,false)); + assert_eq!((-10i8).overflowing_abs(), (10,false)); + assert_eq!((-128i8).overflowing_abs(), (-128,true)); +} diff --git a/tests/run-pass-fullmir/u128.rs b/tests/run-pass-fullmir/u128.rs index 6cad0cf3ec4db..a05308acbe676 100644 --- a/tests/run-pass-fullmir/u128.rs +++ b/tests/run-pass-fullmir/u128.rs @@ -72,6 +72,6 @@ fn main() { assert_eq!(l.checked_add(b(11)), None); assert_eq!(l.checked_sub(l), Some(0)); assert_eq!(o.checked_sub(b(18)), None); - //assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); - //assert_eq!(o.checked_shl(b(128)), None); + assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127)); + assert_eq!(o.checked_shl(b(128)), None); } From ffd482e2f5e5b5635922613500b13f0ba6150aaf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 20:27:09 -0700 Subject: [PATCH 1051/1096] check the assumptions made by the unchecked_ and copy_nonoverlapping intrinsics --- src/eval_context.rs | 2 +- src/memory.rs | 8 ++++++- src/terminator/intrinsic.rs | 15 ++++++++---- tests/compile-fail/copy_nonoverlapping.rs | 24 +++++++++++++++++++ tests/compile-fail/div-by-zero.rs | 21 ++++++++++++++++ .../compile-fail/overflowing-unchecked-rsh.rs | 21 ++++++++++++++++ 6 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 tests/compile-fail/copy_nonoverlapping.rs create mode 100644 tests/compile-fail/div-by-zero.rs create mode 100644 tests/compile-fail/overflowing-unchecked-rsh.rs diff --git a/src/eval_context.rs b/src/eval_context.rs index 630f878f72328..448ac2f708919 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1022,7 +1022,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); let align = self.type_align(ty)?; - self.memory.copy(src, dest, size, align)?; + self.memory.copy(src, dest, size, align, false)?; Ok(()) } diff --git a/src/memory.rs b/src/memory.rs index 638a8d2ff27c8..7623a0c4d2e30 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -658,7 +658,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64) -> EvalResult<'tcx> { + pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { if size == 0 { return Ok(()); } @@ -675,6 +675,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { unsafe { assert_eq!(size as usize as u64, size); if src.alloc_id == dest.alloc_id { + if nonoverlapping { + if (src.offset <= dest.offset && src.offset + size > dest.offset) || + (dest.offset <= src.offset && dest.offset + size > src.offset) { + return Err(EvalError::Intrinsic(format!("copy_nonoverlapping called on overlapping ranges"))); + } + } ptr::copy(src_bytes, dest_bytes, size as usize); } else { ptr::copy_nonoverlapping(src_bytes, dest_bytes, size as usize); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index ae2d0b4f5dea3..058936fd0445e 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -140,7 +140,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy" | "copy_nonoverlapping" => { - // FIXME: check whether overlapping occurs let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); if elem_size != 0 { @@ -148,7 +147,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src = arg_vals[0].read_ptr(&self.memory)?; let dest = arg_vals[1].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; - self.memory.copy(src, dest, count * elem_size, elem_align)?; + self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?; } } @@ -408,12 +407,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "unchecked_shl" => { - // FIXME Check for too-wide shifts + let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8; + let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?; + if rhs >= bits { + return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shl", rhs))); + } self.intrinsic_overflowing(mir::BinOp::Shl, &args[0], &args[1], dest, dest_ty)?; } "unchecked_shr" => { - // FIXME Check for too-wide shifts + let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8; + let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?; + if rhs >= bits { + return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shr", rhs))); + } self.intrinsic_overflowing(mir::BinOp::Shr, &args[0], &args[1], dest, dest_ty)?; } diff --git a/tests/compile-fail/copy_nonoverlapping.rs b/tests/compile-fail/copy_nonoverlapping.rs new file mode 100644 index 0000000000000..f4acbadfd549d --- /dev/null +++ b/tests/compile-fail/copy_nonoverlapping.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core_intrinsics)] + +use std::intrinsics::*; + +//error-pattern: copy_nonoverlapping called on overlapping ranges + +fn main() { + let mut data = [0u8; 16]; + unsafe { + let a = &data[0] as *const _; + let b = &mut data[1] as *mut _; + std::ptr::copy_nonoverlapping(a, b, 2); + } +} diff --git a/tests/compile-fail/div-by-zero.rs b/tests/compile-fail/div-by-zero.rs new file mode 100644 index 0000000000000..4ac6214d88abb --- /dev/null +++ b/tests/compile-fail/div-by-zero.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core_intrinsics)] + +use std::intrinsics::*; + +//error-pattern: Division by 0 in unchecked_div + +fn main() { + unsafe { + let _n = unchecked_div(1i64, 0); + } +} diff --git a/tests/compile-fail/overflowing-unchecked-rsh.rs b/tests/compile-fail/overflowing-unchecked-rsh.rs new file mode 100644 index 0000000000000..b8291e1300edf --- /dev/null +++ b/tests/compile-fail/overflowing-unchecked-rsh.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core_intrinsics)] + +use std::intrinsics::*; + +//error-pattern: Overflowing shift by 64 in unchecked_shr + +fn main() { + unsafe { + let _n = unchecked_shr(1i64, 64); + } +} From 30f92f8a2735bb978869f10fbb7c52414692b9ba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 20:46:21 -0700 Subject: [PATCH 1052/1096] catch overflows that are hidden by first casting the RHS to u32 --- src/operator.rs | 28 +++++++++---------- tests/compile-fail/overflowing-rsh-2.rs | 16 +++++++++++ ...verflowing-rsh-6.rs => overflowing-rsh.rs} | 0 3 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 tests/compile-fail/overflowing-rsh-2.rs rename tests/compile-fail/{overflowing-rsh-6.rs => overflowing-rsh.rs} (100%) diff --git a/src/operator.rs b/src/operator.rs index 35d3ab213afe5..e1362f52eedda 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -93,19 +93,20 @@ macro_rules! int_shift { ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ let l = $l; let r = $r; + let r_wrapped = r as u32; match $kind { - I8 => overflow!($int_op, l as i8, r), - I16 => overflow!($int_op, l as i16, r), - I32 => overflow!($int_op, l as i32, r), - I64 => overflow!($int_op, l as i64, r), - I128 => overflow!($int_op, l as i128, r), - U8 => overflow!($int_op, l as u8, r), - U16 => overflow!($int_op, l as u16, r), - U32 => overflow!($int_op, l as u32, r), - U64 => overflow!($int_op, l as u64, r), - U128 => overflow!($int_op, l as u128, r), + I8 => overflow!($int_op, l as i8, r_wrapped), + I16 => overflow!($int_op, l as i16, r_wrapped), + I32 => overflow!($int_op, l as i32, r_wrapped), + I64 => overflow!($int_op, l as i64, r_wrapped), + I128 => overflow!($int_op, l as i128, r_wrapped), + U8 => overflow!($int_op, l as u8, r_wrapped), + U16 => overflow!($int_op, l as u16, r_wrapped), + U32 => overflow!($int_op, l as u32, r_wrapped), + U64 => overflow!($int_op, l as u64, r_wrapped), + U128 => overflow!($int_op, l as u128, r_wrapped), _ => bug!("int_shift should only be called on int primvals"), - } + }.map(|(val, over)| (val, over || r != r_wrapped as u128)) }) } @@ -226,10 +227,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { - // FIXME: The "as u32" here could hide an overflow return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), - Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), + Shl => int_shift!(left_kind, overflowing_shl, l, r), + Shr => int_shift!(left_kind, overflowing_shr, l, r), _ => bug!("it has already been checked that this is a shift op"), }; } diff --git a/tests/compile-fail/overflowing-rsh-2.rs b/tests/compile-fail/overflowing-rsh-2.rs new file mode 100644 index 0000000000000..ac09a1740c43e --- /dev/null +++ b/tests/compile-fail/overflowing-rsh-2.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(exceeding_bitshifts, const_err)] + +fn main() { + // Make sure we catch overflows that would be hidden by first casting the RHS to u32 + let _n = 1i64 >> (u32::max_value() as i64 + 1); //~ Overflow(Shr) +} diff --git a/tests/compile-fail/overflowing-rsh-6.rs b/tests/compile-fail/overflowing-rsh.rs similarity index 100% rename from tests/compile-fail/overflowing-rsh-6.rs rename to tests/compile-fail/overflowing-rsh.rs From 19d6ad74e55288d31c15f616bba0418980c2ec1b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Jul 2017 20:59:47 -0700 Subject: [PATCH 1053/1096] add test for div-by-zero with the operator (rather than the intrinsic) --- tests/compile-fail/div-by-zero-2.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/compile-fail/div-by-zero-2.rs diff --git a/tests/compile-fail/div-by-zero-2.rs b/tests/compile-fail/div-by-zero-2.rs new file mode 100644 index 0000000000000..3e869ad4a5078 --- /dev/null +++ b/tests/compile-fail/div-by-zero-2.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(const_err)] + +fn main() { + let _n = 1 / 0; //~ ERROR: DivisionByZero +} From 4165051073675814f09f4ef2ab5dbbb901cc10f4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 5 Jul 2017 10:26:15 -0700 Subject: [PATCH 1054/1096] refine comment explaining the order of checks on deallocate --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index bd38b737a40fe..0a032891f06ff 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -276,7 +276,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } { - // Some code somewhere seems to rely on us *not* removing the allocation when we yield this kind of error. + // deallocate_local in eval_context.rs relies on nothing actually having changed when this error occurs. // So we do this test in advance. let alloc = self.get(ptr.alloc_id)?; if alloc.static_kind != StaticKind::NotStatic { From 3c0a6d6922db63981d18de7e1dbd9842648d7227 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 13:34:54 -0700 Subject: [PATCH 1055/1096] simplify reallocate --- src/memory.rs | 47 +++---------------- .../compile-fail/reallocate-bad-alignment.rs | 2 +- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index c638fabbe631a..64549f45b39ce 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -224,6 +224,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + use std::cmp::min; + assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { @@ -233,39 +235,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Err(EvalError::ReallocatedStaticMemory); } - let size = self.get(ptr.alloc_id)?.bytes.len() as u64; - let real_align = self.get(ptr.alloc_id)?.align; - if size != old_size || real_align != align { - return Err(EvalError::IncorrectAllocationInformation); - } + // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" + let new_ptr = self.allocate(new_size, align)?; + self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), align, /*nonoverlapping*/true)?; + self.deallocate(ptr, Some((old_size, align)))?; - if new_size > size { - let amount = new_size - size; - self.memory_usage += amount; - let alloc = self.get_mut(ptr.alloc_id)?; - assert_eq!(amount as usize as u64, amount); - alloc.bytes.extend(iter::repeat(0).take(amount as usize)); - alloc.undef_mask.grow(amount, false); - } else if size > new_size { - self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; - let alloc = self.get_mut(ptr.alloc_id)?; - // `as usize` is fine here, since it is smaller than `size`, which came from a usize - alloc.bytes.truncate(new_size as usize); - alloc.bytes.shrink_to_fit(); - alloc.undef_mask.truncate(new_size); - } - - // Change allocation ID. We do this after the above to be able to re-use methods like `clear_relocations`. - let id = { - let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("We already used this pointer above"); - let id = self.next_id; - self.next_id.0 += 1; - self.alloc_map.insert(id, alloc); - id - }; - - Ok(Pointer::new(id, 0)) + Ok(new_ptr) } // TODO(solson): See comment on `reallocate`. @@ -1138,14 +1113,6 @@ impl UndefMask { self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } - - fn truncate(&mut self, length: u64) { - self.len = length; - let truncate = self.len / BLOCK_SIZE + 1; - assert_eq!(truncate as usize as u64, truncate); - self.blocks.truncate(truncate as usize); - self.blocks.shrink_to_fit(); - } } fn bit_index(bits: u64) -> (usize, usize) { diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs index 2edc13ee1a106..23fe93c5af668 100644 --- a/tests/compile-fail/reallocate-bad-alignment.rs +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -2,7 +2,7 @@ extern crate alloc; -// error-pattern: tried to deallocate or reallocate using incorrect alignment or size +// error-pattern: tried to access memory with alignment 1, but alignment 2 is required use alloc::heap::*; fn main() { From e60f11f52cea83322721a24985ffb1f95c041555 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 13:57:18 -0700 Subject: [PATCH 1056/1096] update for latest nightly --- tests/run-pass/associated-const.rs | 2 -- tests/run-pass/issue-31267-additional.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/run-pass/associated-const.rs b/tests/run-pass/associated-const.rs index d906544500931..fe5da49f807d5 100644 --- a/tests/run-pass/associated-const.rs +++ b/tests/run-pass/associated-const.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(associated_consts)] - trait Foo { const ID: i32; } diff --git a/tests/run-pass/issue-31267-additional.rs b/tests/run-pass/issue-31267-additional.rs index 90160ebcdf94f..14e38f43c527b 100644 --- a/tests/run-pass/issue-31267-additional.rs +++ b/tests/run-pass/issue-31267-additional.rs @@ -10,8 +10,6 @@ #![allow(unused_variables)] -#![feature(associated_consts)] - #[derive(Clone, Copy, Debug)] struct Bar; From d2cf3d76b91f936f7dfecf89c52d06dd54335e9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 15:46:16 -0700 Subject: [PATCH 1057/1096] update for allocator API --- src/memory.rs | 9 ++- src/terminator/mod.rs | 163 +++++++++++++++++++++++------------------- 2 files changed, 95 insertions(+), 77 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 64549f45b39ce..ba9b13f07c978 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -223,10 +223,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, new_size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { use std::cmp::min; - assert!(align.is_power_of_two()); // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { return Err(EvalError::ReallocateNonBasePtr); @@ -236,9 +235,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" - let new_ptr = self.allocate(new_size, align)?; - self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), align, /*nonoverlapping*/true)?; - self.deallocate(ptr, Some((old_size, align)))?; + let new_ptr = self.allocate(new_size, new_align)?; + self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; + self.deallocate(ptr, Some((old_size, old_align)))?; Ok(new_ptr) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9b0596d58da03..38fd465474173 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -520,37 +520,111 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { sig: ty::FnSig<'tcx>, path: String, ) -> EvalResult<'tcx> { + // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early. + match &path[..] { + "std::panicking::rust_panic_with_hook" | + "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), + _ => {}, + } + + let dest_ty = sig.output(); + let (dest, dest_block) = destination.ok_or_else(|| EvalError::NoMirFor(path.clone()))?; + if sig.abi == Abi::C { // An external C function - let ty = sig.output(); - let (ret, target) = destination.unwrap(); - self.call_c_abi(instance.def_id(), arg_operands, ret, ty, target)?; + // TODO: That functions actually has a similar preamble to what follows here. May make sense to + // unify these two mechanisms for "hooking into missing functions". + self.call_c_abi(instance.def_id(), arg_operands, dest, dest_ty, dest_block)?; return Ok(()); } + + let args_res: EvalResult> = arg_operands.iter() + .map(|arg| self.eval_operand(arg)) + .collect(); + let args = args_res?; + + let usize = self.tcx.types.usize; - // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). - // Still, we can make many things mostly work by "emulating" or ignoring some functions. match &path[..] { + // Allocators are magic. They have no MIR, even when the rest of libstd does. + "alloc::heap::::__rust_alloc" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_alloc_zeroed" => { + let size = self.value_to_primval(args[0], usize)?.to_u64()?; + let align = self.value_to_primval(args[1], usize)?.to_u64()?; + if size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + let ptr = self.memory.allocate(size, align)?; + self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "alloc::heap::::__rust_dealloc" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let align = self.value_to_primval(args[2], usize)?.to_u64()?; + if old_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); + } + self.memory.deallocate(ptr, Some((old_size, align)))?; + } + "alloc::heap::::__rust_realloc" => { + let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; + let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; + let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; + let new_align = self.value_to_primval(args[4], usize)?.to_u64()?; + if old_size == 0 || new_size == 0 { + return Err(EvalError::HeapAllocZeroBytes); + } + if !old_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(old_align)); + } + if !new_align.is_power_of_two() { + return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align)); + } + let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; + } + + // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies). + // Still, we can make many things mostly work by "emulating" or ignoring some functions. "std::io::_print" => { trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR."); - self.goto_block(destination.unwrap().1); - Ok(()) - }, - "std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), - "std::panicking::rust_panic_with_hook" | - "std::rt::begin_panic_fmt" => Err(EvalError::Panic), + } + "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), + "std::env::args" => return Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())), "std::panicking::panicking" | "std::rt::panicking" => { - let (lval, block) = destination.expect("std::rt::panicking does not diverge"); // we abort on panic -> `std::rt::panicking` always returns false let bool = self.tcx.types.bool; - self.write_primval(lval, PrimVal::from_bool(false), bool)?; - self.goto_block(block); - Ok(()) + self.write_primval(dest, PrimVal::from_bool(false), bool)?; } - _ => Err(EvalError::NoMirFor(path)), + _ => return Err(EvalError::NoMirFor(path)), } + + // Since we pushed no stack frame, the main loop will act + // as if the call just completed and it's returning to the + // current frame. + self.dump_local(dest); + self.goto_block(dest_block); + return Ok(()); } fn call_c_abi( @@ -609,61 +683,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name))); } - "__rust_allocate" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - if size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let ptr = self.memory.allocate(size, align)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - - "__rust_allocate_zeroed" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - if size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let ptr = self.memory.allocate(size, align)?; - self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - - "__rust_deallocate" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let align = self.value_to_primval(args[2], usize)?.to_u64()?; - if old_size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - self.memory.deallocate(ptr, Some((old_size, align)))?; - }, - - "__rust_reallocate" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; - let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let size = self.value_to_primval(args[2], usize)?.to_u64()?; - let align = self.value_to_primval(args[3], usize)?.to_u64()?; - if old_size == 0 || size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); - } - let new_ptr = self.memory.reallocate(ptr, old_size, size, align)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; - } - "__rust_maybe_catch_panic" => { // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure From ea730ab20f72196a683f313cbd2797456eb28b3e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 15:58:47 -0700 Subject: [PATCH 1058/1096] update tests for new allocator API --- tests/compile-fail/deallocate-bad-alignment.rs | 9 ++++++--- tests/compile-fail/deallocate-bad-size.rs | 9 ++++++--- tests/compile-fail/deallocate-twice.rs | 11 +++++++---- tests/compile-fail/reallocate-bad-alignment.rs | 11 +++++++---- tests/compile-fail/reallocate-bad-size.rs | 9 ++++++--- tests/compile-fail/reallocate-change-alloc.rs | 10 ++++++---- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/tests/compile-fail/deallocate-bad-alignment.rs b/tests/compile-fail/deallocate-bad-alignment.rs index fb3c865fa2508..a0bcffa47d9fc 100644 --- a/tests/compile-fail/deallocate-bad-alignment.rs +++ b/tests/compile-fail/deallocate-bad-alignment.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 2)); } } diff --git a/tests/compile-fail/deallocate-bad-size.rs b/tests/compile-fail/deallocate-bad-size.rs index fb3c865fa2508..d8c4493043dbb 100644 --- a/tests/compile-fail/deallocate-bad-size.rs +++ b/tests/compile-fail/deallocate-bad-size.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(2, 1)); } } diff --git a/tests/compile-fail/deallocate-twice.rs b/tests/compile-fail/deallocate-twice.rs index 9f0f9369a803e..3c4399eaa3ed6 100644 --- a/tests/compile-fail/deallocate-twice.rs +++ b/tests/compile-fail/deallocate-twice.rs @@ -1,14 +1,17 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate with a pointer not to the beginning of an existing object use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - deallocate(x, 1, 1); - deallocate(x, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 1)); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 1)); } } diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs index 23fe93c5af668..246d559295774 100644 --- a/tests/compile-fail/reallocate-bad-alignment.rs +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; -// error-pattern: tried to access memory with alignment 1, but alignment 2 is required +use alloc::heap::Heap; +use alloc::allocator::*; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 1, 1, 2); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 2)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 2)).unwrap(); } } diff --git a/tests/compile-fail/reallocate-bad-size.rs b/tests/compile-fail/reallocate-bad-size.rs index f7f1b48a7f243..2e5a641838020 100644 --- a/tests/compile-fail/reallocate-bad-size.rs +++ b/tests/compile-fail/reallocate-bad-size.rs @@ -1,13 +1,16 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; +use alloc::heap::Heap; +use alloc::allocator::*; + // error-pattern: tried to deallocate or reallocate using incorrect alignment or size use alloc::heap::*; fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 2, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(2, 1), Layout::from_size_align_unchecked(1, 1)).unwrap(); } } diff --git a/tests/compile-fail/reallocate-change-alloc.rs b/tests/compile-fail/reallocate-change-alloc.rs index a63629388e7d6..290c966a2bc8a 100644 --- a/tests/compile-fail/reallocate-change-alloc.rs +++ b/tests/compile-fail/reallocate-change-alloc.rs @@ -1,12 +1,14 @@ -#![feature(alloc, heap_api)] +#![feature(alloc, allocator_api)] extern crate alloc; -use alloc::heap::*; +use alloc::heap::Heap; +use alloc::allocator::*; + fn main() { unsafe { - let x = allocate(1, 1); - let _y = reallocate(x, 1, 1, 1); + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 1)).unwrap(); let _z = *x; //~ ERROR: dangling pointer was dereferenced } } From 1cbf5e896219be3e1eda8b1f92d1ba76ae30bd55 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jul 2017 18:09:46 -0700 Subject: [PATCH 1059/1096] leave notes regarding possible alignment checks --- src/memory.rs | 1 + src/terminator/intrinsic.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index ba9b13f07c978..0f32ac5183def 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -663,6 +663,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { if size == 0 { + // TODO: Should we check for alignment here? (Also see write_bytes intrinsic) return Ok(()); } let src = src.to_ptr()?; diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index dbfd8f1d952fe..f2b0446618a06 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -463,6 +463,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { + // TODO: Should we, at least, validate the alignment? (Also see memory::copy) self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } From 2d52054fb28a81d351b09330496c749775790b63 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 4 Jul 2017 13:16:29 +0200 Subject: [PATCH 1060/1096] Rename Pointer to MemoryPointer --- src/error.rs | 6 +-- src/eval_context.rs | 14 +++---- src/lib.rs | 2 +- src/lvalue.rs | 8 ++-- src/memory.rs | 96 +++++++++++++++++++++---------------------- src/operator.rs | 8 ++-- src/terminator/mod.rs | 6 +-- src/traits.rs | 8 ++-- src/value.rs | 10 ++--- 9 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/error.rs b/src/error.rs index 46a3096930555..801ea5c7da48f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt; use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; -use memory::Pointer; +use memory::MemoryPointer; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -10,14 +10,14 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), - UnterminatedCString(Pointer), + UnterminatedCString(MemoryPointer), DanglingPointerDeref, InvalidMemoryAccess, InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, PointerOutOfBounds { - ptr: Pointer, + ptr: MemoryPointer, access: bool, allocation_size: u64, }, diff --git a/src/eval_context.rs b/src/eval_context.rs index 2ecc4f029e89f..c4c89427b40ba 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -17,7 +17,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; -use memory::{Memory, Pointer, TlsKey}; +use memory::{Memory, MemoryPointer, TlsKey}; use operator; use value::{PrimVal, PrimValKind, Value}; @@ -44,7 +44,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// Environment variables set by `setenv` /// Miri does not expose env vars from the host to the emulated program - pub(crate) env_vars: HashMap, Pointer>, + pub(crate) env_vars: HashMap, MemoryPointer>, } /// A stack frame. @@ -142,7 +142,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { + pub fn alloc_ptr(&mut self, ty: Ty<'tcx>) -> EvalResult<'tcx, MemoryPointer> { let substs = self.substs(); self.alloc_ptr_with_substs(ty, substs) } @@ -151,7 +151,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx> - ) -> EvalResult<'tcx, Pointer> { + ) -> EvalResult<'tcx, MemoryPointer> { let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type"); let align = self.type_align_with_substs(ty, substs)?; self.memory.allocate(size, align) @@ -313,7 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { set } - // Subtract 1 because `local_decls` includes the ReturnPointer, but we don't store a local + // Subtract 1 because `local_decls` includes the ReturnMemoryPointer, but we don't store a local // `Value` for that. let annotated_locals = collect_storage_annotations(mir); let num_locals = mir.local_decls.len() - 1; @@ -1197,7 +1197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, a: PrimVal, b: PrimVal, - ptr: Pointer, + ptr: MemoryPointer, mut ty: Ty<'tcx> ) -> EvalResult<'tcx> { while self.get_field_count(ty)? == 1 { @@ -1322,7 +1322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(crate) fn read_ptr(&self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(crate) fn read_ptr(&self, ptr: MemoryPointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(pointee_ty) { Ok(Value::ByVal(p)) diff --git a/src/lib.rs b/src/lib.rs index f373606218c73..bbb271a6b6a79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ pub use lvalue::{ pub use memory::{ AllocId, Memory, - Pointer, + MemoryPointer, }; pub use value::{ diff --git a/src/lvalue.rs b/src/lvalue.rs index df048d08cfae4..de7d4d917160b 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -5,7 +5,7 @@ use rustc_data_structures::indexed_vec::Idx; use error::{EvalError, EvalResult}; use eval_context::{EvalContext}; -use memory::Pointer; +use memory::MemoryPointer; use value::{PrimVal, Value}; #[derive(Copy, Clone, Debug)] @@ -34,7 +34,7 @@ pub enum Lvalue<'tcx> { pub enum LvalueExtra { None, Length(u64), - Vtable(Pointer), + Vtable(MemoryPointer), DowncastVariant(usize), } @@ -71,7 +71,7 @@ impl<'tcx> Lvalue<'tcx> { Lvalue::Ptr { ptr, extra: LvalueExtra::None } } - pub(crate) fn from_ptr(ptr: Pointer) -> Self { + pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self { Self::from_primval_ptr(PrimVal::Ptr(ptr)) } @@ -83,7 +83,7 @@ impl<'tcx> Lvalue<'tcx> { } } - pub(super) fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + pub(super) fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { let (ptr, extra) = self.to_ptr_and_extra(); assert_eq!(extra, LvalueExtra::None); ptr.to_ptr() diff --git a/src/memory.rs b/src/memory.rs index 0f32ac5183def..568da336f6696 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -50,36 +50,36 @@ pub enum StaticKind { } #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Pointer { +pub struct MemoryPointer { pub alloc_id: AllocId, pub offset: u64, } -impl Pointer { +impl MemoryPointer { pub fn new(alloc_id: AllocId, offset: u64) -> Self { - Pointer { alloc_id, offset } + MemoryPointer { alloc_id, offset } } pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { - Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) + MemoryPointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) } pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) { let (res, over) = value::overflowing_signed_offset(self.offset, i, layout); - (Pointer::new(self.alloc_id, res), over) + (MemoryPointer::new(self.alloc_id, res), over) } pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) + Ok(MemoryPointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) } pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) { let (res, over) = value::overflowing_offset(self.offset, i, layout); - (Pointer::new(self.alloc_id, res), over) + (MemoryPointer::new(self.alloc_id, res), over) } pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) + Ok(MemoryPointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) } } @@ -171,21 +171,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.alloc_map.iter() } - pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> Pointer { + pub fn create_fn_alloc(&mut self, instance: ty::Instance<'tcx>) -> MemoryPointer { if let Some(&alloc_id) = self.function_alloc_cache.get(&instance) { - return Pointer::new(alloc_id, 0); + return MemoryPointer::new(alloc_id, 0); } let id = self.next_id; debug!("creating fn ptr: {}", id); self.next_id.0 += 1; self.functions.insert(id, instance); self.function_alloc_cache.insert(instance, id); - Pointer::new(id, 0) + MemoryPointer::new(id, 0) } - pub fn allocate_cached(&mut self, bytes: &[u8]) -> EvalResult<'tcx, Pointer> { + pub fn allocate_cached(&mut self, bytes: &[u8]) -> EvalResult<'tcx, MemoryPointer> { if let Some(&alloc_id) = self.literal_alloc_cache.get(bytes) { - return Ok(Pointer::new(alloc_id, 0)); + return Ok(MemoryPointer::new(alloc_id, 0)); } let ptr = self.allocate(bytes.len() as u64, 1)?; @@ -195,7 +195,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(ptr) } - pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, Pointer> { + pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, MemoryPointer> { assert_ne!(align, 0); assert!(align.is_power_of_two()); @@ -218,12 +218,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let id = self.next_id; self.next_id.0 += 1; self.alloc_map.insert(id, alloc); - Ok(Pointer::new(id, 0)) + Ok(MemoryPointer::new(id, 0)) } // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { use std::cmp::min; // TODO(solson): Report error about non-__rust_allocate'd pointer. @@ -243,7 +243,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } // TODO(solson): See comment on `reallocate`. - pub fn deallocate(&mut self, ptr: Pointer, size_and_align: Option<(u64, u64)>) -> EvalResult<'tcx> { + pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>) -> EvalResult<'tcx> { if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::DeallocateNonBasePtr); @@ -329,7 +329,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> { + pub(crate) fn check_bounds(&self, ptr: MemoryPointer, access: bool) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; let allocation_size = alloc.bytes.len() as u64; if ptr.offset > allocation_size { @@ -338,7 +338,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) { + pub(crate) fn mark_packed(&mut self, ptr: MemoryPointer, len: u64) { self.packed.insert(Entry { alloc_id: ptr.alloc_id, packed_start: ptr.offset, @@ -466,7 +466,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, ptr: Pointer) -> EvalResult<'tcx, ty::Instance<'tcx>> { + pub fn get_fn(&self, ptr: MemoryPointer) -> EvalResult<'tcx, ty::Instance<'tcx>> { if ptr.offset != 0 { return Err(EvalError::InvalidFunctionPointer); } @@ -571,7 +571,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { - fn get_bytes_unchecked(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { + fn get_bytes_unchecked(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } @@ -584,7 +584,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(&alloc.bytes[offset..offset + size as usize]) } - fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_unchecked_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { if size == 0 { return Ok(&mut []); } @@ -597,7 +597,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(&mut alloc.bytes[offset..offset + size as usize]) } - fn get_bytes(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { + fn get_bytes(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> { assert_ne!(size, 0); if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); @@ -606,7 +606,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes_unchecked(ptr, size, align) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size, 0); self.clear_relocations(ptr, size)?; self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; @@ -697,7 +697,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> { + pub fn read_c_str(&self, ptr: MemoryPointer) -> EvalResult<'tcx, &[u8]> { let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); let offset = ptr.offset as usize; @@ -738,7 +738,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, PrimVal> { + pub fn read_ptr(&self, ptr: MemoryPointer) -> EvalResult<'tcx, PrimVal> { let size = self.pointer_size(); if self.check_defined(ptr, size).is_err() { return Ok(PrimVal::Undef); @@ -750,12 +750,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = offset as u64; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => Ok(PrimVal::Ptr(Pointer::new(alloc_id, offset))), + Some(&alloc_id) => Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, offset))), None => Ok(PrimVal::Bytes(offset as u128)), } } - pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> { + pub fn write_ptr(&mut self, dest: MemoryPointer, ptr: MemoryPointer) -> EvalResult<'tcx> { self.write_usize(dest, ptr.offset as u64)?; self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id); Ok(()) @@ -791,7 +791,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { + pub fn read_bool(&self, ptr: MemoryPointer) -> EvalResult<'tcx, bool> { let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi())?; match bytes[0] { 0 => Ok(false), @@ -800,7 +800,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx> { + pub fn write_bool(&mut self, ptr: MemoryPointer, b: bool) -> EvalResult<'tcx> { let align = self.layout.i1_align.abi(); self.get_bytes_mut(ptr, 1, align) .map(|bytes| bytes[0] = b as u8) @@ -817,12 +817,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn read_int(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, i128> { + pub fn read_int(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx, i128> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } - pub fn write_int(&mut self, ptr: Pointer, n: i128, size: u64) -> EvalResult<'tcx> { + pub fn write_int(&mut self, ptr: MemoryPointer, n: i128, size: u64) -> EvalResult<'tcx> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -830,12 +830,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_uint(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, u128> { + pub fn read_uint(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx, u128> { let align = self.int_align(size)?; self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } - pub fn write_uint(&mut self, ptr: Pointer, n: u128, size: u64) -> EvalResult<'tcx> { + pub fn write_uint(&mut self, ptr: MemoryPointer, n: u128, size: u64) -> EvalResult<'tcx> { let align = self.int_align(size)?; let endianess = self.endianess(); let b = self.get_bytes_mut(ptr, size, align)?; @@ -843,25 +843,25 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { + pub fn read_isize(&self, ptr: MemoryPointer) -> EvalResult<'tcx, i64> { self.read_int(ptr, self.pointer_size()).map(|i| i as i64) } - pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx> { + pub fn write_isize(&mut self, ptr: MemoryPointer, n: i64) -> EvalResult<'tcx> { let size = self.pointer_size(); self.write_int(ptr, n as i128, size) } - pub fn read_usize(&self, ptr: Pointer) -> EvalResult<'tcx, u64> { + pub fn read_usize(&self, ptr: MemoryPointer) -> EvalResult<'tcx, u64> { self.read_uint(ptr, self.pointer_size()).map(|i| i as u64) } - pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx> { + pub fn write_usize(&mut self, ptr: MemoryPointer, n: u64) -> EvalResult<'tcx> { let size = self.pointer_size(); self.write_uint(ptr, n as u128, size) } - pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx> { + pub fn write_f32(&mut self, ptr: MemoryPointer, f: f32) -> EvalResult<'tcx> { let endianess = self.endianess(); let align = self.layout.f32_align.abi(); let b = self.get_bytes_mut(ptr, 4, align)?; @@ -869,7 +869,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx> { + pub fn write_f64(&mut self, ptr: MemoryPointer, f: f64) -> EvalResult<'tcx> { let endianess = self.endianess(); let align = self.layout.f64_align.abi(); let b = self.get_bytes_mut(ptr, 8, align)?; @@ -877,12 +877,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { + pub fn read_f32(&self, ptr: MemoryPointer) -> EvalResult<'tcx, f32> { self.get_bytes(ptr, 4, self.layout.f32_align.abi()) .map(|b| read_target_f32(self.endianess(), b).unwrap()) } - pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { + pub fn read_f64(&self, ptr: MemoryPointer) -> EvalResult<'tcx, f64> { self.get_bytes(ptr, 8, self.layout.f64_align.abi()) .map(|b| read_target_f64(self.endianess(), b).unwrap()) } @@ -890,7 +890,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Relocations impl<'a, 'tcx> Memory<'a, 'tcx> { - fn relocations(&self, ptr: Pointer, size: u64) + fn relocations(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx, btree_map::Range> { let start = ptr.offset.saturating_sub(self.pointer_size() - 1); @@ -898,7 +898,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(self.get(ptr.alloc_id)?.relocations.range(start..end)) } - fn clear_relocations(&mut self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { + fn clear_relocations(&mut self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { // Find all relocations overlapping the given range. let keys: Vec<_> = self.relocations(ptr, size)?.map(|(&k, _)| k).collect(); if keys.is_empty() { return Ok(()); } @@ -922,7 +922,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { + fn check_relocation_edges(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count(); if overlapping_start + overlapping_end != 0 { @@ -931,7 +931,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn copy_relocations(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx> { + fn copy_relocations(&mut self, src: MemoryPointer, dest: MemoryPointer, size: u64) -> EvalResult<'tcx> { let relocations: Vec<_> = self.relocations(src, size)? .map(|(&offset, &alloc_id)| { // Update relocation offsets for the new positions in the destination allocation. @@ -946,7 +946,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Undefined bytes impl<'a, 'tcx> Memory<'a, 'tcx> { // FIXME(solson): This is a very naive, slow version. - fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: u64) -> EvalResult<'tcx> { + fn copy_undef_mask(&mut self, src: MemoryPointer, dest: MemoryPointer, size: u64) -> EvalResult<'tcx> { // The bits have to be saved locally before writing to dest in case src and dest overlap. assert_eq!(size as usize as u64, size); let mut v = Vec::with_capacity(size as usize); @@ -960,7 +960,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - fn check_defined(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { + fn check_defined(&self, ptr: MemoryPointer, size: u64) -> EvalResult<'tcx> { let alloc = self.get(ptr.alloc_id)?; if !alloc.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) { return Err(EvalError::ReadUndefBytes); diff --git a/src/operator.rs b/src/operator.rs index e1362f52eedda..c0369a785b1f2 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -3,7 +3,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; -use memory::Pointer; +use memory::MemoryPointer; use lvalue::Lvalue; use value::{ PrimVal, @@ -297,13 +297,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn ptr_int_arithmetic( &self, bin_op: mir::BinOp, - left: Pointer, + left: MemoryPointer, right: i128, signed: bool, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - fn map_to_primval((res, over) : (Pointer, bool)) -> (PrimVal, bool) { + fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) { (PrimVal::Ptr(res), over) } @@ -321,7 +321,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let right = right as u64; if right & base_mask == base_mask { // Case 1: The base address bits are all preserved, i.e., right is all-1 there - (PrimVal::Ptr(Pointer::new(left.alloc_id, left.offset & right)), false) + (PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left.offset & right)), false) } else if right & base_mask == 0 { // Case 2: The base address bits are all taken away, i.e., right is all-0 there (PrimVal::from_u128((left.offset & right) as u128), false) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 38fd465474173..ad79767afa256 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{Pointer, TlsKey}; +use memory::{MemoryPointer, TlsKey}; use value::PrimVal; use value::Value; use rustc_data_structures::indexed_vec::Idx; @@ -461,7 +461,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(false) } - pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; trace!("read_discriminant_value {:#?}", adt_layout); @@ -500,7 +500,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(discr_val) } - fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> { + fn read_nonnull_discriminant_value(&self, ptr: MemoryPointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> { trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size); let not_null = match self.memory.read_uint(ptr, discr_size) { Ok(0) => false, diff --git a/src/traits.rs b/src/traits.rs index 680776968f989..bfb923510bbb3 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,7 +1,7 @@ use rustc::traits::{self, Reveal}; use eval_context::EvalContext; -use memory::Pointer; +use memory::MemoryPointer; use value::{Value, PrimVal}; use rustc::hir::def_id::DefId; @@ -43,7 +43,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// The `trait_ref` encodes the erased self type. Hence if we are /// making an object `Foo` from a value of type `Foo`, then /// `trait_ref` would map `T:Trait`. - pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, Pointer> { + pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'tcx, MemoryPointer> { debug!("get_vtable(trait_ref={:?})", trait_ref); let size = self.type_size(trait_ref.self_ty())?.expect("can't create a vtable for an unsized type"); @@ -73,7 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(vtable) } - pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option>> { + pub fn read_drop_type_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'tcx, Option>> { // we don't care about the pointee type, we just want a pointer match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? { // some values don't need to call a drop impl, so the value is null @@ -83,7 +83,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { + pub fn read_size_and_align_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); let size = self.memory.read_usize(vtable.offset(pointer_size, self.memory.layout)?)?; let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.memory.layout)?)?; diff --git a/src/value.rs b/src/value.rs index 6f531b126483a..a783f98936440 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,7 @@ use std::mem::transmute; use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; -use memory::{Memory, Pointer}; +use memory::{Memory, MemoryPointer}; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { unsafe { transmute::(bytes as u32) } @@ -49,8 +49,8 @@ pub enum PrimVal { /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `PrimVal` is only large enough to contain one, so we just represent the - /// relocation and its associated offset together as a `Pointer` here. - Ptr(Pointer), + /// relocation and its associated offset together as a `MemoryPointer` here. + Ptr(MemoryPointer), /// An undefined `PrimVal`, for representing values that aren't safe to examine, but are safe /// to copy around, just like undefined bytes in an `Allocation`. @@ -80,7 +80,7 @@ impl<'a, 'tcx: 'a> Value { pub(super) fn expect_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> - ) -> EvalResult<'tcx, (PrimVal, Pointer)> { + ) -> EvalResult<'tcx, (PrimVal, MemoryPointer)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -146,7 +146,7 @@ impl<'tcx> PrimVal { } } - pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { match self { PrimVal::Bytes(_) => Err(EvalError::ReadBytesAsPointer), PrimVal::Ptr(p) => Ok(p), From a8b957a0f86c1d7b6636caaad006a8f538a317dd Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 4 Jul 2017 14:26:27 +0200 Subject: [PATCH 1061/1096] Add a dedicated `write_null` method --- src/eval_context.rs | 12 ++++++++++-- src/step.rs | 3 +-- src/terminator/mod.rs | 22 +++++++++++----------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index c4c89427b40ba..57c8b353bfb9b 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1079,6 +1079,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + pub(super) fn write_null( + &mut self, + dest: Lvalue<'tcx>, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx> { + self.write_primval(dest, PrimVal::Bytes(0), dest_ty) + } + pub(super) fn write_primval( &mut self, dest: Lvalue<'tcx>, @@ -1696,12 +1704,12 @@ pub fn eval_main<'a, 'tcx: 'a>( // Second argument (argc): 0 let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; let ty = ecx.tcx.types.isize; - ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?; + ecx.write_null(dest, ty)?; // Third argument (argv): 0 let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?; let ty = ecx.tcx.mk_imm_ptr(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8)); - ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?; + ecx.write_null(dest, ty)?; } else { ecx.push_stack_frame( main_instance, diff --git a/src/step.rs b/src/step.rs index e2e0d81d0607a..77d0f962c9645 100644 --- a/src/step.rs +++ b/src/step.rs @@ -99,9 +99,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Layout::RawNullablePointer { nndiscr, .. } => { - use value::PrimVal; if variant_index as u64 != nndiscr { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ad79767afa256..63ab17514b966 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -652,7 +652,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "malloc" => { let size = self.value_to_primval(args[0], usize)?.to_u64()?; if size == 0 { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } else { let align = self.memory.pointer_size(); let ptr = self.memory.allocate(size, align)?; @@ -690,7 +690,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let f = args[0].read_ptr(&self.memory)?.to_ptr()?; let data = args[1].read_ptr(&self.memory)?; let f_instance = self.memory.get_fn(f)?; - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, // and of course eval_main. @@ -708,7 +708,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(arg_dest, data, u8_ptr_ty)?; // We ourselves return 0 - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; // Don't fall through return Ok(()); @@ -746,7 +746,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; self.write_primval(dest, new_ptr, dest_ty)?; } else { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } } @@ -758,7 +758,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; self.write_primval(dest, new_ptr, dest_ty)?; } else { - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } } @@ -789,7 +789,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(var) = old { self.memory.deallocate(var, None)?; } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } else { self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; } @@ -816,7 +816,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { self.memory.deallocate(var, None)?; } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } else { self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; } @@ -897,14 +897,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?; // Return success (0) - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } "pthread_key_delete" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; self.memory.delete_tls_key(key)?; // Return success (0) - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } "pthread_getspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t @@ -919,13 +919,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.store_tls(key, new_ptr)?; // Return success (0) - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } // Stub out all the other pthread calls to just return 0 link_name if link_name.starts_with("pthread_") => { warn!("ignoring C ABI call: {}", link_name); - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; }, _ => { From 03f0a88002ad7c7ace7db5960cdb90150fd8ea29 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 4 Jul 2017 14:33:15 +0200 Subject: [PATCH 1062/1096] Use a wrapper type to differentiate between PrimVal and pointers --- src/eval_context.rs | 89 ++++++++++++++++++++----------------- src/lvalue.rs | 14 +++--- src/memory.rs | 48 ++++++++++---------- src/operator.rs | 4 +- src/terminator/drop.rs | 6 +-- src/terminator/intrinsic.rs | 13 +++--- src/terminator/mod.rs | 17 ++++--- src/value.rs | 87 ++++++++++++++++++++++++++++-------- 8 files changed, 170 insertions(+), 108 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 57c8b353bfb9b..8a1e991628f6d 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -19,7 +19,7 @@ use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; use memory::{Memory, MemoryPointer, TlsKey}; use operator; -use value::{PrimVal, PrimValKind, Value}; +use value::{PrimVal, PrimValKind, Value, Pointer}; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -395,7 +395,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_primval(dest, ptr, ty)?; + self.write_ptr(dest, ptr, ty)?; } } } @@ -444,7 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { - ptr: PrimVal::Ptr(dest_ptr), + ptr: dest_ptr.into(), extra: LvalueExtra::DowncastVariant(variant_idx), }; @@ -580,7 +580,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -662,7 +662,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?); + let dest = Pointer::from(self.force_allocation(dest)?.to_ptr()?); for i in 0..length { let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; @@ -686,9 +686,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.lvalue_ty(lvalue); let val = match extra { - LvalueExtra::None => Value::ByVal(ptr), - LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u128(len as u128)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), + LvalueExtra::None => ptr.to_value(), + LvalueExtra::Length(len) => ptr.with_extra(PrimVal::from_u128(len as u128)), + LvalueExtra::Vtable(vtable) => ptr.with_extra(PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -928,14 +928,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { + pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; ptr.wrapping_signed_offset(offset, self.memory.layout) } - pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { + pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // This function raises an error if the offset moves the pointer outside of its allocation. We consider // ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0). // We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own @@ -949,7 +949,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; // Do not do bounds-checking for integers; they can never alias a normal pointer anyway. - if let PrimVal::Ptr(ptr) = ptr { + if let PrimVal::Ptr(ptr) = ptr.into_inner_primval() { self.memory.check_bounds(ptr, false)?; } else if ptr.is_null()? { // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. @@ -1002,7 +1002,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } - fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { + fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); let align = self.type_align(ty)?; self.memory.copy(src, dest, size, align, false)?; @@ -1026,8 +1026,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(PrimVal::Ptr(ptr))); // it stays live - self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; + self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr.into())); // it stays live + self.write_value_to_ptr(val, ptr.into(), ty)?; Lvalue::from_ptr(ptr) } } @@ -1040,14 +1040,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); - self.write_value_to_ptr(global_val.value, PrimVal::Ptr(ptr), global_val.ty)?; + self.write_value_to_ptr(global_val.value, ptr.into(), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { - value: Value::ByRef(PrimVal::Ptr(ptr)), + value: Value::ByRef(ptr.into()), .. global_val }; Lvalue::from_ptr(ptr) @@ -1087,6 +1087,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(0), dest_ty) } + pub(super) fn write_ptr( + &mut self, + dest: Lvalue<'tcx>, + val: Pointer, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx> { + self.write_value(val.to_value(), dest, dest_ty) + } + pub(super) fn write_primval( &mut self, dest: Lvalue<'tcx>, @@ -1172,9 +1181,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { write_dest(self, src_val)?; } else { - let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, PrimVal::Ptr(dest_ptr), dest_ty)?; - write_dest(self, Value::ByRef(PrimVal::Ptr(dest_ptr)))?; + let dest_ptr = self.alloc_ptr(dest_ty)?.into(); + self.copy(src_ptr, dest_ptr, dest_ty)?; + write_dest(self, Value::ByRef(dest_ptr))?; } } else { @@ -1188,7 +1197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn write_value_to_ptr( &mut self, value: Value, - dest: PrimVal, + dest: Pointer, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { @@ -1218,8 +1227,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?; - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?; + self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?.into(), a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?.into(), b, field_1_size)?; Ok(()) } @@ -1322,7 +1331,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, ty)? { Ok(val) } else { @@ -1333,21 +1342,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn read_ptr(&self, ptr: MemoryPointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(pointee_ty) { - Ok(Value::ByVal(p)) + Ok(p.to_value()) } else { trace!("reading fat pointer extra of type {}", pointee_ty); let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; let extra = match self.tcx.struct_tail(pointee_ty).sty { - ty::TyDynamic(..) => self.memory.read_ptr(extra)?, + ty::TyDynamic(..) => self.memory.read_ptr(extra)?.into_inner_primval(), ty::TySlice(..) | ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), _ => bug!("unsized primval ptr read from {:?}", pointee_ty), }; - Ok(Value::ByValPair(p, extra)) + Ok(p.with_extra(extra)) } } - fn try_read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; let val = match ty.sty { @@ -1373,7 +1382,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // if we transmute a ptr to an isize, reading it back into a primval shouldn't panic // Due to read_ptr ignoring the sign, we need to jump around some hoops match self.memory.read_int(ptr.to_ptr()?, size) { - Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?, + Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(), other => PrimVal::from_i128(other?), } } @@ -1390,7 +1399,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; if size == self.memory.pointer_size() { // if we transmute a ptr to an usize, reading it back into a primval shouldn't panic - self.memory.read_ptr(ptr.to_ptr()?)? + self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval() } else { PrimVal::from_u128(self.memory.read_uint(ptr.to_ptr()?, size)?) } @@ -1399,7 +1408,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr.to_ptr()?)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr.to_ptr()?)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr.to_ptr()?)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(), ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => return self.read_ptr(ptr.to_ptr()?, tam.ty).map(Some), @@ -1458,7 +1467,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; let len = PrimVal::from_u128(length as u128); - self.write_value(Value::ByValPair(ptr, len), dest, dest_ty) + self.write_value(ptr.with_extra(len), dest, dest_ty) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker @@ -1472,7 +1481,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; let extra = PrimVal::Ptr(vtable); - self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty) + self.write_value(ptr.with_extra(extra), dest, dest_ty) }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), @@ -1532,7 +1541,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { - self.copy(src_f_ptr, PrimVal::Ptr(dst_f_ptr), src_fty)?; + self.copy(src_f_ptr, dst_f_ptr.into(), src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } @@ -1561,13 +1570,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(err) => { panic!("Failed to access local: {:?}", err); } - Ok(Value::ByRef(PrimVal::Ptr(ptr))) => { - write!(msg, " by ref:").unwrap(); - allocs.push(ptr.alloc_id); - } - Ok(Value::ByRef(ptr)) => { - write!(msg, " integral by ref: {:?}", ptr).unwrap(); - } + Ok(Value::ByRef(ptr)) => match ptr.into_inner_primval() { + PrimVal::Ptr(ptr) => { + write!(msg, " by ref:").unwrap(); + allocs.push(ptr.alloc_id); + }, + ptr => write!(msg, " integral by ref: {:?}", ptr).unwrap(), + }, Ok(Value::ByVal(val)) => { write!(msg, " {:?}", val).unwrap(); if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } diff --git a/src/lvalue.rs b/src/lvalue.rs index de7d4d917160b..3eafe1d0eabb2 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -6,7 +6,7 @@ use rustc_data_structures::indexed_vec::Idx; use error::{EvalError, EvalResult}; use eval_context::{EvalContext}; use memory::MemoryPointer; -use value::{PrimVal, Value}; +use value::{PrimVal, Value, Pointer}; #[derive(Copy, Clone, Debug)] pub enum Lvalue<'tcx> { @@ -15,7 +15,7 @@ pub enum Lvalue<'tcx> { /// An lvalue may have an invalid (integral or undef) pointer, /// since it might be turned back into a reference /// before ever being dereferenced. - ptr: PrimVal, + ptr: Pointer, extra: LvalueExtra, }, @@ -64,18 +64,18 @@ pub struct Global<'tcx> { impl<'tcx> Lvalue<'tcx> { /// Produces an Lvalue that will error if attempted to be read from pub fn undef() -> Self { - Self::from_primval_ptr(PrimVal::Undef) + Self::from_primval_ptr(PrimVal::Undef.into()) } - pub(crate) fn from_primval_ptr(ptr: PrimVal) -> Self { + pub(crate) fn from_primval_ptr(ptr: Pointer) -> Self { Lvalue::Ptr { ptr, extra: LvalueExtra::None } } pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self { - Self::from_primval_ptr(PrimVal::Ptr(ptr)) + Self::from_primval_ptr(ptr.into()) } - pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) { + pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { match self { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), @@ -315,7 +315,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = match base_extra { LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?; + let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.with_extra(PrimVal::Ptr(tab)))?; offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() } _ => offset.bytes(), diff --git a/src/memory.rs b/src/memory.rs index 568da336f6696..caf7f3a651614 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,7 +6,7 @@ use rustc::ty; use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; -use value::{PrimVal, self}; +use value::{PrimVal, self, Pointer}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -87,7 +87,7 @@ pub type TlsKey = usize; #[derive(Copy, Clone, Debug)] pub struct TlsEntry<'tcx> { - data: PrimVal, // Will eventually become a map from thread IDs to `PrimVal`s, if we ever support more than one thread. + data: Pointer, // Will eventually become a map from thread IDs to `Pointer`s, if we ever support more than one thread. dtor: Option>, } @@ -223,7 +223,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, MemoryPointer> { use std::cmp::min; // TODO(solson): Report error about non-__rust_allocate'd pointer. @@ -236,7 +236,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" let new_ptr = self.allocate(new_size, new_align)?; - self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; + self.copy(ptr.into(), new_ptr.into(), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; self.deallocate(ptr, Some((old_size, old_align)))?; Ok(new_ptr) @@ -278,8 +278,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: PrimVal, align: u64, len: u64) -> EvalResult<'tcx> { - let offset = match ptr { + pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx> { + let offset = match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { let alloc = self.get(ptr.alloc_id)?; // check whether the memory was marked as packed @@ -353,7 +353,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn create_tls_key(&mut self, dtor: Option>) -> TlsKey { let new_key = self.next_thread_local; self.next_thread_local += 1; - self.thread_local.insert(new_key, TlsEntry { data: PrimVal::Bytes(0), dtor }); + self.thread_local.insert(new_key, TlsEntry { data: Pointer::null(), dtor }); trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); return new_key; } @@ -368,7 +368,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, PrimVal> { + pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> { return match self.thread_local.get(&key) { Some(&TlsEntry { data, .. }) => { trace!("TLS key {} loaded: {:?}", key, data); @@ -378,7 +378,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: PrimVal) -> EvalResult<'tcx> { + pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> { return match self.thread_local.get_mut(&key) { Some(&mut TlsEntry { ref mut data, .. }) => { trace!("TLS key {} stored: {:?}", key, new_data); @@ -407,7 +407,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// with associated destructors, implementations may stop calling destructors, /// or they may continue calling destructors until no non-NULL values with /// associated destructors exist, even though this might result in an infinite loop. - pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, PrimVal, TlsKey)>> { + pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>> { use std::collections::Bound::*; let start = match key { Some(key) => Excluded(key), @@ -417,7 +417,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if !data.is_null()? { if let Some(dtor) = dtor { let ret = Some((dtor, *data, key)); - *data = PrimVal::Bytes(0); + *data = Pointer::null(); return Ok(ret); } } @@ -575,7 +575,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_align(PrimVal::Ptr(ptr), align, size)?; + // FIXME: check alignment for zst memory accesses? + self.check_align(ptr.into(), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -588,7 +589,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&mut []); } - self.check_align(PrimVal::Ptr(ptr), align, size)?; + // FIXME: check alignment for zst memory accesses? + self.check_align(ptr.into(), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -609,7 +611,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size, 0); self.clear_relocations(ptr, size)?; - self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; + self.mark_definedness(ptr.into(), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) } } @@ -661,7 +663,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { if size == 0 { // TODO: Should we check for alignment here? (Also see write_bytes intrinsic) return Ok(()); @@ -713,7 +715,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn read_bytes(&self, ptr: PrimVal, size: u64) -> EvalResult<'tcx, &[u8]> { + pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } @@ -729,7 +731,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_repeat(&mut self, ptr: PrimVal, val: u8, count: u64) -> EvalResult<'tcx> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { if count == 0 { return Ok(()); } @@ -738,10 +740,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_ptr(&self, ptr: MemoryPointer) -> EvalResult<'tcx, PrimVal> { + pub fn read_ptr(&self, ptr: MemoryPointer) -> EvalResult<'tcx, Pointer> { let size = self.pointer_size(); if self.check_defined(ptr, size).is_err() { - return Ok(PrimVal::Undef); + return Ok(PrimVal::Undef.into()); } let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size, size)?; @@ -750,8 +752,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = offset as u64; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, offset))), - None => Ok(PrimVal::Bytes(offset as u128)), + Some(&alloc_id) => Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, offset)).into()), + None => Ok(PrimVal::Bytes(offset as u128).into()), } } @@ -763,7 +765,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_primval( &mut self, - dest: PrimVal, + dest: Pointer, val: PrimVal, size: u64, ) -> EvalResult<'tcx> { @@ -970,7 +972,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness( &mut self, - ptr: PrimVal, + ptr: Pointer, size: u64, new_state: bool ) -> EvalResult<'tcx> { diff --git a/src/operator.rs b/src/operator.rs index c0369a785b1f2..47bd66641b1e8 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -158,8 +158,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match bin_op { Offset if left_kind == Ptr && right_kind == usize => { let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; - return Ok((ptr, false)); + let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?; + return Ok((ptr.into_inner_primval(), false)); }, // These work on anything Eq if left_kind == right_kind => { diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 072a5d16a1bea..e035aa58fd152 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -12,9 +12,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(ptr, PrimVal::Bytes(len as u128)), - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(ptr), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.with_extra(PrimVal::Ptr(vtable)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.with_extra(PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => ptr.to_value(), _ => bug!("force_allocation broken"), }; self.drop(val, instance, ty, span) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index f2b0446618a06..94c1bac459d6e 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -7,7 +7,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::{Lvalue, LvalueExtra}; -use value::{PrimVal, PrimValKind, Value}; +use value::{PrimVal, PrimValKind, Value, Pointer}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, result_ptr, dest_ty)?; + self.write_ptr(dest, result_ptr, dest_ty)?; } "assume" => { @@ -257,8 +257,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; - this.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; - Value::ByRef(PrimVal::Ptr(ptr)) + let ptr = Pointer::from(PrimVal::Ptr(ptr)); + this.memory.write_repeat(ptr, 0, size)?; + Value::ByRef(ptr) } }, Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)), @@ -307,7 +308,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, result_ptr, dest_ty)?; + self.write_ptr(dest, result_ptr, dest_ty)?; } "overflowing_sub" => { @@ -397,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); let ptr = self.force_allocation(dest)?.to_ptr()?; self.memory.mark_packed(ptr, size); - self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), src_ty)?; + self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; } "unchecked_shl" => { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 63ab17514b966..9602e5798a7bc 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -10,8 +10,7 @@ use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; use memory::{MemoryPointer, TlsKey}; -use value::PrimVal; -use value::Value; +use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; mod drop; @@ -569,7 +568,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } let ptr = self.memory.allocate(size, align)?; - self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; + self.memory.write_repeat(ptr.into(), 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_dealloc" => { @@ -705,7 +704,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_primval(arg_dest, data, u8_ptr_ty)?; + self.write_ptr(arg_dest, data, u8_ptr_ty)?; // We ourselves return 0 self.write_null(dest, dest_ty)?; @@ -744,7 +743,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; - self.write_primval(dest, new_ptr, dest_ty)?; + self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; } @@ -756,7 +755,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; - self.write_primval(dest, new_ptr, dest_ty)?; + self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; } @@ -865,7 +864,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "mmap" => { // This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value let addr = args[0].read_ptr(&self.memory)?; - self.write_primval(dest, addr, dest_ty)?; + self.write_ptr(dest, addr, dest_ty)?; } // Hook pthread calls that go to the thread-local storage memory subsystem @@ -873,7 +872,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let key_ptr = args[0].read_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor = match args[1].read_ptr(&self.memory)? { + let dtor = match args[1].read_ptr(&self.memory)?.into_inner_primval() { PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), @@ -910,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; let ptr = self.memory.load_tls(key)?; - self.write_primval(dest, ptr, dest_ty)?; + self.write_ptr(dest, ptr, dest_ty)?; } "pthread_setspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t diff --git a/src/value.rs b/src/value.rs index a783f98936440..e0937fcb83610 100644 --- a/src/value.rs +++ b/src/value.rs @@ -33,11 +33,70 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug)] pub enum Value { - ByRef(PrimVal), + ByRef(Pointer), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } +/// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. +/// This type clears up a few APIs where having a `PrimVal` argument for something that is +/// potentially an integer pointer or a pointer to an allocation was unclear. +#[derive(Clone, Copy, Debug)] +pub struct Pointer { + primval: PrimVal, +} + +impl<'tcx> Pointer { + pub fn null() -> Self { + PrimVal::Bytes(0).into() + } + pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { + self.primval.to_ptr() + } + pub fn into_inner_primval(self) -> PrimVal { + self.primval + } + + pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.signed_offset(i, layout).map(Pointer::from) + } + + pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.offset(i, layout).map(Pointer::from) + } + + pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.wrapping_signed_offset(i, layout).map(Pointer::from) + } + + pub fn is_null(self) -> EvalResult<'tcx, bool> { + match self.primval { + PrimVal::Bytes(b) => Ok(b == 0), + PrimVal::Ptr(_) => Ok(false), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + + pub fn with_extra(self, extra: PrimVal) -> Value { + Value::ByValPair(self.primval, extra) + } + pub fn to_value(self) -> Value { + Value::ByVal(self.primval) + } +} + +impl ::std::convert::From for Pointer { + fn from(primval: PrimVal) -> Self { + Pointer { primval } + } +} + +impl ::std::convert::From for Pointer { + fn from(ptr: MemoryPointer) -> Self { + PrimVal::Ptr(ptr).into() + } +} + /// A `PrimVal` represents an immediate, primitive value existing outside of a /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes @@ -69,18 +128,18 @@ pub enum PrimValKind { } impl<'a, 'tcx: 'a> Value { - pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { + pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr.to_ptr()?), - ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr), + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()), } } pub(super) fn expect_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> - ) -> EvalResult<'tcx, (PrimVal, MemoryPointer)> { + ) -> EvalResult<'tcx, (Pointer, MemoryPointer)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -89,13 +148,13 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, vtable.to_ptr()?)) } - ByValPair(ptr, vtable) => Ok((ptr, vtable.to_ptr()?)), + ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, u64)> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -106,7 +165,7 @@ impl<'a, 'tcx: 'a> Value { ByValPair(ptr, val) => { let len = val.to_u128()?; assert_eq!(len as u64 as u128, len); - Ok((ptr, len as u64)) + Ok((ptr.into(), len as u64)) }, ByVal(_) => unimplemented!(), } @@ -220,15 +279,7 @@ impl<'tcx> PrimVal { } } - pub fn is_null(self) -> EvalResult<'tcx, bool> { - match self { - PrimVal::Bytes(b) => Ok(b == 0), - PrimVal::Ptr(_) => Ok(false), - PrimVal::Undef => Err(EvalError::ReadUndefBytes), - } - } - - pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); @@ -239,7 +290,7 @@ impl<'tcx> PrimVal { } } - pub fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); @@ -250,7 +301,7 @@ impl<'tcx> PrimVal { } } - pub fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); From d0d1d2d5e8a1e95ff947a5337cb662d29bf44952 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 4 Jul 2017 14:47:46 +0200 Subject: [PATCH 1063/1096] Remove `*offset*` methods from `PrimVal` onto `Pointer` --- src/value.rs | 60 +++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/value.rs b/src/value.rs index e0937fcb83610..a69c84aa083e0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -58,15 +58,36 @@ impl<'tcx> Pointer { } pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - self.primval.signed_offset(i, layout).map(Pointer::from) + match self.primval { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(Pointer::from(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128))) + }, + PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(Pointer::from), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } } pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - self.primval.offset(i, layout).map(Pointer::from) + match self.primval { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(Pointer::from(PrimVal::Bytes(offset(b as u64, i, layout)? as u128))) + }, + PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } } pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - self.primval.wrapping_signed_offset(i, layout).map(Pointer::from) + match self.primval { + PrimVal::Bytes(b) => { + assert_eq!(b as u64 as u128, b); + Ok(Pointer::from(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128))) + }, + PrimVal::Ptr(ptr) => Ok(Pointer::from(ptr.wrapping_signed_offset(i, layout))), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } } pub fn is_null(self) -> EvalResult<'tcx, bool> { @@ -278,39 +299,6 @@ impl<'tcx> PrimVal { _ => Err(EvalError::InvalidBool), } } - - pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - match self { - PrimVal::Bytes(b) => { - assert_eq!(b as u64 as u128, b); - Ok(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128)) - }, - PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(PrimVal::Ptr), - PrimVal::Undef => Err(EvalError::ReadUndefBytes), - } - } - - pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - match self { - PrimVal::Bytes(b) => { - assert_eq!(b as u64 as u128, b); - Ok(PrimVal::Bytes(offset(b as u64, i, layout)? as u128)) - }, - PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(PrimVal::Ptr), - PrimVal::Undef => Err(EvalError::ReadUndefBytes), - } - } - - pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - match self { - PrimVal::Bytes(b) => { - assert_eq!(b as u64 as u128, b); - Ok(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128)) - }, - PrimVal::Ptr(ptr) => Ok(PrimVal::Ptr(ptr.wrapping_signed_offset(i, layout))), - PrimVal::Undef => Err(EvalError::ReadUndefBytes), - } - } } // Overflow checking only works properly on the range from -u64 to +u64. From 0dd6ef8301df00293f944c74b5793c25f55bdf13 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 11 Jul 2017 12:39:12 +0200 Subject: [PATCH 1064/1096] Simplify `with_extra` --- src/eval_context.rs | 20 +++++++++----------- src/lvalue.rs | 2 +- src/terminator/drop.rs | 4 ++-- src/value.rs | 9 +++++++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 8a1e991628f6d..e5b473a70e3bd 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -687,8 +687,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = match extra { LvalueExtra::None => ptr.to_value(), - LvalueExtra::Length(len) => ptr.with_extra(PrimVal::from_u128(len as u128)), - LvalueExtra::Vtable(vtable) => ptr.with_extra(PrimVal::Ptr(vtable)), + LvalueExtra::Length(len) => ptr.to_value_with_len(len), + LvalueExtra::Vtable(vtable) => ptr.to_value_with_vtable(vtable), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -1346,13 +1346,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { trace!("reading fat pointer extra of type {}", pointee_ty); let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; - let extra = match self.tcx.struct_tail(pointee_ty).sty { - ty::TyDynamic(..) => self.memory.read_ptr(extra)?.into_inner_primval(), + match self.tcx.struct_tail(pointee_ty).sty { + ty::TyDynamic(..) => Ok(p.to_value_with_vtable(self.memory.read_ptr(extra)?.to_ptr()?)), ty::TySlice(..) | - ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), + ty::TyStr => Ok(p.to_value_with_len(self.memory.read_usize(extra)?)), _ => bug!("unsized primval ptr read from {:?}", pointee_ty), - }; - Ok(p.with_extra(extra)) + } } } @@ -1466,8 +1465,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - let len = PrimVal::from_u128(length as u128); - self.write_value(ptr.with_extra(len), dest, dest_ty) + // u64 cast is from usize to u64, which is always good + self.write_value(ptr.to_value_with_len(length as u64), dest, dest_ty) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker @@ -1480,8 +1479,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - let extra = PrimVal::Ptr(vtable); - self.write_value(ptr.with_extra(extra), dest, dest_ty) + self.write_value(ptr.to_value_with_vtable(vtable), dest, dest_ty) }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), diff --git a/src/lvalue.rs b/src/lvalue.rs index 3eafe1d0eabb2..f2581379ea174 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -315,7 +315,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = match base_extra { LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.with_extra(PrimVal::Ptr(tab)))?; + let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.to_value_with_vtable(tab))?; offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() } _ => offset.bytes(), diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index e035aa58fd152..f69f99f3d28c4 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -12,8 +12,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.with_extra(PrimVal::Ptr(vtable)), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.with_extra(PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.to_value_with_vtable(vtable), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.to_value_with_len(len), Lvalue::Ptr { ptr, extra: LvalueExtra::None } => ptr.to_value(), _ => bug!("force_allocation broken"), }; diff --git a/src/value.rs b/src/value.rs index a69c84aa083e0..7e178afde269c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -98,9 +98,14 @@ impl<'tcx> Pointer { } } - pub fn with_extra(self, extra: PrimVal) -> Value { - Value::ByValPair(self.primval, extra) + pub fn to_value_with_len(self, len: u64) -> Value { + Value::ByValPair(self.primval, PrimVal::from_u128(len as u128)) } + + pub fn to_value_with_vtable(self, vtable: MemoryPointer) -> Value { + Value::ByValPair(self.primval, PrimVal::Ptr(vtable)) + } + pub fn to_value(self) -> Value { Value::ByVal(self.primval) } From eba199a437116570d10f902b4176d4a01662cbd9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 11 Jul 2017 12:50:03 +0200 Subject: [PATCH 1065/1096] Document the reason for `Pointer`'s existence --- src/value.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/value.rs b/src/value.rs index 7e178afde269c..4bda56a2877f9 100644 --- a/src/value.rs +++ b/src/value.rs @@ -41,6 +41,10 @@ pub enum Value { /// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. /// This type clears up a few APIs where having a `PrimVal` argument for something that is /// potentially an integer pointer or a pointer to an allocation was unclear. +/// +/// I (@oli-obk) believe it is less easy to mix up generic primvals and primvals that are just +/// the representation of pointers. Also all the sites that convert between primvals and pointers +/// are explicit now (and rare!) #[derive(Clone, Copy, Debug)] pub struct Pointer { primval: PrimVal, From 9a9666e2a67757a8c4ce1b37790e46cc16d78169 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 11 Jul 2017 10:24:34 -0700 Subject: [PATCH 1066/1096] Add a test for using a too big alignment on reallocate --- .../compile-fail/reallocate-bad-alignment-2.rs | 17 +++++++++++++++++ tests/compile-fail/reallocate-bad-alignment.rs | 1 + 2 files changed, 18 insertions(+) create mode 100644 tests/compile-fail/reallocate-bad-alignment-2.rs diff --git a/tests/compile-fail/reallocate-bad-alignment-2.rs b/tests/compile-fail/reallocate-bad-alignment-2.rs new file mode 100644 index 0000000000000..41da885a2c65a --- /dev/null +++ b/tests/compile-fail/reallocate-bad-alignment-2.rs @@ -0,0 +1,17 @@ +#![feature(alloc, allocator_api)] + +extern crate alloc; + +use alloc::heap::Heap; +use alloc::allocator::*; + +// error-pattern: tried to deallocate or reallocate using incorrect alignment or size + +use alloc::heap::*; +fn main() { + unsafe { + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + // Try realloc with a too big alignment. + let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 2), Layout::from_size_align_unchecked(1, 1)).unwrap(); + } +} diff --git a/tests/compile-fail/reallocate-bad-alignment.rs b/tests/compile-fail/reallocate-bad-alignment.rs index 246d559295774..be4bc5589c5c6 100644 --- a/tests/compile-fail/reallocate-bad-alignment.rs +++ b/tests/compile-fail/reallocate-bad-alignment.rs @@ -11,6 +11,7 @@ use alloc::heap::*; fn main() { unsafe { let x = Heap.alloc(Layout::from_size_align_unchecked(1, 2)).unwrap(); + // Try realloc with a too small alignment. let _y = Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 2)).unwrap(); } } From 3e7f69aae251153a6306d079f813f1c71cf3c3eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 11 Jul 2017 10:25:05 -0700 Subject: [PATCH 1067/1096] add little script to build libstd That's easier to use than having to `cd xargo` --- .travis.yml | 4 +--- xargo/build.sh | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100755 xargo/build.sh diff --git a/.travis.yml b/.travis.yml index 5bc7f291d7629..d9dd443d7ac1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,9 +12,7 @@ before_script: script: - | # get ourselves a MIR-ful libstd - cd xargo && - RUSTFLAGS='-Zalways-encode-mir' xargo build && - cd .. + xargo/build.sh - | # Test plain miri cargo build && diff --git a/xargo/build.sh b/xargo/build.sh new file mode 100755 index 0000000000000..e744abaadfdfe --- /dev/null +++ b/xargo/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(readlink -e "$(dirname "$0")")" +RUSTFLAGS='-Zalways-encode-mir' xargo build From eafe659ee0600e41ff5d63a51b542fca050f7a4a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 11 Jul 2017 17:24:15 -0700 Subject: [PATCH 1068/1096] hooking mmap is no longer needed --- src/terminator/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9602e5798a7bc..dc6610a2213b0 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -861,12 +861,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, result, dest_ty)?; } - "mmap" => { - // This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value - let addr = args[0].read_ptr(&self.memory)?; - self.write_ptr(dest, addr, dest_ty)?; - } - // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { let key_ptr = args[0].read_ptr(&self.memory)?; From 4a03e45169beb9a2d69518436ecc7850401cce50 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 12 Jul 2017 09:29:18 +0200 Subject: [PATCH 1069/1096] Add tests for #113 resolves #113 --- tests/compile-fail/transmute_fat.rs | 13 +++++++++++++ tests/compile-fail/transmute_fat2.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/compile-fail/transmute_fat.rs create mode 100644 tests/compile-fail/transmute_fat2.rs diff --git a/tests/compile-fail/transmute_fat.rs b/tests/compile-fail/transmute_fat.rs new file mode 100644 index 0000000000000..6b9e6f876481d --- /dev/null +++ b/tests/compile-fail/transmute_fat.rs @@ -0,0 +1,13 @@ +#![feature(i128_type)] + +fn main() { + #[cfg(target_pointer_width="64")] + let bad = unsafe { + std::mem::transmute::<&[u8], u128>(&[1u8]) + }; + #[cfg(target_pointer_width="32")] + let bad = unsafe { + std::mem::transmute::<&[u8], u64>(&[1u8]) + }; + bad + 1; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes +} diff --git a/tests/compile-fail/transmute_fat2.rs b/tests/compile-fail/transmute_fat2.rs new file mode 100644 index 0000000000000..028ed613eee71 --- /dev/null +++ b/tests/compile-fail/transmute_fat2.rs @@ -0,0 +1,13 @@ +#![feature(i128_type)] + +fn main() { + #[cfg(target_pointer_width="64")] + let bad = unsafe { + std::mem::transmute::(42) + }; + #[cfg(target_pointer_width="32")] + let bad = unsafe { + std::mem::transmute::(42) + }; + bad[0]; //~ ERROR index out of bounds: the len is 0 but the index is 0 +} From 4ce8be9538e332cb650983f14a341b6093ba0c34 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 4 Jul 2017 17:03:21 +0200 Subject: [PATCH 1070/1096] Produce `ConstInt` from a `def_id` for rustc --- src/const_eval.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 ++++ 2 files changed, 68 insertions(+) create mode 100644 src/const_eval.rs diff --git a/src/const_eval.rs b/src/const_eval.rs new file mode 100644 index 0000000000000..ff80f68e2c3ff --- /dev/null +++ b/src/const_eval.rs @@ -0,0 +1,63 @@ +use rustc::hir::def_id::DefId; +use rustc::traits::Reveal; +use rustc::ty::subst::Substs; +use rustc::ty::{self, TyCtxt}; + +use error::{EvalError, EvalResult}; +use lvalue::{Global, GlobalId, Lvalue}; +use rustc_const_math::ConstInt; +use eval_context::{EvalContext, StackPopCleanup}; + +pub fn eval_body_as_integer<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + (def_id, substs): (DefId, &'tcx Substs<'tcx>), +) -> EvalResult<'tcx, ConstInt> { + let limits = ::ResourceLimits::default(); + let mut ecx = EvalContext::new(tcx, limits); + let instance = ecx.resolve_associated_const(def_id, substs); + let cid = GlobalId { instance, promoted: None }; + if ecx.tcx.has_attr(def_id, "linkage") { + return Err(EvalError::NotConst("extern global".to_string())); + } + + let mir = ecx.load_mir(instance.def)?; + if !ecx.globals.contains_key(&cid) { + ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); + let mutable = !mir.return_ty.is_freeze( + ecx.tcx, + ty::ParamEnv::empty(Reveal::All), + mir.span); + let cleanup = StackPopCleanup::MarkStatic(mutable); + let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + trace!("pushing stack frame for global: {}", name); + ecx.push_stack_frame( + instance, + mir.span, + mir, + Lvalue::Global(cid), + cleanup, + )?; + + while ecx.step()? {} + } + let value = ecx.globals.get(&cid).expect("global not cached").value; + let prim = ecx.value_to_primval(value, mir.return_ty)?.to_bytes()?; + use syntax::ast::{IntTy, UintTy}; + use rustc::ty::TypeVariants::*; + use rustc_const_math::{ConstIsize, ConstUsize}; + Ok(match mir.return_ty.sty { + TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8), + TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16), + TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32), + TyInt(IntTy::I64) => ConstInt::I64(prim as i128 as i64), + TyInt(IntTy::I128) => ConstInt::I128(prim as i128), + TyInt(IntTy::Is) => ConstInt::Isize(ConstIsize::new(prim as i128 as i64, tcx.sess.target.int_type).expect("miri should already have errored")), + TyUint(UintTy::U8) => ConstInt::U8(prim as u8), + TyUint(UintTy::U16) => ConstInt::U16(prim as u16), + TyUint(UintTy::U32) => ConstInt::U32(prim as u32), + TyUint(UintTy::U64) => ConstInt::U64(prim as u64), + TyUint(UintTy::U128) => ConstInt::U128(prim), + TyUint(UintTy::Us) => ConstInt::Usize(ConstUsize::new(prim as u64, tcx.sess.target.uint_type).expect("miri should already have errored")), + _ => return Err(EvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string())), + }) +} diff --git a/src/lib.rs b/src/lib.rs index bbb271a6b6a79..d87a9ab653583 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ extern crate syntax; extern crate byteorder; mod cast; +mod const_eval; mod error; mod eval_context; mod lvalue; @@ -56,3 +57,7 @@ pub use value::{ PrimValKind, Value, }; + +pub use const_eval::{ + eval_body_as_integer, +}; From c149c3fc6a32148ede8229c39bf23249d8174f4b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Jul 2017 17:46:56 -0700 Subject: [PATCH 1071/1096] Re-do packed memory accesses We now track in the lvalue whether what we computed is expected to be aligend or not, and then set some state in the memory system accordingly to make it (not) do alignment checks --- src/eval_context.rs | 46 +++++++++---------- src/lvalue.rs | 71 ++++++++++++++--------------- src/memory.rs | 80 +++++++-------------------------- src/step.rs | 3 +- src/terminator/drop.rs | 6 +-- src/terminator/intrinsic.rs | 15 +++---- tests/run-pass/packed_struct.rs | 5 ++- 7 files changed, 87 insertions(+), 139 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e5b473a70e3bd..0e63e033cfb78 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -446,6 +446,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = Lvalue::Ptr { ptr: dest_ptr.into(), extra: LvalueExtra::DowncastVariant(variant_idx), + aligned: true, }; self.assign_fields(dest, dest_ty, operands) @@ -496,8 +497,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Rvalue::*; match *rvalue { Use(ref operand) => { - let value = self.eval_operand(operand)?; + let (value, aligned) = self.eval_operand_maybe_unaligned(operand)?; + self.memory.reads_are_aligned = aligned; self.write_value(value, dest, dest_ty)?; + self.memory.reads_are_aligned = true; } BinaryOp(bin_op, ref left, ref right) => { @@ -528,15 +531,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.inc_step_counter_and_check_limit(operands.len() as u64)?; use rustc::ty::layout::Layout::*; match *dest_layout { - Univariant { ref variant, .. } => { - if variant.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, variant.stride().bytes()); - } - self.assign_fields(dest, dest_ty, operands)?; - } - - Array { .. } => { + Univariant { .. } | Array { .. } => { self.assign_fields(dest, dest_ty, operands)?; } @@ -547,10 +542,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("broken mir: Adt variant id invalid") .to_u128_unchecked(); let discr_size = discr.size().bytes(); - if variants[variant].packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, variants[variant].stride().bytes()); - } self.assign_discr_and_fields( dest, @@ -587,12 +578,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => { + StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { if let mir::AggregateKind::Adt(_, variant, _, _) = **kind { - if nonnull.packed { - let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?; - self.memory.mark_packed(ptr, nonnull.stride().bytes()); - } if nndiscr == variant as u64 { self.assign_fields(dest, dest_ty, operands)?; } else { @@ -682,7 +669,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let src = self.eval_lvalue(lvalue)?; - let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra(); + // We ignore the alignment of the lvalue here -- this rvalue produces sth. of type &, which must always be aligned. + let (ptr, extra, _aligned) = self.force_allocation(src)?.to_ptr_extra_aligned(); let ty = self.lvalue_ty(lvalue); let val = match extra { @@ -695,7 +683,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Check alignment and non-NULLness. let (_, align) = self.size_and_align_of_dst(ty, val)?; - self.memory.check_align(ptr, align, 0)?; + self.memory.check_align(ptr, align)?; self.write_value(val, dest, dest_ty)?; } @@ -967,7 +955,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.value_to_primval(value, ty) } - pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn eval_operand_maybe_unaligned(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, (Value, bool)> { use rustc::mir::Operand::*; match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), @@ -993,11 +981,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - Ok(value) + Ok((value, true)) } } } + pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { + // This is called when the packed flag is not taken into account. Ignore alignment. + Ok(self.eval_operand_maybe_unaligned(op)?.0) + } + pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } @@ -1131,9 +1124,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_possibly_by_val(src_val, write_dest, dest.value, dest_ty) }, - Lvalue::Ptr { ptr, extra } => { + Lvalue::Ptr { ptr, extra, aligned } => { assert_eq!(extra, LvalueExtra::None); - self.write_value_to_ptr(src_val, ptr, dest_ty) + self.memory.writes_are_aligned = aligned; + let r = self.write_value_to_ptr(src_val, ptr, dest_ty); + self.memory.writes_are_aligned = true; + r } Lvalue::Local { frame, local } => { diff --git a/src/lvalue.rs b/src/lvalue.rs index f2581379ea174..18ec7a77cb8ce 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -17,6 +17,8 @@ pub enum Lvalue<'tcx> { /// before ever being dereferenced. ptr: Pointer, extra: LvalueExtra, + /// Remember whether this lvalue is *supposed* to be aligned. + aligned: bool, }, /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with @@ -68,24 +70,25 @@ impl<'tcx> Lvalue<'tcx> { } pub(crate) fn from_primval_ptr(ptr: Pointer) -> Self { - Lvalue::Ptr { ptr, extra: LvalueExtra::None } + Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true } } pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self { Self::from_primval_ptr(ptr.into()) } - pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { + pub(super) fn to_ptr_extra_aligned(self) -> (Pointer, LvalueExtra, bool) { match self { - Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned), _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), } } pub(super) fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { - let (ptr, extra) = self.to_ptr_and_extra(); + let (ptr, extra, aligned) = self.to_ptr_extra_aligned(); assert_eq!(extra, LvalueExtra::None); + assert_eq!(aligned, true, "tried converting an unaligned lvalue into a ptr"); ptr.to_ptr() } @@ -175,13 +178,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses. + pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, (Value, bool)> { let ty = self.lvalue_ty(lvalue); // Shortcut for things like accessing a fat pointer's field, // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory // and returning an `Lvalue::Ptr` to it if let Some(val) = self.try_read_lvalue(lvalue)? { - return Ok(val); + return Ok((val, true)); } let lvalue = self.eval_lvalue(lvalue)?; @@ -190,15 +194,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } match lvalue { - Lvalue::Ptr { ptr, extra } => { + Lvalue::Ptr { ptr, extra, aligned } => { assert_eq!(extra, LvalueExtra::None); - Ok(Value::ByRef(ptr)) + Ok((Value::ByRef(ptr), aligned)) } Lvalue::Local { frame, local } => { - self.stack[frame].get_local(local) + Ok((self.stack[frame].get_local(local)?, true)) } Lvalue::Global(cid) => { - Ok(self.globals.get(&cid).expect("global not cached").value) + Ok((self.globals.get(&cid).expect("global not cached").value, true)) } } } @@ -239,7 +243,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, General { ref variants, .. } => { - let (_, base_extra) = base.to_ptr_and_extra(); + let (_, base_extra, _) = base.to_ptr_extra_aligned(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed) @@ -289,8 +293,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; // Do not allocate in trivial cases - let (base_ptr, base_extra) = match base { - Lvalue::Ptr { ptr, extra } => (ptr, extra), + let (base_ptr, base_extra, aligned) = match base { + Lvalue::Ptr { ptr, extra, aligned } => (ptr, extra, aligned), Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? { // in case the type has a single field, just return the value Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => { @@ -299,7 +303,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Value::ByRef(_) | Value::ByValPair(..) | - Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(), + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), }, Lvalue::Global(cid) => match self.globals.get(&cid).expect("uncached global").value { // in case the type has a single field, just return the value @@ -309,7 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Value::ByRef(_) | Value::ByValPair(..) | - Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(), + Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), }, }; @@ -325,11 +329,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_ty = self.monomorphize(field_ty, self.substs()); - if packed { - let size = self.type_size(field_ty)?.expect("packed struct must be sized"); - self.memory.mark_packed(ptr.to_ptr()?, size); - } - let extra = if self.type_is_sized(field_ty) { LvalueExtra::None } else { @@ -343,7 +342,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { base_extra }; - Ok(Lvalue::Ptr { ptr, extra }) + Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed }) } fn eval_lvalue_projection( @@ -351,7 +350,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { proj: &mir::LvalueProjection<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::ProjectionElem::*; - let (ptr, extra) = match proj.elem { + let (ptr, extra, aligned) = match proj.elem { Field(field, field_ty) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); @@ -364,7 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let base_layout = self.type_layout(base_ty)?; // FIXME(solson) let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let (base_ptr, base_extra, aligned) = base.to_ptr_extra_aligned(); use rustc::ty::layout::Layout::*; let extra = match *base_layout { @@ -372,12 +371,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra, _ => bug!("variant downcast on non-aggregate: {:?}", base_layout), }; - (base_ptr, extra) + (base_ptr, extra, aligned) } Deref => { let base_ty = self.lvalue_ty(&proj.base); - let val = self.eval_and_read_lvalue(&proj.base)?; + let (val, _aligned) = self.eval_and_read_lvalue(&proj.base)?; + // Conservatively, the intermediate accesses of a Deref lvalue do not take into account the packed flag. + // Hence we ignore alignment here. let pointee_type = match base_ty.sty { ty::TyRawPtr(ref tam) | @@ -391,13 +392,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.tcx.struct_tail(pointee_type).sty { ty::TyDynamic(..) => { let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; - (ptr, LvalueExtra::Vtable(vtable)) + (ptr, LvalueExtra::Vtable(vtable), true) }, ty::TyStr | ty::TySlice(_) => { let (ptr, len) = val.expect_slice(&self.memory)?; - (ptr, LvalueExtra::Length(len)) + (ptr, LvalueExtra::Length(len), true) }, - _ => (val.read_ptr(&self.memory)?, LvalueExtra::None), + _ => (val.read_ptr(&self.memory)?, LvalueExtra::None, true), } } @@ -406,7 +407,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); + let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); let (elem_ty, len) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); @@ -415,7 +416,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; - (ptr, LvalueExtra::None) + (ptr, LvalueExtra::None, aligned) } ConstantIndex { offset, min_length, from_end } => { @@ -423,7 +424,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); + let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized"); @@ -436,7 +437,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?; - (ptr, LvalueExtra::None) + (ptr, LvalueExtra::None, aligned) } Subslice { from, to } => { @@ -444,18 +445,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; - let (base_ptr, _) = base.to_ptr_and_extra(); + let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!(u64::from(from) <= n - u64::from(to)); let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?; let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); - (ptr, extra) + (ptr, extra, aligned) } }; - Ok(Lvalue::Ptr { ptr, extra }) + Ok(Lvalue::Ptr { ptr, extra, aligned }) } pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { diff --git a/src/memory.rs b/src/memory.rs index caf7f3a651614..debdf05a6cdc4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,5 +1,5 @@ use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian}; -use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet}; +use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr, mem, io}; use rustc::ty; @@ -124,20 +124,6 @@ pub struct Memory<'a, 'tcx> { /// Target machine data layout to emulate. pub layout: &'a TargetDataLayout, - /// List of memory regions containing packed structures. - /// - /// We mark memory as "packed" or "unaligned" for a single statement, and clear the marking - /// afterwards. In the case where no packed structs are present, it's just a single emptyness - /// check of a set instead of heavily influencing all memory access code as other solutions - /// would. This is simpler than the alternative of passing a "packed" parameter to every - /// load/store method. - /// - /// One disadvantage of this solution is the fact that you can cast a pointer to a packed - /// struct to a pointer to a normal struct and if you access a field of both in the same MIR - /// statement, the normal struct access will succeed even though it shouldn't. But even with - /// mir optimizations, that situation is hard/impossible to produce. - packed: BTreeSet, - /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. literal_alloc_cache: HashMap, AllocId>, @@ -147,6 +133,11 @@ pub struct Memory<'a, 'tcx> { /// The Key to use for the next thread-local allocation. next_thread_local: TlsKey, + + /// To avoid having to pass flags to every single memory access, we have some global state saying whether + /// alignment checking is currently enforced for read and/or write accesses. + pub reads_are_aligned: bool, + pub writes_are_aligned: bool, } impl<'a, 'tcx> Memory<'a, 'tcx> { @@ -159,11 +150,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { layout, memory_size: max_memory, memory_usage: 0, - packed: BTreeSet::new(), static_alloc: HashSet::new(), literal_alloc_cache: HashMap::new(), thread_local: BTreeMap::new(), next_thread_local: 0, + reads_are_aligned: true, + writes_are_aligned: true, } } @@ -278,30 +270,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx> { + pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx> { let offset = match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { let alloc = self.get(ptr.alloc_id)?; - // check whether the memory was marked as packed - // we select all elements that have the correct alloc_id and are within - // the range given by the offset into the allocation and the length - let start = Entry { - alloc_id: ptr.alloc_id, - packed_start: 0, - packed_end: ptr.offset + len, - }; - let end = Entry { - alloc_id: ptr.alloc_id, - packed_start: ptr.offset + len, - packed_end: 0, - }; - for &Entry { packed_start, packed_end, .. } in self.packed.range(start..end) { - // if the region we are checking is covered by a region in `packed` - // ignore the actual alignment - if packed_start <= ptr.offset && (ptr.offset + len) <= packed_end { - return Ok(()); - } - } if alloc.align < align { return Err(EvalError::AlignmentCheckFailed { has: alloc.align, @@ -338,18 +310,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub(crate) fn mark_packed(&mut self, ptr: MemoryPointer, len: u64) { - self.packed.insert(Entry { - alloc_id: ptr.alloc_id, - packed_start: ptr.offset, - packed_end: ptr.offset + len, - }); - } - - pub(crate) fn clear_packed(&mut self) { - self.packed.clear(); - } - pub(crate) fn create_tls_key(&mut self, dtor: Option>) -> TlsKey { let new_key = self.next_thread_local; self.next_thread_local += 1; @@ -426,20 +386,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } -// The derived `Ord` impl sorts first by the first field, then, if the fields are the same -// by the second field, and if those are the same, too, then by the third field. -// This is exactly what we need for our purposes, since a range within an allocation -// will give us all `Entry`s that have that `AllocId`, and whose `packed_start` is <= than -// the one we're looking for, but not > the end of the range we're checking. -// At the same time the `packed_end` is irrelevant for the sorting and range searching, but used for the check. -// This kind of search breaks, if `packed_end < packed_start`, so don't do that! -#[derive(Eq, PartialEq, Ord, PartialOrd)] -struct Entry { - alloc_id: AllocId, - packed_start: u64, - packed_end: u64, -} - /// Allocation accessors impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { @@ -576,7 +522,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&[]); } // FIXME: check alignment for zst memory accesses? - self.check_align(ptr.into(), align, size)?; + if self.reads_are_aligned { + self.check_align(ptr.into(), align)?; + } self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -590,7 +538,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } // FIXME: check alignment for zst memory accesses? - self.check_align(ptr.into(), align, size)?; + if self.writes_are_aligned { + self.check_align(ptr.into(), align)?; + } self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); diff --git a/src/step.rs b/src/step.rs index 77d0f962c9645..3fd28463e0735 100644 --- a/src/step.rs +++ b/src/step.rs @@ -28,8 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { - // see docs on the `Memory::packed` field for why we do this - self.memory.clear_packed(); + assert!(self.memory.reads_are_aligned && self.memory.writes_are_aligned, "Someone forgot to clear the 'unaligned' flag"); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index f69f99f3d28c4..d0ad71a7293d7 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -12,9 +12,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.to_value_with_vtable(vtable), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.to_value_with_len(len), - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => ptr.to_value(), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true } => ptr.to_value_with_vtable(vtable), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true } => ptr.to_value_with_len(len), + Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true } => ptr.to_value(), _ => bug!("force_allocation broken"), }; self.drop(val, instance, ty, span) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 94c1bac459d6e..def080d5b206b 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -270,8 +270,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, - Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), + Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true } => self.memory.write_repeat(ptr, 0, size)?, + Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat or unaligned ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, } } @@ -394,11 +394,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); - let dest_ty = substs.type_at(1); - let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); let ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.mark_packed(ptr, size); + self.memory.writes_are_aligned = false; self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; + self.memory.writes_are_aligned = true; } "unchecked_shl" => { @@ -448,9 +447,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; match dest { Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => + Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true } => self.memory.mark_definedness(ptr, size, false)?, - Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), + Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat or unaligned ptr target"), Lvalue::Global(cid) => self.modify_global(cid, uninit)?, } } @@ -465,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { // TODO: Should we, at least, validate the alignment? (Also see memory::copy) - self.memory.check_align(ptr, ty_align, size * count)?; + self.memory.check_align(ptr, ty_align)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } } diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs index 5b3f09c0dd096..796204ea4eef2 100644 --- a/tests/run-pass/packed_struct.rs +++ b/tests/run-pass/packed_struct.rs @@ -5,7 +5,7 @@ struct S { } fn main() { - let x = S { + let mut x = S { a: 42, b: 99, }; @@ -16,4 +16,7 @@ fn main() { // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference assert_eq!({x.a}, 42); assert_eq!({x.b}, 99); + + x.b = 77; + assert_eq!({x.b}, 77); } From 454fc854ab5d5fb349dbb1408867d1817a180206 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Jul 2017 17:52:57 -0700 Subject: [PATCH 1072/1096] Rename value accessors to "into_*" so the three of them are better aligned --- src/eval_context.rs | 4 ++-- src/lvalue.rs | 6 +++--- src/terminator/intrinsic.rs | 28 ++++++++++++------------- src/terminator/mod.rs | 42 ++++++++++++++++++------------------- src/value.rs | 8 ++++--- 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 0e63e033cfb78..4d7caaf6ff3cc 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1460,7 +1460,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let ptr = src.read_ptr(&self.memory)?; + let ptr = src.into_ptr(&self.memory)?; // u64 cast is from usize to u64, which is always good self.write_value(ptr.to_value_with_len(length as u64), dest, dest_ty) } @@ -1474,7 +1474,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; - let ptr = src.read_ptr(&self.memory)?; + let ptr = src.into_ptr(&self.memory)?; self.write_value(ptr.to_value_with_vtable(vtable), dest, dest_ty) }, diff --git a/src/lvalue.rs b/src/lvalue.rs index 18ec7a77cb8ce..bff6f9f955ade 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -391,14 +391,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.tcx.struct_tail(pointee_type).sty { ty::TyDynamic(..) => { - let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?; + let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?; (ptr, LvalueExtra::Vtable(vtable), true) }, ty::TyStr | ty::TySlice(_) => { - let (ptr, len) = val.expect_slice(&self.memory)?; + let (ptr, len) = val.into_slice(&self.memory)?; (ptr, LvalueExtra::Length(len), true) }, - _ => (val.read_ptr(&self.memory)?, LvalueExtra::None, true), + _ => (val.into_ptr(&self.memory)?, LvalueExtra::None, true), } } diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index def080d5b206b..7af678645550a 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; self.write_ptr(dest, result_ptr, dest_ty)?; } @@ -60,7 +60,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; self.write_value(Value::ByRef(ptr), dest, ty)?; } @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); - let dest = arg_vals[0].read_ptr(&self.memory)?; + let dest = arg_vals[0].into_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -79,7 +79,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_xchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -93,7 +93,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_cxchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; let old = self.read_value(ptr, ty)?; @@ -114,7 +114,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { @@ -144,8 +144,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); if elem_size != 0 { let elem_align = self.type_align(elem_ty)?; - let src = arg_vals[0].read_ptr(&self.memory)?; - let dest = arg_vals[1].read_ptr(&self.memory)?; + let src = arg_vals[0].into_ptr(&self.memory)?; + let dest = arg_vals[1].into_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?; } @@ -173,7 +173,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let adt_ptr = arg_vals[0].into_ptr(&self.memory)?.to_ptr()?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } @@ -293,7 +293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } @@ -306,7 +306,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; self.write_ptr(dest, result_ptr, dest_ty)?; } @@ -460,7 +460,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let ptr = arg_vals[0].read_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { // TODO: Should we, at least, validate the alignment? (Also see memory::copy) @@ -545,7 +545,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((size, align.abi())) } ty::TyDynamic(..) => { - let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = value.into_ptr_vtable_pair(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. self.read_size_and_align_from_vtable(vtable) } @@ -553,7 +553,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; - let (_, len) = value.expect_slice(&self.memory)?; + let (_, len) = value.into_slice(&self.memory)?; let align = self.type_align(elem_ty)?; Ok((len * elem_size, align as u64)) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index dc6610a2213b0..9a7619576c407 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -393,7 +393,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); - let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); @@ -572,7 +572,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_dealloc" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let align = self.value_to_primval(args[2], usize)?.to_u64()?; if old_size == 0 { @@ -584,7 +584,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.deallocate(ptr, Some((old_size, align)))?; } "alloc::heap::::__rust_realloc" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; @@ -660,7 +660,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "free" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&self.memory)?; if !ptr.is_null()? { self.memory.deallocate(ptr.to_ptr()?, None)?; } @@ -674,8 +674,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "dlsym" => { - let _handle = args[0].read_ptr(&self.memory)?; - let symbol = args[1].read_ptr(&self.memory)?.to_ptr()?; + let _handle = args[0].into_ptr(&self.memory)?; + let symbol = args[1].into_ptr(&self.memory)?.to_ptr()?; let symbol_name = self.memory.read_c_str(symbol)?; let err = format!("bad c unicode symbol: {:?}", symbol_name); let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); @@ -686,8 +686,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - let f = args[0].read_ptr(&self.memory)?.to_ptr()?; - let data = args[1].read_ptr(&self.memory)?; + let f = args[0].into_ptr(&self.memory)?.to_ptr()?; + let data = args[1].into_ptr(&self.memory)?; let f_instance = self.memory.get_fn(f)?; self.write_null(dest, dest_ty)?; @@ -718,8 +718,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memcmp" => { - let left = args[0].read_ptr(&self.memory)?; - let right = args[1].read_ptr(&self.memory)?; + let left = args[0].into_ptr(&self.memory)?; + let right = args[1].into_ptr(&self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; let result = { @@ -738,7 +738,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memrchr" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { @@ -750,7 +750,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memchr" => { - let ptr = args[0].read_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { @@ -763,7 +763,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "getenv" => { let result = { - let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let name_ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; let name = self.memory.read_c_str(name_ptr)?; match self.env_vars.get(name) { Some(&var) => PrimVal::Ptr(var), @@ -776,7 +776,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "unsetenv" => { let mut success = None; { - let name_ptr = args[0].read_ptr(&self.memory)?; + let name_ptr = args[0].into_ptr(&self.memory)?; if !name_ptr.is_null()? { let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; if !name.is_empty() && !name.contains(&b'=') { @@ -797,8 +797,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "setenv" => { let mut new = None; { - let name_ptr = args[0].read_ptr(&self.memory)?; - let value_ptr = args[1].read_ptr(&self.memory)?.to_ptr()?; + let name_ptr = args[0].into_ptr(&self.memory)?; + let value_ptr = args[1].into_ptr(&self.memory)?.to_ptr()?; let value = self.memory.read_c_str(value_ptr)?; if !name_ptr.is_null()? { let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; @@ -823,7 +823,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write" => { let fd = self.value_to_primval(args[0], usize)?.to_u64()?; - let buf = args[1].read_ptr(&self.memory)?; + let buf = args[1].into_ptr(&self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); let result = if fd == 1 || fd == 2 { // stdout/stderr @@ -840,7 +840,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "strlen" => { - let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; let n = self.memory.read_c_str(ptr)?.len(); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } @@ -863,10 +863,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { - let key_ptr = args[0].read_ptr(&self.memory)?; + let key_ptr = args[0].into_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor = match args[1].read_ptr(&self.memory)?.into_inner_primval() { + let dtor = match args[1].into_ptr(&self.memory)?.into_inner_primval() { PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), @@ -908,7 +908,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "pthread_setspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; - let new_ptr = args[1].read_ptr(&self.memory)?; + let new_ptr = args[1].into_ptr(&self.memory)?; self.memory.store_tls(key, new_ptr)?; // Return success (0) diff --git a/src/value.rs b/src/value.rs index 4bda56a2877f9..0e544456524f7 100644 --- a/src/value.rs +++ b/src/value.rs @@ -158,7 +158,9 @@ pub enum PrimValKind { } impl<'a, 'tcx: 'a> Value { - pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + /// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef, + /// this may have to perform a load. + pub(super) fn into_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr.to_ptr()?), @@ -166,7 +168,7 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn expect_ptr_vtable_pair( + pub(super) fn into_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> ) -> EvalResult<'tcx, (Pointer, MemoryPointer)> { @@ -184,7 +186,7 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { + pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { ByRef(ref_ptr) => { From 1fe310c8ba663f1046daca57530077e87bf3665d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Jul 2017 19:29:16 -0700 Subject: [PATCH 1073/1096] Memory::read_ptr has to check for relocations on the edges --- src/memory.rs | 1 + tests/compile-fail/reading_half_a_pointer.rs | 29 ++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/compile-fail/reading_half_a_pointer.rs diff --git a/src/memory.rs b/src/memory.rs index caf7f3a651614..710912d64d775 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -745,6 +745,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if self.check_defined(ptr, size).is_err() { return Ok(PrimVal::Undef.into()); } + self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size, size)?; let offset = read_target_uint(endianess, bytes).unwrap(); diff --git a/tests/compile-fail/reading_half_a_pointer.rs b/tests/compile-fail/reading_half_a_pointer.rs new file mode 100644 index 0000000000000..cc41b52f33372 --- /dev/null +++ b/tests/compile-fail/reading_half_a_pointer.rs @@ -0,0 +1,29 @@ +#![allow(dead_code)] + +// We use packed structs to get around alignment restrictions +#[repr(packed)] +struct Data { + pad: u8, + ptr: &'static i32, +} + +// But we need to gurantee some alignment +struct Wrapper { + align: u64, + data: Data, +} + +static G : i32 = 0; + +fn main() { + let mut w = Wrapper { align: 0, data: Data { pad: 0, ptr: &G } }; + + // Get a pointer to the beginning of the Data struct (one u8 byte, then the pointer bytes). + // Thanks to the wrapper, we know this is aligned-enough to perform a load at ptr size. + // We load at pointer type, so having a relocation is okay -- but here, the relocation + // starts 1 byte to the right, so using it would actually be wrong! + let d_alias = &mut w.data as *mut _ as *mut *const u8; + unsafe { + let _x = *d_alias; //~ ERROR: tried to access part of a pointer value as raw bytes + } +} From 287b6be5ca0e153383fdc13e44a7be540606e957 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Jul 2017 21:06:57 -0700 Subject: [PATCH 1074/1096] track alignment also for ByRef values --- src/eval_context.rs | 75 +++++++++++++++++++-------------- src/lvalue.rs | 20 ++++----- src/memory.rs | 31 +++++++++++++- src/step.rs | 2 +- src/terminator/drop.rs | 7 +-- src/terminator/intrinsic.rs | 47 +++++++++++---------- src/terminator/mod.rs | 45 ++++++++++---------- src/value.rs | 29 ++++++++++--- tests/run-pass/packed_struct.rs | 20 +++++++++ 9 files changed, 176 insertions(+), 100 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 4d7caaf6ff3cc..a4ce1d327e3a8 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -352,7 +352,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .expect("global should have been cached (static)"); match global_value.value { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions - Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?, + Value::ByRef(ptr, _aligned) => + // Alignment does not matter for this call + self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; }, @@ -408,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn deallocate_local(&mut self, local: Option) -> EvalResult<'tcx> { - if let Some(Value::ByRef(ptr)) = local { + if let Some(Value::ByRef(ptr, _aligned)) = local { trace!("deallocating local"); let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); @@ -497,10 +499,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Rvalue::*; match *rvalue { Use(ref operand) => { - let (value, aligned) = self.eval_operand_maybe_unaligned(operand)?; - self.memory.reads_are_aligned = aligned; + let value = self.eval_operand(operand)?; self.write_value(value, dest, dest_ty)?; - self.memory.reads_are_aligned = true; } BinaryOp(bin_op, ref left, ref right) => { @@ -725,7 +725,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_ty = self.operand_ty(operand); if self.type_is_fat_ptr(src_ty) { match (src, self.type_is_fat_ptr(dest_ty)) { - (Value::ByRef(_), _) | + (Value::ByRef(..), _) | (Value::ByValPair(..), true) => { self.write_value(src, dest, dest_ty)?; }, @@ -955,7 +955,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.value_to_primval(value, ty) } - pub(super) fn eval_operand_maybe_unaligned(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, (Value, bool)> { + pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::Operand::*; match *op { Consume(ref lvalue) => self.eval_and_read_lvalue(lvalue), @@ -981,16 +981,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; - Ok((value, true)) + Ok(value) } } } - pub(super) fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { - // This is called when the packed flag is not taken into account. Ignore alignment. - Ok(self.eval_operand_maybe_unaligned(op)?.0) - } - pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } @@ -1011,15 +1006,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // -1 since we don't store the return value match self.stack[frame].locals[local.index() - 1] { None => return Err(EvalError::DeadLocal), - Some(Value::ByRef(ptr)) => { - Lvalue::from_primval_ptr(ptr) + Some(Value::ByRef(ptr, aligned)) => { + Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None } }, Some(val) => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr.into())); // it stays live + self.stack[frame].locals[local.index() - 1] = Some(Value::by_ref(ptr.into())); // it stays live self.write_value_to_ptr(val, ptr.into(), ty)?; Lvalue::from_ptr(ptr) } @@ -1029,7 +1024,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Global(cid) => { let global_val = *self.globals.get(&cid).expect("global not cached"); match global_val.value { - Value::ByRef(ptr) => Lvalue::from_primval_ptr(ptr), + Value::ByRef(ptr, aligned) => + Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }, _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); @@ -1040,7 +1036,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { - value: Value::ByRef(ptr.into()), + value: Value::by_ref(ptr.into()), .. global_val }; Lvalue::from_ptr(ptr) @@ -1054,14 +1050,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// ensures this Value is not a ByRef pub(super) fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match value { - Value::ByRef(ptr) => self.read_value(ptr, ty), + Value::ByRef(ptr, aligned) => { + self.memory.begin_unaligned_read(aligned); + let r = self.read_value(ptr, ty); + self.memory.end_unaligned_read(); + r + } other => Ok(other), } } pub(super) fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match self.follow_by_ref_value(value, ty)? { - Value::ByRef(_) => bug!("follow_by_ref_value can't result in `ByRef`"), + Value::ByRef(..) => bug!("follow_by_ref_value can't result in `ByRef`"), Value::ByVal(primval) => { self.ensure_valid_value(primval, ty)?; @@ -1126,9 +1127,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra, aligned } => { assert_eq!(extra, LvalueExtra::None); - self.memory.writes_are_aligned = aligned; + self.memory.begin_unaligned_write(aligned); let r = self.write_value_to_ptr(src_val, ptr, dest_ty); - self.memory.writes_are_aligned = true; + self.memory.end_unaligned_write(); r } @@ -1152,7 +1153,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { old_dest_val: Value, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { - if let Value::ByRef(dest_ptr) = old_dest_val { + if let Value::ByRef(dest_ptr, aligned) = old_dest_val { // If the value is already `ByRef` (that is, backed by an `Allocation`), // then we must write the new value into this allocation, because there may be // other pointers into the allocation. These other pointers are logically @@ -1160,9 +1161,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. + self.memory.begin_unaligned_write(aligned); self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; + self.memory.end_unaligned_write(); - } else if let Value::ByRef(src_ptr) = src_val { + } else if let Value::ByRef(src_ptr, aligned) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it // and we can simply overwrite the `Value` in the locals array directly. // @@ -1174,13 +1177,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // It is a valid optimization to attempt reading a primitive value out of the // source and write that into the destination without making an allocation, so // we do so here. + self.memory.begin_unaligned_read(aligned); if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { write_dest(self, src_val)?; } else { let dest_ptr = self.alloc_ptr(dest_ty)?.into(); self.copy(src_ptr, dest_ptr, dest_ty)?; - write_dest(self, Value::ByRef(dest_ptr))?; + write_dest(self, Value::by_ref(dest_ptr))?; } + self.memory.end_unaligned_read(); } else { // Finally, we have the simple case where neither source nor destination are @@ -1197,7 +1202,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { - Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty), + Value::ByRef(ptr, aligned) => { + self.memory.begin_unaligned_read(aligned); + let r = self.copy(ptr, dest, dest_ty); + self.memory.end_unaligned_read(); + r + }, Value::ByVal(primval) => { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(dest, primval, size) @@ -1460,7 +1470,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let ptr = src.into_ptr(&self.memory)?; + let ptr = src.into_ptr(&mut self.memory)?; // u64 cast is from usize to u64, which is always good self.write_value(ptr.to_value_with_len(length as u64), dest, dest_ty) } @@ -1474,7 +1484,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; - let ptr = src.into_ptr(&self.memory)?; + let ptr = src.into_ptr(&mut self.memory)?; self.write_value(ptr.to_value_with_vtable(vtable), dest, dest_ty) }, @@ -1517,8 +1527,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { //let src = adt::MaybeSizedValue::sized(src); //let dst = adt::MaybeSizedValue::sized(dst); let src_ptr = match src { - Value::ByRef(ptr) => ptr, - _ => bug!("expected pointer, got {:?}", src), + Value::ByRef(ptr, true) => ptr, + // TODO: Is it possible for unaligned pointers to occur here? + _ => bug!("expected aligned pointer, got {:?}", src), }; // FIXME(solson) @@ -1537,7 +1548,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr.into(), src_fty)?; } else { - self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; + self.unsize_into(Value::by_ref(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } } Ok(()) @@ -1564,7 +1575,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(err) => { panic!("Failed to access local: {:?}", err); } - Ok(Value::ByRef(ptr)) => match ptr.into_inner_primval() { + Ok(Value::ByRef(ptr, _aligned)) => match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { write!(msg, " by ref:").unwrap(); allocs.push(ptr.alloc_id); diff --git a/src/lvalue.rs b/src/lvalue.rs index bff6f9f955ade..b042baa2696c9 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -179,13 +179,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } /// Returns a value and (in case of a ByRef) if we are supposed to use aligned accesses. - pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, (Value, bool)> { + pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { let ty = self.lvalue_ty(lvalue); // Shortcut for things like accessing a fat pointer's field, // which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory // and returning an `Lvalue::Ptr` to it if let Some(val) = self.try_read_lvalue(lvalue)? { - return Ok((val, true)); + return Ok(val); } let lvalue = self.eval_lvalue(lvalue)?; @@ -196,13 +196,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match lvalue { Lvalue::Ptr { ptr, extra, aligned } => { assert_eq!(extra, LvalueExtra::None); - Ok((Value::ByRef(ptr), aligned)) + Ok(Value::ByRef(ptr, aligned)) } Lvalue::Local { frame, local } => { - Ok((self.stack[frame].get_local(local)?, true)) + Ok(self.stack[frame].get_local(local)?) } Lvalue::Global(cid) => { - Ok((self.globals.get(&cid).expect("global not cached").value, true)) + Ok(self.globals.get(&cid).expect("global not cached").value) } } } @@ -301,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); return Ok(base); }, - Value::ByRef(_) | + Value::ByRef(..) | Value::ByValPair(..) | Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), }, @@ -311,7 +311,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); return Ok(base); }, - Value::ByRef(_) | + Value::ByRef(..) | Value::ByValPair(..) | Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(), }, @@ -376,9 +376,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { let base_ty = self.lvalue_ty(&proj.base); - let (val, _aligned) = self.eval_and_read_lvalue(&proj.base)?; - // Conservatively, the intermediate accesses of a Deref lvalue do not take into account the packed flag. - // Hence we ignore alignment here. + let val = self.eval_and_read_lvalue(&proj.base)?; let pointee_type = match base_ty.sty { ty::TyRawPtr(ref tam) | @@ -398,7 +396,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (ptr, len) = val.into_slice(&self.memory)?; (ptr, LvalueExtra::Length(len), true) }, - _ => (val.into_ptr(&self.memory)?, LvalueExtra::None, true), + _ => (val.into_ptr(&mut self.memory)?, LvalueExtra::None, true), } } diff --git a/src/memory.rs b/src/memory.rs index debdf05a6cdc4..bc0940e270b2f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -136,8 +136,8 @@ pub struct Memory<'a, 'tcx> { /// To avoid having to pass flags to every single memory access, we have some global state saying whether /// alignment checking is currently enforced for read and/or write accesses. - pub reads_are_aligned: bool, - pub writes_are_aligned: bool, + reads_are_aligned: bool, + writes_are_aligned: bool, } impl<'a, 'tcx> Memory<'a, 'tcx> { @@ -384,6 +384,33 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } return Ok(None); } + + #[inline] + pub(crate) fn begin_unaligned_read(&mut self, aligned: bool) { + assert!(self.reads_are_aligned, "Unaligned reads must not be nested"); + self.reads_are_aligned = aligned; + } + + #[inline] + pub(crate) fn end_unaligned_read(&mut self) { + self.reads_are_aligned = true; + } + + #[inline] + pub(crate) fn begin_unaligned_write(&mut self, aligned: bool) { + assert!(self.writes_are_aligned, "Unaligned writes must not be nested"); + self.writes_are_aligned = aligned; + } + + #[inline] + pub(crate) fn end_unaligned_write(&mut self) { + self.writes_are_aligned = true; + } + + #[inline] + pub(crate) fn assert_all_aligned(&mut self) { + assert!(self.reads_are_aligned && self.writes_are_aligned, "Someone forgot to clear the 'unaligned' flag"); + } } /// Allocation accessors diff --git a/src/step.rs b/src/step.rs index 3fd28463e0735..f1d32ec69d81f 100644 --- a/src/step.rs +++ b/src/step.rs @@ -28,7 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { - assert!(self.memory.reads_are_aligned && self.memory.writes_are_aligned, "Someone forgot to clear the 'unaligned' flag"); + self.memory.assert_all_aligned(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index d0ad71a7293d7..ce40672839b13 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -11,10 +11,11 @@ use value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); + // We take the address of the object. let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true } => ptr.to_value_with_vtable(vtable), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true } => ptr.to_value_with_len(len), - Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: true } => ptr.to_value(), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: _ } => ptr.to_value_with_vtable(vtable), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: _ } => ptr.to_value_with_len(len), + Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned: _ } => ptr.to_value(), _ => bug!("force_allocation broken"), }; self.drop(val, instance, ty, span) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 7af678645550a..c37974caecb54 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "arith_offset" => { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; self.write_ptr(dest, result_ptr, dest_ty)?; } @@ -60,8 +60,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_load_acq" | "volatile_load" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].into_ptr(&self.memory)?; - self.write_value(Value::ByRef(ptr), dest, ty)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; + self.write_value(Value::by_ref(ptr), dest, ty)?; } "atomic_store" | @@ -69,7 +69,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_store_rel" | "volatile_store" => { let ty = substs.type_at(0); - let dest = arg_vals[0].into_ptr(&self.memory)?; + let dest = arg_vals[0].into_ptr(&mut self.memory)?; self.write_value_to_ptr(arg_vals[1], dest, ty)?; } @@ -79,12 +79,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_xchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { Value::ByVal(val) => val, - Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByRef(..) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"), }; self.write_primval(dest, old, ty)?; @@ -93,13 +93,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ if intrinsic_name.starts_with("atomic_cxchg") => { let ty = substs.type_at(0); - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let expect_old = self.value_to_primval(arg_vals[1], ty)?; let change = self.value_to_primval(arg_vals[2], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { Value::ByVal(val) => val, - Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByRef(..) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"), }; let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?; @@ -114,12 +114,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" | "atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let change = self.value_to_primval(arg_vals[1], ty)?; let old = self.read_value(ptr, ty)?; let old = match old { Value::ByVal(val) => val, - Value::ByRef(_) => bug!("just read the value, can't be byref"), + Value::ByRef(..) => bug!("just read the value, can't be byref"), Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"), }; self.write_primval(dest, old, ty)?; @@ -144,8 +144,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value"); if elem_size != 0 { let elem_align = self.type_align(elem_ty)?; - let src = arg_vals[0].into_ptr(&self.memory)?; - let dest = arg_vals[1].into_ptr(&self.memory)?; + let src = arg_vals[0].into_ptr(&mut self.memory)?; + let dest = arg_vals[1].into_ptr(&mut self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?; } @@ -173,7 +173,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = arg_vals[0].into_ptr(&self.memory)?.to_ptr()?; + let adt_ptr = arg_vals[0].into_ptr(&mut self.memory)?.to_ptr()?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?; } @@ -248,9 +248,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("cannot zero unsized value"); let init = |this: &mut Self, val: Value| { let zero_val = match val { - Value::ByRef(ptr) => { + Value::ByRef(ptr, aligned) => { + // These writes have no alignment restriction anyway. this.memory.write_repeat(ptr, 0, size)?; - Value::ByRef(ptr) + Value::ByRef(ptr, aligned) }, // TODO(solson): Revisit this, it's fishy to check for Undef here. Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) { @@ -259,7 +260,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; let ptr = Pointer::from(PrimVal::Ptr(ptr)); this.memory.write_repeat(ptr, 0, size)?; - Value::ByRef(ptr) + Value::by_ref(ptr) } }, Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)), @@ -293,7 +294,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; self.write_value_to_ptr(arg_vals[1], ptr, ty)?; } @@ -306,7 +307,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; self.write_ptr(dest, result_ptr, dest_ty)?; } @@ -395,9 +396,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); let ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.writes_are_aligned = false; + self.memory.begin_unaligned_read(false); self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; - self.memory.writes_are_aligned = true; + self.memory.end_unaligned_read(); } "unchecked_shl" => { @@ -438,9 +439,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = dest_layout.size(&self.tcx.data_layout).bytes(); let uninit = |this: &mut Self, val: Value| { match val { - Value::ByRef(ptr) => { + Value::ByRef(ptr, aligned) => { this.memory.mark_definedness(ptr, size, false)?; - Ok(Value::ByRef(ptr)) + Ok(Value::ByRef(ptr, aligned)) }, _ => Ok(Value::ByVal(PrimVal::Undef)), } @@ -460,7 +461,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let ptr = arg_vals[0].into_ptr(&self.memory)?; + let ptr = arg_vals[0].into_ptr(&mut self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { // TODO: Should we, at least, validate the alignment? (Also see memory::copy) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 9a7619576c407..cb6a2ff345615 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -310,9 +310,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if self.frame().mir.args_iter().count() == fields.len() + 1 { let offsets = variant.offsets.iter().map(|s| s.bytes()); match arg_val { - Value::ByRef(ptr) => { + Value::ByRef(ptr, aligned) => { + assert!(aligned, "Unaligned ByRef-values cannot occur as function arguments"); for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?); + let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?, true); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); self.write_value(arg, dest, ty)?; @@ -572,7 +573,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_dealloc" => { - let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let align = self.value_to_primval(args[2], usize)?.to_u64()?; if old_size == 0 { @@ -584,7 +585,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.deallocate(ptr, Some((old_size, align)))?; } "alloc::heap::::__rust_realloc" => { - let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; @@ -660,7 +661,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "free" => { - let ptr = args[0].into_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&mut self.memory)?; if !ptr.is_null()? { self.memory.deallocate(ptr.to_ptr()?, None)?; } @@ -674,8 +675,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "dlsym" => { - let _handle = args[0].into_ptr(&self.memory)?; - let symbol = args[1].into_ptr(&self.memory)?.to_ptr()?; + let _handle = args[0].into_ptr(&mut self.memory)?; + let symbol = args[1].into_ptr(&mut self.memory)?.to_ptr()?; let symbol_name = self.memory.read_c_str(symbol)?; let err = format!("bad c unicode symbol: {:?}", symbol_name); let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); @@ -686,8 +687,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 // We abort on panic, so not much is going on here, but we still have to call the closure let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - let f = args[0].into_ptr(&self.memory)?.to_ptr()?; - let data = args[1].into_ptr(&self.memory)?; + let f = args[0].into_ptr(&mut self.memory)?.to_ptr()?; + let data = args[1].into_ptr(&mut self.memory)?; let f_instance = self.memory.get_fn(f)?; self.write_null(dest, dest_ty)?; @@ -718,8 +719,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memcmp" => { - let left = args[0].into_ptr(&self.memory)?; - let right = args[1].into_ptr(&self.memory)?; + let left = args[0].into_ptr(&mut self.memory)?; + let right = args[1].into_ptr(&mut self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; let result = { @@ -738,7 +739,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memrchr" => { - let ptr = args[0].into_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&mut self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { @@ -750,7 +751,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "memchr" => { - let ptr = args[0].into_ptr(&self.memory)?; + let ptr = args[0].into_ptr(&mut self.memory)?; let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { @@ -763,7 +764,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "getenv" => { let result = { - let name_ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; + let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; let name = self.memory.read_c_str(name_ptr)?; match self.env_vars.get(name) { Some(&var) => PrimVal::Ptr(var), @@ -776,7 +777,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "unsetenv" => { let mut success = None; { - let name_ptr = args[0].into_ptr(&self.memory)?; + let name_ptr = args[0].into_ptr(&mut self.memory)?; if !name_ptr.is_null()? { let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; if !name.is_empty() && !name.contains(&b'=') { @@ -797,8 +798,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "setenv" => { let mut new = None; { - let name_ptr = args[0].into_ptr(&self.memory)?; - let value_ptr = args[1].into_ptr(&self.memory)?.to_ptr()?; + let name_ptr = args[0].into_ptr(&mut self.memory)?; + let value_ptr = args[1].into_ptr(&mut self.memory)?.to_ptr()?; let value = self.memory.read_c_str(value_ptr)?; if !name_ptr.is_null()? { let name = self.memory.read_c_str(name_ptr.to_ptr()?)?; @@ -823,7 +824,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "write" => { let fd = self.value_to_primval(args[0], usize)?.to_u64()?; - let buf = args[1].into_ptr(&self.memory)?; + let buf = args[1].into_ptr(&mut self.memory)?; let n = self.value_to_primval(args[2], usize)?.to_u64()?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); let result = if fd == 1 || fd == 2 { // stdout/stderr @@ -840,7 +841,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "strlen" => { - let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?; + let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; let n = self.memory.read_c_str(ptr)?.len(); self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?; } @@ -863,10 +864,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Hook pthread calls that go to the thread-local storage memory subsystem "pthread_key_create" => { - let key_ptr = args[0].into_ptr(&self.memory)?; + let key_ptr = args[0].into_ptr(&mut self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor = match args[1].into_ptr(&self.memory)?.into_inner_primval() { + let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() { PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), @@ -908,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "pthread_setspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; - let new_ptr = args[1].into_ptr(&self.memory)?; + let new_ptr = args[1].into_ptr(&mut self.memory)?; self.memory.store_tls(key, new_ptr)?; // Return success (0) diff --git a/src/value.rs b/src/value.rs index 0e544456524f7..42d12e0cdcf06 100644 --- a/src/value.rs +++ b/src/value.rs @@ -26,14 +26,15 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { /// A `Value` represents a single self-contained Rust value. /// /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve -/// value held directly, outside of any allocation (`ByVal`). +/// value held directly, outside of any allocation (`ByVal`). For `ByRef`-values, we remember +/// whether the pointer is supposed to be aligned or not (also see Lvalue). /// /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug)] pub enum Value { - ByRef(Pointer), + ByRef(Pointer, bool), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } @@ -158,12 +159,22 @@ pub enum PrimValKind { } impl<'a, 'tcx: 'a> Value { + #[inline] + pub(super) fn by_ref(ptr: Pointer) -> Self { + Value::ByRef(ptr, true) + } + /// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef, /// this may have to perform a load. - pub(super) fn into_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + pub(super) fn into_ptr(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { - ByRef(ptr) => mem.read_ptr(ptr.to_ptr()?), + ByRef(ptr, aligned) => { + mem.begin_unaligned_read(aligned); + let r = mem.read_ptr(ptr.to_ptr()?); + mem.end_unaligned_read(); + r + }, ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()), } } @@ -174,7 +185,10 @@ impl<'a, 'tcx: 'a> Value { ) -> EvalResult<'tcx, (Pointer, MemoryPointer)> { use self::Value::*; match *self { - ByRef(ref_ptr) => { + ByRef(ref_ptr, aligned) => { + if !aligned { + return Err(EvalError::Unimplemented(format!("Unaligned fat-pointers are not implemented"))); + } let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; Ok((ptr, vtable.to_ptr()?)) @@ -189,7 +203,10 @@ impl<'a, 'tcx: 'a> Value { pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { - ByRef(ref_ptr) => { + ByRef(ref_ptr, aligned) => { + if !aligned { + return Err(EvalError::Unimplemented(format!("Unaligned fat-pointers are not implemented"))); + } let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; Ok((ptr, len)) diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs index 796204ea4eef2..ec1533304c1e9 100644 --- a/tests/run-pass/packed_struct.rs +++ b/tests/run-pass/packed_struct.rs @@ -1,9 +1,27 @@ +#![allow(dead_code)] #[repr(packed)] struct S { a: i32, b: i64, } +#[repr(packed)] +struct Test1<'a> { + x: u8, + other: &'a u32, +} + +#[repr(packed)] +struct Test2<'a> { + x: u8, + other: &'a Test1<'a>, +} + +fn test(t: Test2) { + let x = *t.other.other; + assert_eq!(x, 42); +} + fn main() { let mut x = S { a: 42, @@ -19,4 +37,6 @@ fn main() { x.b = 77; assert_eq!({x.b}, 77); + + test(Test2 { x: 0, other: &Test1 { x: 0, other: &42 }}); } From 81307d72992d709c5f3086a620a94d91694e4096 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 12 Jul 2017 23:40:31 -0700 Subject: [PATCH 1075/1096] fix "unaligned" transmute --- src/terminator/intrinsic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index c37974caecb54..adc2a08819247 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -396,9 +396,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); let ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.begin_unaligned_read(false); + self.memory.begin_unaligned_write(/*aligned*/false); self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; - self.memory.end_unaligned_read(); + self.memory.end_unaligned_write(); } "unchecked_shl" => { From fdef27acf5a233220272776cd565378a4fc59459 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 13:59:16 +0200 Subject: [PATCH 1076/1096] Copy `path_to_def` from clippy --- src/terminator/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index dc6610a2213b0..450cae3b49cb9 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,4 +1,5 @@ -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc::hir::def::Def; use rustc::mir; use rustc::ty::{self, TypeVariants, Ty}; use rustc::ty::layout::Layout; @@ -13,6 +14,8 @@ use memory::{MemoryPointer, TlsKey}; use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; +use std::mem; + mod drop; mod intrinsic; @@ -933,4 +936,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(dest_block); Ok(()) } + /// Get the definition associated to a path. + fn path_to_def(&self, path: &[&str]) -> Option { + let cstore = &self.tcx.sess.cstore; + + let crates = cstore.crates(); + crates.iter() + .find(|&&krate| cstore.crate_name(krate) == path[0]) + .and_then(|krate| { + let krate = DefId { + krate: *krate, + index: CRATE_DEF_INDEX, + }; + let mut items = cstore.item_children(krate, self.tcx.sess); + let mut path_it = path.iter().skip(1).peekable(); + + while let Some(segment) = path_it.next() { + for item in &mem::replace(&mut items, vec![]) { + if item.ident.name == *segment { + if path_it.peek().is_none() { + return Some(item.def); + } + + items = cstore.item_children(item.def.def_id(), self.tcx.sess); + break; + } + } + } + None + }) + } } From f78d6a0d9719d6d63b06e0fb0a4648a86bf317b8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 12 Jul 2017 10:36:14 +0200 Subject: [PATCH 1077/1096] Don't use magic numbers for synconf names instead read them from the `libc` crate if available. fixes #216 --- src/error.rs | 5 ++++ src/terminator/mod.rs | 64 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/error.rs b/src/error.rs index 801ea5c7da48f..3e1155e0b874d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,6 +69,7 @@ pub enum EvalError<'tcx> { NeedsRfc(String), NotConst(String), ReadFromReturnPointer, + PathNotFound(Vec), } pub type EvalResult<'tcx, T = ()> = Result>; @@ -175,6 +176,8 @@ impl<'tcx> Error for EvalError<'tcx> { "this feature is not compatible with constant evaluation", ReadFromReturnPointer => "tried to read from the return pointer", + EvalError::PathNotFound(_) => + "a path could not be resolved, maybe the crate is not loaded", } } @@ -215,6 +218,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg), + EvalError::PathNotFound(ref path) => + write!(f, "Cannot find path {:?}", path), _ => write!(f, "{}", self.description()), } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 450cae3b49cb9..c15b7b9162d24 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,5 +1,4 @@ use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; -use rustc::hir::def::Def; use rustc::mir; use rustc::ty::{self, TypeVariants, Ty}; use rustc::ty::layout::Layout; @@ -856,12 +855,50 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "sysconf" => { let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); - let result = match name { - 30 => PrimVal::Bytes(4096), // _SC_PAGESIZE - 70 => PrimVal::from_i128(-1), // _SC_GETPW_R_SIZE_MAX - _ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))) - }; - self.write_primval(dest, result, dest_ty)?; + // cache the sysconf integers + let paths = &[ + (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), + (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), + ]; + let mut result = None; + for &(path, path_value) in paths { + if let Ok(instance) = self.resolve_path(path) { + use lvalue::{Global, GlobalId}; + let cid = GlobalId { instance, promoted: None }; + let val = match self.globals.get(&cid).map(|glob| glob.value) { + Some(value) => value, + None => { + let mir = self.load_mir(instance.def)?; + self.globals.insert(cid, Global::uninitialized(mir.return_ty)); + let cleanup = StackPopCleanup::MarkStatic(false); + let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + trace!("pushing stack frame for global: {}", name); + let frame = self.stack.len(); + self.push_stack_frame( + instance, + mir.span, + mir, + Lvalue::Global(cid), + cleanup, + )?; + while self.stack.len() != frame { + self.step()?; + } + self.globals.get(&cid).expect("we just computed the global").value + } + }; + let val = self.value_to_primval(val, usize)?.to_u64()?; + if val == name { + result = Some(path_value); + break; + } + } + } + if let Some(result) = result { + self.write_primval(dest, result, dest_ty)?; + } else { + return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))); + } } // Hook pthread calls that go to the thread-local storage memory subsystem @@ -936,8 +973,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(dest_block); Ok(()) } - /// Get the definition associated to a path. - fn path_to_def(&self, path: &[&str]) -> Option { + + /// Get an instance for a path. + fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> { let cstore = &self.tcx.sess.cstore; let crates = cstore.crates(); @@ -955,7 +993,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for item in &mem::replace(&mut items, vec![]) { if item.ident.name == *segment { if path_it.peek().is_none() { - return Some(item.def); + return Some(ty::Instance::mono(self.tcx, item.def.def_id())); } items = cstore.item_children(item.def.def_id(), self.tcx.sess); @@ -965,5 +1003,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } None }) + .ok_or_else(|| { + let path = path.iter() + .map(|&s| s.to_owned()) + .collect(); + EvalError::PathNotFound(path) + }) } } From f8757aa092ef296ca02a33bb0994f27a60572950 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 16:49:55 +0200 Subject: [PATCH 1078/1096] Reuse the `const_eval` method for syscall name resolution --- src/const_eval.rs | 28 +++++++++++++++++----------- src/terminator/mod.rs | 28 +++++----------------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/const_eval.rs b/src/const_eval.rs index ff80f68e2c3ff..771b740b3755a 100644 --- a/src/const_eval.rs +++ b/src/const_eval.rs @@ -1,22 +1,20 @@ -use rustc::hir::def_id::DefId; use rustc::traits::Reveal; -use rustc::ty::subst::Substs; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, TyCtxt, Ty, Instance}; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue}; +use value::PrimVal; use rustc_const_math::ConstInt; use eval_context::{EvalContext, StackPopCleanup}; -pub fn eval_body_as_integer<'a, 'tcx>( +pub fn eval_body_as_primval<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - (def_id, substs): (DefId, &'tcx Substs<'tcx>), -) -> EvalResult<'tcx, ConstInt> { + instance: Instance<'tcx>, +) -> EvalResult<'tcx, (PrimVal, Ty<'tcx>)> { let limits = ::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, limits); - let instance = ecx.resolve_associated_const(def_id, substs); let cid = GlobalId { instance, promoted: None }; - if ecx.tcx.has_attr(def_id, "linkage") { + if ecx.tcx.has_attr(instance.def_id(), "linkage") { return Err(EvalError::NotConst("extern global".to_string())); } @@ -28,7 +26,7 @@ pub fn eval_body_as_integer<'a, 'tcx>( ty::ParamEnv::empty(Reveal::All), mir.span); let cleanup = StackPopCleanup::MarkStatic(mutable); - let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); trace!("pushing stack frame for global: {}", name); ecx.push_stack_frame( instance, @@ -41,11 +39,19 @@ pub fn eval_body_as_integer<'a, 'tcx>( while ecx.step()? {} } let value = ecx.globals.get(&cid).expect("global not cached").value; - let prim = ecx.value_to_primval(value, mir.return_ty)?.to_bytes()?; + Ok((ecx.value_to_primval(value, mir.return_ty)?, mir.return_ty)) +} + +pub fn eval_body_as_integer<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, +) -> EvalResult<'tcx, ConstInt> { + let (prim, ty) = eval_body_as_primval(tcx, instance)?; + let prim = prim.to_bytes()?; use syntax::ast::{IntTy, UintTy}; use rustc::ty::TypeVariants::*; use rustc_const_math::{ConstIsize, ConstUsize}; - Ok(match mir.return_ty.sty { + Ok(match ty.sty { TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8), TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16), TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32), diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c15b7b9162d24..643df3608f9c2 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -855,7 +855,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "sysconf" => { let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); - // cache the sysconf integers + // cache the sysconf integers via miri's global cache let paths = &[ (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), @@ -863,31 +863,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut result = None; for &(path, path_value) in paths { if let Ok(instance) = self.resolve_path(path) { - use lvalue::{Global, GlobalId}; + use lvalue::GlobalId; let cid = GlobalId { instance, promoted: None }; + // compute global if not cached let val = match self.globals.get(&cid).map(|glob| glob.value) { - Some(value) => value, - None => { - let mir = self.load_mir(instance.def)?; - self.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let cleanup = StackPopCleanup::MarkStatic(false); - let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); - trace!("pushing stack frame for global: {}", name); - let frame = self.stack.len(); - self.push_stack_frame( - instance, - mir.span, - mir, - Lvalue::Global(cid), - cleanup, - )?; - while self.stack.len() != frame { - self.step()?; - } - self.globals.get(&cid).expect("we just computed the global").value - } + Some(value) => self.value_to_primval(value, usize)?.to_u64()?, + None => ::const_eval::eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, }; - let val = self.value_to_primval(val, usize)?.to_u64()?; if val == name { result = Some(path_value); break; From 6c9fdc7922e01a47a8398a0f10f30debdfbad938 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 08:40:05 -0700 Subject: [PATCH 1079/1096] expand comment --- src/terminator/drop.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index ce40672839b13..c166980a150d3 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -11,7 +11,9 @@ use value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); - // We take the address of the object. + // 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 val = match self.force_allocation(lval)? { Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: _ } => ptr.to_value_with_vtable(vtable), Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: _ } => ptr.to_value_with_len(len), From 6fb6a1c4d0527b07a1599c9a9ae7d6b0b9097984 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 09:06:27 -0700 Subject: [PATCH 1080/1096] make all Value::into_* methods handle alignment the same way --- src/lvalue.rs | 4 ++-- src/terminator/intrinsic.rs | 6 +++--- src/terminator/mod.rs | 2 +- src/value.rs | 16 +++++++--------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index b042baa2696c9..79b8d50c96e61 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -389,11 +389,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.tcx.struct_tail(pointee_type).sty { ty::TyDynamic(..) => { - let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?; + let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?; (ptr, LvalueExtra::Vtable(vtable), true) }, ty::TyStr | ty::TySlice(_) => { - let (ptr, len) = val.into_slice(&self.memory)?; + let (ptr, len) = val.into_slice(&mut self.memory)?; (ptr, LvalueExtra::Length(len), true) }, _ => (val.into_ptr(&mut self.memory)?, LvalueExtra::None, true), diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index adc2a08819247..26b3ee5622bfd 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -482,7 +482,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub fn size_and_align_of_dst( - &self, + &mut self, ty: ty::Ty<'tcx>, value: Value, ) -> EvalResult<'tcx, (u64, u64)> { @@ -546,7 +546,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((size, align.abi())) } ty::TyDynamic(..) => { - let (_, vtable) = value.into_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?; // the second entry in the vtable is the dynamic size of the object. self.read_size_and_align_from_vtable(vtable) } @@ -554,7 +554,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64; - let (_, len) = value.into_slice(&self.memory)?; + let (_, len) = value.into_slice(&mut self.memory)?; let align = self.type_align(elem_ty)?; Ok((len * elem_size, align as u64)) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index cb6a2ff345615..52c25f35a5d2a 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -394,7 +394,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); - let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?; + let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&mut self.memory)?; let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); diff --git a/src/value.rs b/src/value.rs index 42d12e0cdcf06..ad4a66a16262d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -181,16 +181,15 @@ impl<'a, 'tcx: 'a> Value { pub(super) fn into_ptr_vtable_pair( &self, - mem: &Memory<'a, 'tcx> + mem: &mut Memory<'a, 'tcx> ) -> EvalResult<'tcx, (Pointer, MemoryPointer)> { use self::Value::*; match *self { ByRef(ref_ptr, aligned) => { - if !aligned { - return Err(EvalError::Unimplemented(format!("Unaligned fat-pointers are not implemented"))); - } + mem.begin_unaligned_read(aligned); let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; + mem.end_unaligned_read(); Ok((ptr, vtable.to_ptr()?)) } @@ -200,15 +199,14 @@ impl<'a, 'tcx: 'a> Value { } } - pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { + pub(super) fn into_slice(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { ByRef(ref_ptr, aligned) => { - if !aligned { - return Err(EvalError::Unimplemented(format!("Unaligned fat-pointers are not implemented"))); - } + mem.begin_unaligned_read(aligned); let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; + mem.end_unaligned_read(); Ok((ptr, len)) }, ByValPair(ptr, val) => { @@ -216,7 +214,7 @@ impl<'a, 'tcx: 'a> Value { assert_eq!(len as u64 as u128, len); Ok((ptr.into(), len as u64)) }, - ByVal(_) => unimplemented!(), + ByVal(_) => bug!("expected ptr and length, got {:?}", self), } } } From 62334acd66dcc0812cb04f4a66792ede7aed9b2a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 09:09:45 -0700 Subject: [PATCH 1081/1096] show alignedness of ByRefs; allow converting unaligned ByRef to ptr --- src/eval_context.rs | 4 ++-- src/lvalue.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index a4ce1d327e3a8..1e57adbcf3eae 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1575,9 +1575,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(err) => { panic!("Failed to access local: {:?}", err); } - Ok(Value::ByRef(ptr, _aligned)) => match ptr.into_inner_primval() { + Ok(Value::ByRef(ptr, aligned)) => match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { - write!(msg, " by ref:").unwrap(); + write!(msg, " by {}ref:", if aligned { "" } else { "unaligned " }).unwrap(); allocs.push(ptr.alloc_id); }, ptr => write!(msg, " integral by ref: {:?}", ptr).unwrap(), diff --git a/src/lvalue.rs b/src/lvalue.rs index 79b8d50c96e61..86e09356fd76a 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -86,9 +86,10 @@ impl<'tcx> Lvalue<'tcx> { } pub(super) fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { - let (ptr, extra, aligned) = self.to_ptr_extra_aligned(); + let (ptr, extra, _aligned) = self.to_ptr_extra_aligned(); + // At this point, we forget about the alignment information -- the lvalue has been turned into a reference, + // and no matter where it came from, it now must be aligned. assert_eq!(extra, LvalueExtra::None); - assert_eq!(aligned, true, "tried converting an unaligned lvalue into a ptr"); ptr.to_ptr() } From d02e7f0da8d20e2e664b34566d3eaf8b904971dc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 09:10:50 -0700 Subject: [PATCH 1082/1096] simplify --- src/lvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 86e09356fd76a..15b2dfdb83580 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -200,7 +200,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByRef(ptr, aligned)) } Lvalue::Local { frame, local } => { - Ok(self.stack[frame].get_local(local)?) + self.stack[frame].get_local(local) } Lvalue::Global(cid) => { Ok(self.globals.get(&cid).expect("global not cached").value) From da5538f0b26605948c6bd57918526fd007dc8bc5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 10:29:11 -0700 Subject: [PATCH 1083/1096] use closures to ensure proper bracketing of unaligned accesses --- src/eval_context.rs | 42 +++++++++------------- src/lvalue.rs | 2 +- src/memory.rs | 71 +++++++++++++++++++++++-------------- src/step.rs | 1 - src/terminator/intrinsic.rs | 7 ++-- src/value.rs | 27 +++++++------- 6 files changed, 78 insertions(+), 72 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 1e57adbcf3eae..58fabf694d1ea 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -17,7 +17,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; -use memory::{Memory, MemoryPointer, TlsKey}; +use memory::{Memory, MemoryPointer, TlsKey, HasMemory}; use operator; use value::{PrimVal, PrimValKind, Value, Pointer}; @@ -1051,10 +1051,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { match value { Value::ByRef(ptr, aligned) => { - self.memory.begin_unaligned_read(aligned); - let r = self.read_value(ptr, ty); - self.memory.end_unaligned_read(); - r + self.read_maybe_aligned(aligned, |ectx| ectx.read_value(ptr, ty)) } other => Ok(other), } @@ -1127,10 +1124,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Ptr { ptr, extra, aligned } => { assert_eq!(extra, LvalueExtra::None); - self.memory.begin_unaligned_write(aligned); - let r = self.write_value_to_ptr(src_val, ptr, dest_ty); - self.memory.end_unaligned_write(); - r + self.write_maybe_aligned(aligned, + |ectx| ectx.write_value_to_ptr(src_val, ptr, dest_ty)) } Lvalue::Local { frame, local } => { @@ -1161,9 +1156,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // // Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we // knew for certain that there were no outstanding pointers to this allocation. - self.memory.begin_unaligned_write(aligned); - self.write_value_to_ptr(src_val, dest_ptr, dest_ty)?; - self.memory.end_unaligned_write(); + self.write_maybe_aligned(aligned, + |ectx| ectx.write_value_to_ptr(src_val, dest_ptr, dest_ty))?; } else if let Value::ByRef(src_ptr, aligned) = src_val { // If the value is not `ByRef`, then we know there are no pointers to it @@ -1177,15 +1171,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // It is a valid optimization to attempt reading a primitive value out of the // source and write that into the destination without making an allocation, so // we do so here. - self.memory.begin_unaligned_read(aligned); - if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { - write_dest(self, src_val)?; - } else { - let dest_ptr = self.alloc_ptr(dest_ty)?.into(); - self.copy(src_ptr, dest_ptr, dest_ty)?; - write_dest(self, Value::by_ref(dest_ptr))?; - } - self.memory.end_unaligned_read(); + self.read_maybe_aligned(aligned, |ectx| { + if let Ok(Some(src_val)) = ectx.try_read_value(src_ptr, dest_ty) { + write_dest(ectx, src_val)?; + } else { + let dest_ptr = ectx.alloc_ptr(dest_ty)?.into(); + ectx.copy(src_ptr, dest_ptr, dest_ty)?; + write_dest(ectx, Value::by_ref(dest_ptr))?; + } + Ok(()) + })?; } else { // Finally, we have the simple case where neither source nor destination are @@ -1203,10 +1198,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx> { match value { Value::ByRef(ptr, aligned) => { - self.memory.begin_unaligned_read(aligned); - let r = self.copy(ptr, dest, dest_ty); - self.memory.end_unaligned_read(); - r + self.read_maybe_aligned(aligned, |ectx| ectx.copy(ptr, dest, dest_ty)) }, Value::ByVal(primval) => { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); diff --git a/src/lvalue.rs b/src/lvalue.rs index 15b2dfdb83580..f4a1f050735aa 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -4,7 +4,7 @@ use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; use error::{EvalError, EvalResult}; -use eval_context::{EvalContext}; +use eval_context::EvalContext; use memory::MemoryPointer; use value::{PrimVal, Value, Pointer}; diff --git a/src/memory.rs b/src/memory.rs index bc0940e270b2f..739e7f2a858c2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -7,6 +7,7 @@ use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; use value::{PrimVal, self, Pointer}; +use eval_context::EvalContext; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -384,33 +385,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } return Ok(None); } - - #[inline] - pub(crate) fn begin_unaligned_read(&mut self, aligned: bool) { - assert!(self.reads_are_aligned, "Unaligned reads must not be nested"); - self.reads_are_aligned = aligned; - } - - #[inline] - pub(crate) fn end_unaligned_read(&mut self) { - self.reads_are_aligned = true; - } - - #[inline] - pub(crate) fn begin_unaligned_write(&mut self, aligned: bool) { - assert!(self.writes_are_aligned, "Unaligned writes must not be nested"); - self.writes_are_aligned = aligned; - } - - #[inline] - pub(crate) fn end_unaligned_write(&mut self) { - self.writes_are_aligned = true; - } - - #[inline] - pub(crate) fn assert_all_aligned(&mut self) { - assert!(self.reads_are_aligned && self.writes_are_aligned, "Someone forgot to clear the 'unaligned' flag"); - } } /// Allocation accessors @@ -1101,3 +1075,46 @@ fn bit_index(bits: u64) -> (usize, usize) { assert_eq!(b as usize as u64, b); (a as usize, b as usize) } + +//////////////////////////////////////////////////////////////////////////////// +// Unaligned accesses +//////////////////////////////////////////////////////////////////////////////// + +pub(crate) trait HasMemory<'a, 'tcx> { + fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx>; + + // These are not supposed to be overriden. + fn read_maybe_aligned(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> + where F: FnOnce(&mut Self) -> EvalResult<'tcx, T> + { + assert!(self.memory_mut().reads_are_aligned, "Unaligned reads must not be nested"); + self.memory_mut().reads_are_aligned = aligned; + let t = f(self); + self.memory_mut().reads_are_aligned = true; + t + } + + fn write_maybe_aligned(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> + where F: FnOnce(&mut Self) -> EvalResult<'tcx, T> + { + assert!(self.memory_mut().writes_are_aligned, "Unaligned writes must not be nested"); + self.memory_mut().writes_are_aligned = aligned; + let t = f(self); + self.memory_mut().writes_are_aligned = true; + t + } +} + +impl<'a, 'tcx> HasMemory<'a, 'tcx> for Memory<'a, 'tcx> { + #[inline] + fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { + self + } +} + +impl<'a, 'tcx> HasMemory<'a, 'tcx> for EvalContext<'a, 'tcx> { + #[inline] + fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { + &mut self.memory + } +} diff --git a/src/step.rs b/src/step.rs index f1d32ec69d81f..3aafda476363d 100644 --- a/src/step.rs +++ b/src/step.rs @@ -28,7 +28,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns true as long as there are more things to do. pub fn step(&mut self) -> EvalResult<'tcx, bool> { - self.memory.assert_all_aligned(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { return Ok(false); diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index 26b3ee5622bfd..da45d7b410a60 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -8,6 +8,7 @@ use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::{Lvalue, LvalueExtra}; use value::{PrimVal, PrimValKind, Value, Pointer}; +use memory::HasMemory; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -396,9 +397,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let src_ty = substs.type_at(0); let ptr = self.force_allocation(dest)?.to_ptr()?; - self.memory.begin_unaligned_write(/*aligned*/false); - self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; - self.memory.end_unaligned_write(); + self.write_maybe_aligned(/*aligned*/false, |ectx| { + ectx.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty) + })?; } "unchecked_shl" => { diff --git a/src/value.rs b/src/value.rs index ad4a66a16262d..f80a05805c2c3 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,7 @@ use std::mem::transmute; use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; -use memory::{Memory, MemoryPointer}; +use memory::{Memory, MemoryPointer, HasMemory}; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { unsafe { transmute::(bytes as u32) } @@ -170,10 +170,7 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ptr, aligned) => { - mem.begin_unaligned_read(aligned); - let r = mem.read_ptr(ptr.to_ptr()?); - mem.end_unaligned_read(); - r + mem.read_maybe_aligned(aligned, |mem| mem.read_ptr(ptr.to_ptr()?) ) }, ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()), } @@ -186,11 +183,11 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ref_ptr, aligned) => { - mem.begin_unaligned_read(aligned); - let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; - mem.end_unaligned_read(); - Ok((ptr, vtable.to_ptr()?)) + mem.read_maybe_aligned(aligned, |mem| { + let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; + Ok((ptr, vtable.to_ptr()?)) + }) } ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)), @@ -203,11 +200,11 @@ impl<'a, 'tcx: 'a> Value { use self::Value::*; match *self { ByRef(ref_ptr, aligned) => { - mem.begin_unaligned_read(aligned); - let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; - mem.end_unaligned_read(); - Ok((ptr, len)) + mem.write_maybe_aligned(aligned, |mem| { + let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?; + Ok((ptr, len)) + }) }, ByValPair(ptr, val) => { let len = val.to_u128()?; From 0fbbcae92d868c047d3cbab43434404c7b0c8c2c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 14:18:26 -0700 Subject: [PATCH 1084/1096] packed structs: test unsize coercions --- tests/run-pass/packed_struct.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs index ec1533304c1e9..7219649e728c9 100644 --- a/tests/run-pass/packed_struct.rs +++ b/tests/run-pass/packed_struct.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(unsize, coerce_unsized)] + #[repr(packed)] struct S { a: i32, @@ -22,6 +24,25 @@ fn test(t: Test2) { assert_eq!(x, 42); } +fn test_unsizing() { + #[repr(packed)] + struct UnalignedPtr<'a, T: ?Sized> + where T: 'a, + { + data: &'a T, + } + + impl<'a, T, U> std::ops::CoerceUnsized> for UnalignedPtr<'a, T> + where + T: std::marker::Unsize + ?Sized, + U: ?Sized, + { } + + let arr = [1, 2, 3]; + let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr }; + let _uns: UnalignedPtr<[i32]> = arr_unaligned; +} + fn main() { let mut x = S { a: 42, @@ -39,4 +60,6 @@ fn main() { assert_eq!({x.b}, 77); test(Test2 { x: 0, other: &Test1 { x: 0, other: &42 }}); + + test_unsizing(); } From 192da8819f2e936f7b944624a202a97405e689b3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 12 Jul 2017 14:51:47 +0200 Subject: [PATCH 1085/1096] Ensure that it is not possible to explicitly free stack memory --- src/error.rs | 21 ++++-- src/eval_context.rs | 31 ++++---- src/memory.rs | 121 ++++++++++++++++-------------- src/terminator/mod.rs | 20 ++--- src/traits.rs | 6 +- tests/compile-fail/double_free.rs | 8 ++ tests/compile-fail/stack_free.rs | 5 ++ 7 files changed, 122 insertions(+), 90 deletions(-) create mode 100644 tests/compile-fail/double_free.rs create mode 100644 tests/compile-fail/stack_free.rs diff --git a/src/error.rs b/src/error.rs index 3e1155e0b874d..403ca9539e5b0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt; use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; -use memory::MemoryPointer; +use memory::{MemoryPointer, Kind}; use rustc_const_math::ConstMathErr; use syntax::codemap::Span; @@ -12,6 +12,7 @@ pub enum EvalError<'tcx> { NoMirFor(String), UnterminatedCString(MemoryPointer), DanglingPointerDeref, + DoubleFree, InvalidMemoryAccess, InvalidFunctionPointer, InvalidBool, @@ -56,8 +57,8 @@ pub enum EvalError<'tcx> { AssumptionNotHeld, InlineAsm, TypeNotPrimitive(Ty<'tcx>), - ReallocatedStaticMemory, - DeallocatedStaticMemory, + ReallocatedWrongMemoryKind(Kind, Kind), + DeallocatedWrongMemoryKind(Kind, Kind), ReallocateNonBasePtr, DeallocateNonBasePtr, IncorrectAllocationInformation, @@ -84,6 +85,8 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to access memory through an invalid pointer", DanglingPointerDeref => "dangling pointer was dereferenced", + DoubleFree => + "tried to deallocate dangling pointer", InvalidFunctionPointer => "tried to use an integer pointer or a dangling pointer as a function pointer", InvalidBool => @@ -148,10 +151,10 @@ impl<'tcx> Error for EvalError<'tcx> { "miri does not support inline assembly", TypeNotPrimitive(_) => "expected primitive type, got nonprimitive", - ReallocatedStaticMemory => - "tried to reallocate static memory", - DeallocatedStaticMemory => - "tried to deallocate static memory", + ReallocatedWrongMemoryKind(_, _) => + "tried to reallocate memory from one kind to another", + DeallocatedWrongMemoryKind(_, _) => + "tried to deallocate memory of the wrong kind", ReallocateNonBasePtr => "tried to reallocate with a pointer not to the beginning of an existing object", DeallocateNonBasePtr => @@ -198,6 +201,10 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig, got), ArrayIndexOutOfBounds(span, len, index) => write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span), + ReallocatedWrongMemoryKind(old, new) => + write!(f, "tried to reallocate memory from {:?} to {:?}", old, new), + DeallocatedWrongMemoryKind(old, new) => + write!(f, "tried to deallocate {:?} memory but gave {:?} as the kind", old, new), Math(span, ref err) => write!(f, "{:?} at {:?}", err, span), Intrinsic(ref err) => diff --git a/src/eval_context.rs b/src/eval_context.rs index 58fabf694d1ea..ac39cc4339d16 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -154,7 +154,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ) -> EvalResult<'tcx, MemoryPointer> { let size = self.type_size_with_substs(ty, substs)?.expect("cannot alloc memory for unsized type"); let align = self.type_align_with_substs(ty, substs)?; - self.memory.allocate(size, align) + self.memory.allocate(size, align, ::memory::Kind::Stack) } pub fn memory(&self) -> &Memory<'a, 'tcx> { @@ -354,16 +354,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions Value::ByRef(ptr, _aligned) => // Alignment does not matter for this call - self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?, + self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, !mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; } }, } @@ -414,11 +414,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("deallocating local"); let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); - match self.memory.deallocate(ptr, None) { - // We could alternatively check whether the alloc_id is static before calling - // deallocate, but this is much simpler and is probably the rare case. - Ok(()) | Err(EvalError::DeallocatedStaticMemory) => {}, - other => return other, + match self.memory.get(ptr.alloc_id)?.kind { + ::memory::Kind::Static => {}, + ::memory::Kind::Stack => self.memory.deallocate(ptr, None, ::memory::Kind::Stack)?, + other => bug!("local contained non-stack memory: {:?}", other), } }; Ok(()) @@ -693,11 +692,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string())); } // FIXME: call the `exchange_malloc` lang item if available - if self.type_size(ty)?.expect("box only works with sized types") == 0 { + let size = self.type_size(ty)?.expect("box only works with sized types"); + if size == 0 { let align = self.type_align(ty)?; self.write_primval(dest, PrimVal::Bytes(align.into()), dest_ty)?; } else { - let ptr = self.alloc_ptr(ty)?; + let align = self.type_align(ty)?; + let ptr = self.memory.allocate(size, align, ::memory::Kind::Rust)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } } @@ -1032,7 +1033,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(global_val.value, ptr.into(), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { - self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, !global_val.mutable)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { @@ -1686,7 +1687,7 @@ pub fn eval_main<'a, 'tcx: 'a>( } // Return value - let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi())?; + let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi(), ::memory::Kind::Stack)?; cleanup_ptr = Some(ret_ptr); // Push our stack frame @@ -1728,7 +1729,7 @@ pub fn eval_main<'a, 'tcx: 'a>( while ecx.step()? {} if let Some(cleanup_ptr) = cleanup_ptr { - ecx.memory.deallocate(cleanup_ptr, None)?; + ecx.memory.deallocate(cleanup_ptr, None, ::memory::Kind::Stack)?; } return Ok(()); } diff --git a/src/memory.rs b/src/memory.rs index 6e70e3692c11f..f84dd31248245 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -35,19 +35,25 @@ pub struct Allocation { /// The alignment of the allocation to detect unaligned reads. pub align: u64, /// Whether the allocation may be modified. + pub mutable: bool, /// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this /// allocation is modified or deallocated in the future. - pub static_kind: StaticKind, + /// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate` + pub kind: Kind, } #[derive(Debug, PartialEq, Copy, Clone)] -pub enum StaticKind { - /// may be deallocated without breaking miri's invariants - NotStatic, - /// may be modified, but never deallocated - Mutable, - /// may neither be modified nor deallocated - Immutable, +pub enum Kind { + /// Error if deallocated any other way than `rust_deallocate` + Rust, + /// Error if deallocated any other way than `free` + C, + /// Error if deallocated via `rust_deallocate` + Stack, + /// May never be deallocated + Static, + /// Part of env var emulation + Env, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -181,14 +187,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(MemoryPointer::new(alloc_id, 0)); } - let ptr = self.allocate(bytes.len() as u64, 1)?; + let ptr = self.allocate(bytes.len() as u64, 1, Kind::Static)?; self.write_bytes(PrimVal::Ptr(ptr), bytes)?; - self.mark_static_initalized(ptr.alloc_id, false)?; + self.mark_static_initalized(ptr.alloc_id, true)?; self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id); Ok(ptr) } - pub fn allocate(&mut self, size: u64, align: u64) -> EvalResult<'tcx, MemoryPointer> { + pub fn allocate(&mut self, size: u64, align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> { assert_ne!(align, 0); assert!(align.is_power_of_two()); @@ -206,7 +212,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), align, - static_kind: StaticKind::NotStatic, + kind, + mutable: true, }; let id = self.next_id; self.next_id.0 += 1; @@ -214,49 +221,45 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(MemoryPointer::new(id, 0)) } - // TODO(solson): Track which allocations were returned from __rust_allocate and report an error - // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, MemoryPointer> { + pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> { use std::cmp::min; - // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { return Err(EvalError::ReallocateNonBasePtr); } - if self.get(ptr.alloc_id).ok().map_or(false, |alloc| alloc.static_kind != StaticKind::NotStatic) { - return Err(EvalError::ReallocatedStaticMemory); + if let Ok(alloc) = self.get(ptr.alloc_id) { + if alloc.kind != kind { + return Err(EvalError::ReallocatedWrongMemoryKind(alloc.kind, kind)); + } } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" - let new_ptr = self.allocate(new_size, new_align)?; + let new_ptr = self.allocate(new_size, new_align, kind)?; self.copy(ptr.into(), new_ptr.into(), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; - self.deallocate(ptr, Some((old_size, old_align)))?; + self.deallocate(ptr, Some((old_size, old_align)), kind)?; Ok(new_ptr) } - // TODO(solson): See comment on `reallocate`. - pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>) -> EvalResult<'tcx> { + pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>, kind: Kind) -> EvalResult<'tcx> { if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { - // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::DeallocateNonBasePtr); } - { - // deallocate_local in eval_context.rs relies on nothing actually having changed when this error occurs. - // So we do this test in advance. - let alloc = self.get(ptr.alloc_id)?; - if alloc.static_kind != StaticKind::NotStatic { - return Err(EvalError::DeallocatedStaticMemory); - } - if let Some((size, align)) = size_and_align { - if size != alloc.bytes.len() as u64 || align != alloc.align { - return Err(EvalError::IncorrectAllocationInformation); - } + let alloc = match self.alloc_map.remove(&ptr.alloc_id) { + Some(alloc) => alloc, + None => return Err(EvalError::DoubleFree), + }; + + if alloc.kind != kind { + return Err(EvalError::DeallocatedWrongMemoryKind(alloc.kind, kind)); + } + if let Some((size, align)) = size_and_align { + if size != alloc.bytes.len() as u64 || align != alloc.align { + return Err(EvalError::IncorrectAllocationInformation); } } - let alloc = self.alloc_map.remove(&ptr.alloc_id).expect("already verified"); self.memory_usage -= alloc.bytes.len() as u64; debug!("deallocated : {}", ptr.alloc_id); @@ -401,10 +404,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { match self.alloc_map.get_mut(&id) { - Some(alloc) => match alloc.static_kind { - StaticKind::Mutable | - StaticKind::NotStatic => Ok(alloc), - StaticKind::Immutable => Err(EvalError::ModifiedConstantMemory), + Some(alloc) => if alloc.mutable { + Ok(alloc) + } else { + Err(EvalError::ModifiedConstantMemory) }, None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), @@ -473,10 +476,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - let immutable = match alloc.static_kind { - StaticKind::Mutable => " (static mut)", - StaticKind::Immutable => " (immutable)", - StaticKind::NotStatic => "", + let immutable = match (alloc.kind, alloc.mutable) { + (Kind::Static, true) => " (static mut)", + (Kind::Static, false) => " (immutable)", + (Kind::Env, _) => " (env var)", + (Kind::C, _) => " (malloc)", + (Kind::Rust, _) => " (heap)", + (Kind::Stack, _) => " (stack)", }; trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable); @@ -503,7 +509,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let leaks: Vec<_> = self.alloc_map .iter() .filter_map(|(&key, val)| { - if val.static_kind == StaticKind::NotStatic { + if val.kind != Kind::Static { Some(key) } else { None @@ -578,26 +584,31 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } /// mark an allocation pointed to by a static as static and initialized - pub fn mark_inner_allocation(&mut self, alloc: AllocId, mutable: bool) -> EvalResult<'tcx> { + pub fn mark_inner_allocation(&mut self, alloc: AllocId, make_immutable: bool) -> EvalResult<'tcx> { // relocations into other statics are not "inner allocations" if !self.static_alloc.contains(&alloc) { - self.mark_static_initalized(alloc, mutable)?; + self.mark_static_initalized(alloc, make_immutable)?; } Ok(()) } /// mark an allocation as static and initialized, either mutable or not - pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { - trace!("mark_static_initialized {:?}, mutable: {:?}", alloc_id, mutable); + pub fn mark_static_initalized(&mut self, alloc_id: AllocId, make_immutable: bool) -> EvalResult<'tcx> { + trace!("mark_static_initalized {:?}, make_immutable: {:?}", alloc_id, make_immutable); // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { ref mut relocations, static_kind: ref mut kind @ StaticKind::NotStatic, .. }) => { - *kind = if mutable { - StaticKind::Mutable - } else { - StaticKind::Immutable - }; + Some(&mut Allocation { kind: Kind::Static, ref mut mutable, .. }) => { + if make_immutable { + *mutable = false; + } + return Ok(()); + }, + Some(&mut Allocation { ref mut relocations, ref mut kind, ref mut mutable, .. }) => { + *kind = Kind::Static; + if make_immutable { + *mutable = false; + } // take out the relocations vector to free the borrow on self, so we can call // mark recursively mem::replace(relocations, Default::default()) @@ -607,7 +618,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - self.mark_inner_allocation(alloc, mutable)?; + self.mark_inner_allocation(alloc, make_immutable)?; } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 0c25dc3bed37b..c4a8d2e73c291 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::{MemoryPointer, TlsKey}; +use memory::{MemoryPointer, TlsKey, Kind}; use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; @@ -558,7 +558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - let ptr = self.memory.allocate(size, align)?; + let ptr = self.memory.allocate(size, align, Kind::Rust)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_alloc_zeroed" => { @@ -570,7 +570,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - let ptr = self.memory.allocate(size, align)?; + let ptr = self.memory.allocate(size, align, Kind::Rust)?; self.memory.write_repeat(ptr.into(), 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } @@ -584,7 +584,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - self.memory.deallocate(ptr, Some((old_size, align)))?; + self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?; } "alloc::heap::::__rust_realloc" => { let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; @@ -601,7 +601,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if !new_align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align)); } - let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align)?; + let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } @@ -657,7 +657,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_null(dest, dest_ty)?; } else { let align = self.memory.pointer_size(); - let ptr = self.memory.allocate(size, align)?; + let ptr = self.memory.allocate(size, align, Kind::C)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } } @@ -665,7 +665,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "free" => { let ptr = args[0].into_ptr(&mut self.memory)?; if !ptr.is_null()? { - self.memory.deallocate(ptr.to_ptr()?, None)?; + self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?; } } @@ -789,7 +789,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } if let Some(old) = success { if let Some(var) = old { - self.memory.deallocate(var, None)?; + self.memory.deallocate(var, None, Kind::Env)?; } self.write_null(dest, dest_ty)?; } else { @@ -812,11 +812,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } if let Some((name, value)) = new { // +1 for the null terminator - let value_copy = self.memory.allocate((value.len() + 1) as u64, 1)?; + let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?; self.memory.write_bytes(PrimVal::Ptr(value_copy), &value)?; self.memory.write_bytes(PrimVal::Ptr(value_copy.offset(value.len() as u64, self.memory.layout)?), &[0])?; if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { - self.memory.deallocate(var, None)?; + self.memory.deallocate(var, None, Kind::Env)?; } self.write_null(dest, dest_ty)?; } else { diff --git a/src/traits.rs b/src/traits.rs index bfb923510bbb3..6fbb973d7b144 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,7 +1,7 @@ use rustc::traits::{self, Reveal}; use eval_context::EvalContext; -use memory::MemoryPointer; +use memory::{MemoryPointer, Kind}; use value::{Value, PrimVal}; use rustc::hir::def_id::DefId; @@ -51,7 +51,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_size = self.memory.pointer_size(); let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref); - let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size)?; + let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size, Kind::Static)?; let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty); let drop = self.memory.create_fn_alloc(drop); @@ -68,7 +68,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.memory.mark_static_initalized(vtable.alloc_id, false)?; + self.memory.mark_static_initalized(vtable.alloc_id, true)?; Ok(vtable) } diff --git a/tests/compile-fail/double_free.rs b/tests/compile-fail/double_free.rs new file mode 100644 index 0000000000000..29ccf8c32131d --- /dev/null +++ b/tests/compile-fail/double_free.rs @@ -0,0 +1,8 @@ +fn main() { + let x = Box::new(42); + { + let bad_box: Box = unsafe { std::ptr::read(&x) }; + drop(bad_box); + } + drop(x); //~ ERROR dangling pointer was dereferenced +} diff --git a/tests/compile-fail/stack_free.rs b/tests/compile-fail/stack_free.rs new file mode 100644 index 0000000000000..1828d809b2082 --- /dev/null +++ b/tests/compile-fail/stack_free.rs @@ -0,0 +1,5 @@ +fn main() { + let x = 42; + let bad_box = unsafe { std::mem::transmute::<&i32, Box>(&x) }; + drop(bad_box); //~ ERROR tried to deallocate Stack memory but gave Rust as the kind +} From 70a914cd34432b49da06c9778fceebd638bfa91d Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 16:58:36 +0200 Subject: [PATCH 1086/1096] Clarify documentation --- src/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory.rs b/src/memory.rs index f84dd31248245..b25d69e5d0778 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -48,7 +48,7 @@ pub enum Kind { Rust, /// Error if deallocated any other way than `free` C, - /// Error if deallocated via `rust_deallocate` + /// Error if deallocated except during a stack pop Stack, /// May never be deallocated Static, From eb01c3fdd2ee6141f5e6a43d60f8a095e2c807f4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 17:25:17 +0200 Subject: [PATCH 1087/1096] Use enum instead of boolean --- src/const_eval.rs | 8 +++++++- src/eval_context.rs | 27 +++++++++++++-------------- src/lvalue.rs | 9 +++++---- src/memory.rs | 41 ++++++++++++++++++++--------------------- src/step.rs | 23 ++++++++++++++--------- src/traits.rs | 4 ++-- 6 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/const_eval.rs b/src/const_eval.rs index 771b740b3755a..b538d2d835786 100644 --- a/src/const_eval.rs +++ b/src/const_eval.rs @@ -1,5 +1,6 @@ use rustc::traits::Reveal; use rustc::ty::{self, TyCtxt, Ty, Instance}; +use syntax::ast::Mutability; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue}; @@ -25,7 +26,12 @@ pub fn eval_body_as_primval<'a, 'tcx>( ecx.tcx, ty::ParamEnv::empty(Reveal::All), mir.span); - let cleanup = StackPopCleanup::MarkStatic(mutable); + let mutability = if mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); trace!("pushing stack frame for global: {}", name); ecx.push_stack_frame( diff --git a/src/eval_context.rs b/src/eval_context.rs index ac39cc4339d16..76ed797684b88 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -12,7 +12,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder}; use rustc::traits; use rustc_data_structures::indexed_vec::Idx; use syntax::codemap::{self, DUMMY_SP, Span}; -use syntax::ast; +use syntax::ast::{self, Mutability}; use syntax::abi::Abi; use error::{EvalError, EvalResult}; @@ -98,8 +98,7 @@ pub enum StackPopCleanup { /// isn't modifyable afterwards in case of constants. /// In case of `static mut`, mark the memory to ensure it's never marked as immutable through /// references or deallocated - /// The bool decides whether the value is mutable (true) or not (false) - MarkStatic(bool), + MarkStatic(Mutability), /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), @@ -354,23 +353,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions Value::ByRef(ptr, _aligned) => // Alignment does not matter for this call - self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, !mutable)?, + self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.mark_inner_allocation(ptr.alloc_id, !mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } }, } // see comment on `initialized` field assert!(!global_value.initialized); global_value.initialized = true; - assert!(global_value.mutable); + assert_eq!(global_value.mutable, Mutability::Mutable); global_value.mutable = mutable; } else { bug!("StackPopCleanup::MarkStatic on: {:?}", frame.return_lvalue); @@ -1023,7 +1022,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Lvalue::Ptr { .. } => lvalue, Lvalue::Global(cid) => { - let global_val = *self.globals.get(&cid).expect("global not cached"); + let global_val = self.globals.get(&cid).expect("global not cached").clone(); match global_val.value { Value::ByRef(ptr, aligned) => Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }, @@ -1033,7 +1032,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(global_val.value, ptr.into(), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { - self.memory.mark_static_initalized(ptr.alloc_id, !global_val.mutable)?; + self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { @@ -1109,8 +1108,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match dest { Lvalue::Global(cid) => { - let dest = *self.globals.get_mut(&cid).expect("global should be cached"); - if !dest.mutable { + let dest = self.globals.get_mut(&cid).expect("global should be cached").clone(); + if dest.mutable == Mutability::Immutable { return Err(EvalError::ModifiedConstantMemory); } let write_dest = |this: &mut Self, val| { @@ -1595,8 +1594,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn modify_global(&mut self, cid: GlobalId<'tcx>, f: F) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let mut val = *self.globals.get(&cid).expect("global not cached"); - if !val.mutable { + let mut val = self.globals.get(&cid).expect("global not cached").clone(); + if val.mutable == Mutability::Immutable { return Err(EvalError::ModifiedConstantMemory); } val.value = f(self, val.value)?; diff --git a/src/lvalue.rs b/src/lvalue.rs index f4a1f050735aa..5e02bb4a57221 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -2,6 +2,7 @@ use rustc::mir; use rustc::ty::layout::{Size, Align}; use rustc::ty::{self, Ty}; use rustc_data_structures::indexed_vec::Idx; +use syntax::ast::Mutability; use error::{EvalError, EvalResult}; use eval_context::EvalContext; @@ -51,7 +52,7 @@ pub struct GlobalId<'tcx> { pub(super) promoted: Option, } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct Global<'tcx> { pub(super) value: Value, /// Only used in `force_allocation` to ensure we don't mark the memory @@ -59,7 +60,7 @@ pub struct Global<'tcx> { /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets /// lifted to an allocation before the static is fully initialized pub(super) initialized: bool, - pub(super) mutable: bool, + pub(super) mutable: Mutability, pub(super) ty: Ty<'tcx>, } @@ -113,13 +114,13 @@ impl<'tcx> Global<'tcx> { pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self { Global { value: Value::ByVal(PrimVal::Undef), - mutable: true, + mutable: Mutability::Mutable, ty, initialized: false, } } - pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: bool) -> Self { + pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: Mutability) -> Self { Global { value, mutable, diff --git a/src/memory.rs b/src/memory.rs index b25d69e5d0778..75ebff2f97b0b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,6 +4,7 @@ use std::{fmt, iter, ptr, mem, io}; use rustc::ty; use rustc::ty::layout::{self, TargetDataLayout}; +use syntax::ast::Mutability; use error::{EvalError, EvalResult}; use value::{PrimVal, self, Pointer}; @@ -35,7 +36,7 @@ pub struct Allocation { /// The alignment of the allocation to detect unaligned reads. pub align: u64, /// Whether the allocation may be modified. - pub mutable: bool, + pub mutable: Mutability, /// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this /// allocation is modified or deallocated in the future. /// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate` @@ -50,6 +51,11 @@ pub enum Kind { C, /// Error if deallocated except during a stack pop Stack, + /// Static in the process of being initialized. + /// The difference is important: An immutable static referring to a + /// mutable initialized static will freeze immutably and would not + /// be able to distinguish already initialized statics from uninitialized ones + UninitializedStatic, /// May never be deallocated Static, /// Part of env var emulation @@ -189,7 +195,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let ptr = self.allocate(bytes.len() as u64, 1, Kind::Static)?; self.write_bytes(PrimVal::Ptr(ptr), bytes)?; - self.mark_static_initalized(ptr.alloc_id, true)?; + self.mark_static_initalized(ptr.alloc_id, Mutability::Mutable)?; self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id); Ok(ptr) } @@ -213,7 +219,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { undef_mask: UndefMask::new(size), align, kind, - mutable: true, + mutable: Mutability::Mutable, }; let id = self.next_id; self.next_id.0 += 1; @@ -404,7 +410,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { match self.alloc_map.get_mut(&id) { - Some(alloc) => if alloc.mutable { + Some(alloc) => if alloc.mutable == Mutability::Mutable { Ok(alloc) } else { Err(EvalError::ModifiedConstantMemory) @@ -477,8 +483,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } let immutable = match (alloc.kind, alloc.mutable) { - (Kind::Static, true) => " (static mut)", - (Kind::Static, false) => " (immutable)", + (Kind::UninitializedStatic, _) => " (static in the process of initialization)", + (Kind::Static, Mutability::Mutable) => " (static mut)", + (Kind::Static, Mutability::Immutable) => " (immutable)", (Kind::Env, _) => " (env var)", (Kind::C, _) => " (malloc)", (Kind::Rust, _) => " (heap)", @@ -584,31 +591,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } /// mark an allocation pointed to by a static as static and initialized - pub fn mark_inner_allocation(&mut self, alloc: AllocId, make_immutable: bool) -> EvalResult<'tcx> { + pub fn mark_inner_allocation(&mut self, alloc: AllocId, mutability: Mutability) -> EvalResult<'tcx> { // relocations into other statics are not "inner allocations" if !self.static_alloc.contains(&alloc) { - self.mark_static_initalized(alloc, make_immutable)?; + self.mark_static_initalized(alloc, mutability)?; } Ok(()) } /// mark an allocation as static and initialized, either mutable or not - pub fn mark_static_initalized(&mut self, alloc_id: AllocId, make_immutable: bool) -> EvalResult<'tcx> { - trace!("mark_static_initalized {:?}, make_immutable: {:?}", alloc_id, make_immutable); + pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutability: Mutability) -> EvalResult<'tcx> { + trace!("mark_static_initalized {:?}, mutability: {:?}", alloc_id, mutability); // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { kind: Kind::Static, ref mut mutable, .. }) => { - if make_immutable { - *mutable = false; - } - return Ok(()); - }, - Some(&mut Allocation { ref mut relocations, ref mut kind, ref mut mutable, .. }) => { + Some(&mut Allocation { ref mut relocations, kind: ref mut kind @ Kind::UninitializedStatic, ref mut mutable, .. }) => { *kind = Kind::Static; - if make_immutable { - *mutable = false; - } + *mutable = mutability; // take out the relocations vector to free the borrow on self, so we can call // mark recursively mem::replace(relocations, Default::default()) @@ -618,7 +617,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - self.mark_inner_allocation(alloc, make_immutable)?; + self.mark_inner_allocation(alloc, mutability)?; } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/step.rs b/src/step.rs index 3aafda476363d..d854218d7a90d 100644 --- a/src/step.rs +++ b/src/step.rs @@ -15,6 +15,7 @@ use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; use syntax::codemap::Span; +use syntax::ast::Mutability; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { @@ -162,7 +163,7 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span, - shared: bool, + mutability: Mutability, ) { let instance = self.ecx.resolve_associated_const(def_id, substs); let cid = GlobalId { instance, promoted: None }; @@ -171,18 +172,22 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { } if self.ecx.tcx.has_attr(def_id, "linkage") { trace!("Initializing an extern global with NULL"); - self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), !shared)); + self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), mutability)); return; } self.try(|this| { let mir = this.ecx.load_mir(instance.def)?; this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let mutable = !shared || - !mir.return_ty.is_freeze( + let internally_mutable = !mir.return_ty.is_freeze( this.ecx.tcx, ty::ParamEnv::empty(Reveal::All), span); - let cleanup = StackPopCleanup::MarkStatic(mutable); + let mutability = if mutability == Mutability::Mutable || internally_mutable { + Mutability::Mutable + } else { + Mutability::Immutable + }; + let cleanup = StackPopCleanup::MarkStatic(mutability); let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); trace!("pushing stack frame for global: {}", name); this.ecx.push_stack_frame( @@ -214,7 +219,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { // already computed by rustc mir::Literal::Value { .. } => {} mir::Literal::Item { def_id, substs } => { - self.global_item(def_id, substs, constant.span, true); + self.global_item(def_id, substs, constant.span, Mutability::Immutable); }, mir::Literal::Promoted { index } => { let cid = GlobalId { @@ -233,7 +238,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { constant.span, mir, Lvalue::Global(cid), - StackPopCleanup::MarkStatic(false), + StackPopCleanup::MarkStatic(Mutability::Immutable), ) }); } @@ -254,7 +259,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) { if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item { if let hir::ItemStatic(_, m, _) = *node { - self.global_item(def_id, substs, span, m == hir::MutImmutable); + self.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable }); return; } else { bug!("static def id doesn't point to static"); @@ -265,7 +270,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { } else { let def = self.ecx.tcx.describe_def(def_id).expect("static not found"); if let hir::def::Def::Static(_, mutable) = def { - self.global_item(def_id, substs, span, !mutable); + self.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable }); } else { bug!("static found but isn't a static: {:?}", def); } diff --git a/src/traits.rs b/src/traits.rs index 6fbb973d7b144..b3e77a9299c13 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -8,7 +8,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; use syntax::codemap::DUMMY_SP; -use syntax::ast; +use syntax::ast::{self, Mutability}; use error::{EvalResult, EvalError}; @@ -68,7 +68,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - self.memory.mark_static_initalized(vtable.alloc_id, true)?; + self.memory.mark_static_initalized(vtable.alloc_id, Mutability::Mutable)?; Ok(vtable) } From 7701ff2f898b24cb17391b2378a9c9fe7272aa72 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 17:25:27 +0200 Subject: [PATCH 1088/1096] Remove duplicate test --- tests/compile-fail/double_free.rs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 tests/compile-fail/double_free.rs diff --git a/tests/compile-fail/double_free.rs b/tests/compile-fail/double_free.rs deleted file mode 100644 index 29ccf8c32131d..0000000000000 --- a/tests/compile-fail/double_free.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - let x = Box::new(42); - { - let bad_box: Box = unsafe { std::ptr::read(&x) }; - drop(bad_box); - } - drop(x); //~ ERROR dangling pointer was dereferenced -} From 2e562a4d642736f78f52292feb05a5a1048de7d5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 19:10:14 +0200 Subject: [PATCH 1089/1096] Fix static mutation tests --- src/eval_context.rs | 2 +- src/memory.rs | 23 ++++++++++++++++++++--- src/traits.rs | 2 +- tests/compile-fail/stack_free.rs | 4 +++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 76ed797684b88..07f63fad6cf43 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -414,7 +414,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); match self.memory.get(ptr.alloc_id)?.kind { - ::memory::Kind::Static => {}, + ::memory::Kind::Static | ::memory::Kind::UninitializedStatic => {}, ::memory::Kind::Stack => self.memory.deallocate(ptr, None, ::memory::Kind::Stack)?, other => bug!("local contained non-stack memory: {:?}", other), } diff --git a/src/memory.rs b/src/memory.rs index 75ebff2f97b0b..6b181e31063ab 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -193,9 +193,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(MemoryPointer::new(alloc_id, 0)); } - let ptr = self.allocate(bytes.len() as u64, 1, Kind::Static)?; + let ptr = self.allocate(bytes.len() as u64, 1, Kind::UninitializedStatic)?; self.write_bytes(PrimVal::Ptr(ptr), bytes)?; - self.mark_static_initalized(ptr.alloc_id, Mutability::Mutable)?; + self.mark_static_initalized(ptr.alloc_id, Mutability::Immutable)?; self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id); Ok(ptr) } @@ -605,7 +605,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { - Some(&mut Allocation { ref mut relocations, kind: ref mut kind @ Kind::UninitializedStatic, ref mut mutable, .. }) => { + Some(&mut Allocation { ref mut relocations, ref mut kind, ref mut mutable, .. }) => { + match *kind { + // const eval results can refer to "locals". + // E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1` + Kind::Stack | + // The entire point of this function + Kind::UninitializedStatic | + // In the future const eval will allow heap allocations so we'll need to protect them + // from deallocation, too + Kind::Rust | + Kind::C => {}, + Kind::Static => { + trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized"); + return Ok(()); + }, + // FIXME: This could be allowed, but not for env vars set during miri execution + Kind::Env => return Err(EvalError::Unimplemented("statics can't refer to env vars".to_owned())), + } *kind = Kind::Static; *mutable = mutability; // take out the relocations vector to free the borrow on self, so we can call diff --git a/src/traits.rs b/src/traits.rs index b3e77a9299c13..68d027bc63384 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -51,7 +51,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr_size = self.memory.pointer_size(); let methods = ::rustc::traits::get_vtable_methods(self.tcx, trait_ref); - let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size, Kind::Static)?; + let vtable = self.memory.allocate(ptr_size * (3 + methods.count() as u64), ptr_size, Kind::UninitializedStatic)?; let drop = ::eval_context::resolve_drop_in_place(self.tcx, ty); let drop = self.memory.create_fn_alloc(drop); diff --git a/tests/compile-fail/stack_free.rs b/tests/compile-fail/stack_free.rs index 1828d809b2082..08ff7457b76be 100644 --- a/tests/compile-fail/stack_free.rs +++ b/tests/compile-fail/stack_free.rs @@ -1,5 +1,7 @@ +// error-pattern: tried to deallocate Stack memory but gave Rust as the kind + fn main() { let x = 42; let bad_box = unsafe { std::mem::transmute::<&i32, Box>(&x) }; - drop(bad_box); //~ ERROR tried to deallocate Stack memory but gave Rust as the kind + drop(bad_box); } From 45ab975610d67f2fe6d55bc4ea0d72d80e5083ac Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 14 Jul 2017 17:46:28 +0200 Subject: [PATCH 1090/1096] Add a comment explaining the static "local" during `deallocate_local` --- src/eval_context.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 07f63fad6cf43..2f28063ff86dd 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -414,7 +414,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = ptr.to_ptr()?; self.memory.dump_alloc(ptr.alloc_id); match self.memory.get(ptr.alloc_id)?.kind { - ::memory::Kind::Static | ::memory::Kind::UninitializedStatic => {}, + // for a constant like `const FOO: &i32 = &1;` the local containing + // the `1` is referred to by the global. We transitively marked everything + // the global refers to as static itself, so we don't free it here + ::memory::Kind::Static => {} ::memory::Kind::Stack => self.memory.deallocate(ptr, None, ::memory::Kind::Stack)?, other => bug!("local contained non-stack memory: {:?}", other), } From 2312ac8db6afe6e2cc7ec2be47a2582777a2f8df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 19 Jun 2017 20:13:26 -0700 Subject: [PATCH 1091/1096] lvalue: refactoring to permit applying a mir projection to a miri lvalue --- src/lvalue.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index f4a1f050735aa..3aa3f01122ab7 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -189,7 +189,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(val); } let lvalue = self.eval_lvalue(lvalue)?; + self.read_lvalue(lvalue, ty) + } + fn read_lvalue(&self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if ty.is_never() { return Err(EvalError::Unreachable); } @@ -219,7 +222,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Lvalue::Global(GlobalId { instance, promoted: None }) } - Projection(ref proj) => return self.eval_lvalue_projection(proj), + Projection(ref proj) => { + let ty = self.lvalue_ty(&proj.base); + let lvalue = self.eval_lvalue(&proj.base)?; + return self.eval_lvalue_projection(lvalue, ty, &proj.elem); + } }; if log_enabled!(::log::LogLevel::Trace) { @@ -348,19 +355,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_lvalue_projection( &mut self, - proj: &mir::LvalueProjection<'tcx>, + base: Lvalue<'tcx>, + base_ty: Ty<'tcx>, + proj_elem: &mir::ProjectionElem<'tcx, mir::Operand<'tcx>>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { use rustc::mir::ProjectionElem::*; - let (ptr, extra, aligned) = match proj.elem { + let (ptr, extra, aligned) = match *proj_elem { Field(field, field_ty) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); return self.lvalue_field(base, field.index(), base_ty, field_ty); } Downcast(_, variant) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty)?; // FIXME(solson) let base = self.force_allocation(base)?; @@ -376,8 +381,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Deref => { - let base_ty = self.lvalue_ty(&proj.base); - let val = self.eval_and_read_lvalue(&proj.base)?; + let val = self.read_lvalue(base, base_ty)?; let pointee_type = match base_ty.sty { ty::TyRawPtr(ref tam) | @@ -402,8 +406,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Index(ref operand) => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); @@ -419,8 +421,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ConstantIndex { offset, min_length, from_end } => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); @@ -440,8 +440,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Subslice { from, to } => { - let base = self.eval_lvalue(&proj.base)?; - let base_ty = self.lvalue_ty(&proj.base); // FIXME(solson) let base = self.force_allocation(base)?; let (base_ptr, _, aligned) = base.to_ptr_extra_aligned(); From ff9192e3469bd0eca4207a6c55b372384d5d7f5a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 18 Jul 2017 13:50:54 -0700 Subject: [PATCH 1092/1096] remove reundant dangling checks in {r,d}eallocate --- src/memory.rs | 4 ++-- tests/compile-fail/deallocate-twice.rs | 2 +- tests/compile-fail/reallocate-dangling.rs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/compile-fail/reallocate-dangling.rs diff --git a/src/memory.rs b/src/memory.rs index 6b181e31063ab..cf7f969be8ecd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -230,7 +230,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> { use std::cmp::min; - if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { + if ptr.offset != 0 { return Err(EvalError::ReallocateNonBasePtr); } if let Ok(alloc) = self.get(ptr.alloc_id) { @@ -248,7 +248,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>, kind: Kind) -> EvalResult<'tcx> { - if ptr.offset != 0 || self.get(ptr.alloc_id).is_err() { + if ptr.offset != 0 { return Err(EvalError::DeallocateNonBasePtr); } diff --git a/tests/compile-fail/deallocate-twice.rs b/tests/compile-fail/deallocate-twice.rs index 3c4399eaa3ed6..fd3cccfd53a91 100644 --- a/tests/compile-fail/deallocate-twice.rs +++ b/tests/compile-fail/deallocate-twice.rs @@ -5,7 +5,7 @@ extern crate alloc; use alloc::heap::Heap; use alloc::allocator::*; -// error-pattern: tried to deallocate with a pointer not to the beginning of an existing object +// error-pattern: tried to deallocate dangling pointer use alloc::heap::*; fn main() { diff --git a/tests/compile-fail/reallocate-dangling.rs b/tests/compile-fail/reallocate-dangling.rs new file mode 100644 index 0000000000000..54636b5d2005c --- /dev/null +++ b/tests/compile-fail/reallocate-dangling.rs @@ -0,0 +1,17 @@ +#![feature(alloc, allocator_api)] + +extern crate alloc; + +use alloc::heap::Heap; +use alloc::allocator::*; + +// error-pattern: dangling pointer was dereferenced + +use alloc::heap::*; +fn main() { + unsafe { + let x = Heap.alloc(Layout::from_size_align_unchecked(1, 1)).unwrap(); + Heap.dealloc(x, Layout::from_size_align_unchecked(1, 1)); + Heap.realloc(x, Layout::from_size_align_unchecked(1, 1), Layout::from_size_align_unchecked(1, 1)); + } +} From 9bccfd388c18f68fe39544b0b7858b18ee3afbe8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 18 Jul 2017 13:56:01 -0700 Subject: [PATCH 1093/1096] use libstd methods for floating-point <-> bytes conversion --- src/value.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/value.rs b/src/value.rs index f80a05805c2c3..606a3516485f4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,26 +1,25 @@ #![allow(unknown_lints)] #![allow(float_cmp)] -use std::mem::transmute; use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; use memory::{Memory, MemoryPointer, HasMemory}; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { - unsafe { transmute::(bytes as u32) } + f32::from_bits(bytes as u32) } pub(super) fn bytes_to_f64(bytes: u128) -> f64 { - unsafe { transmute::(bytes as u64) } + f64::from_bits(bytes as u64) } pub(super) fn f32_to_bytes(f: f32) -> u128 { - unsafe { transmute::(f) as u128 } + f.to_bits() as u128 } pub(super) fn f64_to_bytes(f: f64) -> u128 { - unsafe { transmute::(f) as u128 } + f.to_bits() as u128 } /// A `Value` represents a single self-contained Rust value. From d9d792ba03e89c36e9f505fa7ed7318eb6903c17 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 19 Jul 2017 11:06:07 +0200 Subject: [PATCH 1094/1096] Export types and functions needed by priroda --- src/lib.rs | 1 + src/lvalue.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d87a9ab653583..424affa92a8c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub use value::{ PrimVal, PrimValKind, Value, + Pointer, }; pub use const_eval::{ diff --git a/src/lvalue.rs b/src/lvalue.rs index f673ff060ed65..9fc01eb0384f3 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -193,7 +193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.read_lvalue(lvalue, ty) } - fn read_lvalue(&self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if ty.is_never() { return Err(EvalError::Unreachable); } From 57f9b2f54753e81afa7f4cbdb2d49db50d899a89 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 19 Jul 2017 18:58:26 +0200 Subject: [PATCH 1095/1096] Prepare miri for merging it into rustc --- .editorconfig | 25 - .gitignore | 6 - .travis.yml | 36 - Cargo.lock | 256 ------- LICENSE-APACHE | 201 ------ LICENSE-MIT | 25 - benches/fibonacci.rs | 30 - benches/helpers/fibonacci_helper.rs | 12 - benches/helpers/fibonacci_helper_iterative.rs | 15 - benches/helpers/miri_helper.rs | 68 -- benches/helpers/mod.rs | 7 - benches/helpers/repeat.rs | 4 - benches/helpers/repeat_manual.rs | 7 - benches/helpers/smoke_helper.rs | 3 - benches/repeat.rs | 16 - benches/smoke.rs | 37 - cargo-miri-test/Cargo.lock | 14 - cargo-miri-test/Cargo.toml | 7 - cargo-miri-test/src/main.rs | 9 - cargo-miri-test/tests/foo.rs | 4 - src/bin/cargo-miri.rs | 180 ----- src/bin/miri.rs | 211 ------ README.md => src/librustc_mir/miri/README.md | 0 src/{ => librustc_mir/miri}/cast.rs | 0 src/{ => librustc_mir/miri}/const_eval.rs | 0 src/{ => librustc_mir/miri}/error.rs | 0 src/{ => librustc_mir/miri}/eval_context.rs | 0 src/{ => librustc_mir/miri}/lib.rs | 0 src/{ => librustc_mir/miri}/lvalue.rs | 0 src/{ => librustc_mir/miri}/memory.rs | 0 src/{ => librustc_mir/miri}/operator.rs | 0 src/{ => librustc_mir/miri}/step.rs | 0 .../miri}/terminator/drop.rs | 0 .../miri}/terminator/intrinsic.rs | 0 src/{ => librustc_mir/miri}/terminator/mod.rs | 0 src/{ => librustc_mir/miri}/traits.rs | 0 src/{ => librustc_mir/miri}/value.rs | 0 tex/final-presentation/latexmkrc | 12 - tex/final-presentation/rust-logo-512x512.png | Bin 96029 -> 0 bytes tex/final-presentation/slides.tex | 444 ------------ tex/report/latexmkrc | 12 - tex/report/miri-report.tex | 663 ------------------ xargo/Cargo.lock | 4 - xargo/Cargo.toml | 6 - xargo/Xargo.toml | 2 - xargo/build.sh | 3 - xargo/src/lib.rs | 0 47 files changed, 2319 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 Cargo.lock delete mode 100644 LICENSE-APACHE delete mode 100644 LICENSE-MIT delete mode 100644 benches/fibonacci.rs delete mode 100644 benches/helpers/fibonacci_helper.rs delete mode 100644 benches/helpers/fibonacci_helper_iterative.rs delete mode 100644 benches/helpers/miri_helper.rs delete mode 100644 benches/helpers/mod.rs delete mode 100644 benches/helpers/repeat.rs delete mode 100644 benches/helpers/repeat_manual.rs delete mode 100644 benches/helpers/smoke_helper.rs delete mode 100644 benches/repeat.rs delete mode 100644 benches/smoke.rs delete mode 100644 cargo-miri-test/Cargo.lock delete mode 100644 cargo-miri-test/Cargo.toml delete mode 100644 cargo-miri-test/src/main.rs delete mode 100644 cargo-miri-test/tests/foo.rs delete mode 100644 src/bin/cargo-miri.rs delete mode 100644 src/bin/miri.rs rename README.md => src/librustc_mir/miri/README.md (100%) rename src/{ => librustc_mir/miri}/cast.rs (100%) rename src/{ => librustc_mir/miri}/const_eval.rs (100%) rename src/{ => librustc_mir/miri}/error.rs (100%) rename src/{ => librustc_mir/miri}/eval_context.rs (100%) rename src/{ => librustc_mir/miri}/lib.rs (100%) rename src/{ => librustc_mir/miri}/lvalue.rs (100%) rename src/{ => librustc_mir/miri}/memory.rs (100%) rename src/{ => librustc_mir/miri}/operator.rs (100%) rename src/{ => librustc_mir/miri}/step.rs (100%) rename src/{ => librustc_mir/miri}/terminator/drop.rs (100%) rename src/{ => librustc_mir/miri}/terminator/intrinsic.rs (100%) rename src/{ => librustc_mir/miri}/terminator/mod.rs (100%) rename src/{ => librustc_mir/miri}/traits.rs (100%) rename src/{ => librustc_mir/miri}/value.rs (100%) delete mode 100644 tex/final-presentation/latexmkrc delete mode 100644 tex/final-presentation/rust-logo-512x512.png delete mode 100644 tex/final-presentation/slides.tex delete mode 100644 tex/report/latexmkrc delete mode 100644 tex/report/miri-report.tex delete mode 100644 xargo/Cargo.lock delete mode 100644 xargo/Cargo.toml delete mode 100644 xargo/Xargo.toml delete mode 100755 xargo/build.sh delete mode 100644 xargo/src/lib.rs diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 3c1f41bdcca6c..0000000000000 --- a/.editorconfig +++ /dev/null @@ -1,25 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - - -[*] -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 4 - -[*.rs] -indent_style = space -indent_size = 4 - -[*.toml] -indent_style = space -indent_size = 4 - -[*.md] -trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d32d9eb99afd2..0000000000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -target -/doc -tex/*/out -*.dot -*.mir -*.rs.bk diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d9dd443d7ac1a..0000000000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: rust -rust: -- nightly -before_script: -- export PATH=$HOME/.local/bin:$PATH -- rustup target add i686-unknown-linux-gnu -- rustup target add i686-pc-windows-gnu -- rustup target add i686-pc-windows-msvc -- rustup component add rust-src -- cargo install xargo -- export RUST_SYSROOT=$HOME/rust -script: -- | - # get ourselves a MIR-ful libstd - xargo/build.sh -- | - # Test plain miri - cargo build && - cargo test && - cargo install -- | - # Test cargo miri - cd cargo-miri-test && - cargo miri && - cargo miri test && - cd .. -- | - # and run all tests with full mir - MIRI_SYSROOT=~/.xargo/HOST cargo test -notifications: - email: - on_success: never -env: - global: - - RUST_TEST_NOCAPTURE=1 - - TRAVIS_CARGO_NIGHTLY_FEATURE="" diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index f7e9fe39565dc..0000000000000 --- a/Cargo.lock +++ /dev/null @@ -1,256 +0,0 @@ -[root] -name = "miri" -version = "0.1.0" -dependencies = [ - "byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)", - "cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aho-corasick" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.0.0" -source = "git+https://github.com/BurntSushi/byteorder#f8e7685b3a81c52f5448fd77fb4e0535bc92f880" - -[[package]] -name = "cargo_metadata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "compiletest_rs" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dtoa" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "env_logger" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "itoa" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log_settings" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "regex" -version = "0.1.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde_derive" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_json" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread-id" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "utf8-ranges" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum byteorder 1.0.0 (git+https://github.com/BurntSushi/byteorder)" = "" -"checksum cargo_metadata 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d84cb53c78e573aa126a4b9f963fdb2629f8183b26e235da08bb36dc7381162" -"checksum compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "617b23d0ed4f57b3bcff6b5fe0a78f0010f1efb636298317665a960b6dbc0533" -"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" -"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" -"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" -"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" -"checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6" -"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" -"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" -"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3" -"checksum serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c06b68790963518008b8ae0152d48be4bbbe77015d2c717f6282eea1824be9a" -"checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" -"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b" -"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" -"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index a32595fa70bc1..0000000000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2016 The Miri Developers - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 1f9d89a5862b5..0000000000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 The Miri Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/benches/fibonacci.rs b/benches/fibonacci.rs deleted file mode 100644 index 39974ad6c18ea..0000000000000 --- a/benches/fibonacci.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![feature(test, rustc_private)] - -extern crate test; -use test::Bencher; -mod helpers; -use helpers::*; - -#[bench] -fn fib(bencher: &mut Bencher) { - bencher.iter(|| { - fibonacci_helper::main(); - }) -} - -#[bench] -fn fib_miri(bencher: &mut Bencher) { - miri_helper::run("fibonacci_helper", bencher); -} - -#[bench] -fn fib_iter(bencher: &mut Bencher) { - bencher.iter(|| { - fibonacci_helper_iterative::main(); - }) -} - -#[bench] -fn fib_iter_miri(bencher: &mut Bencher) { - miri_helper::run("fibonacci_helper_iterative", bencher); -} diff --git a/benches/helpers/fibonacci_helper.rs b/benches/helpers/fibonacci_helper.rs deleted file mode 100644 index 004000e70ea73..0000000000000 --- a/benches/helpers/fibonacci_helper.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[inline(never)] -pub fn main() { - assert_eq!(fib(10), 55); -} - -fn fib(n: usize) -> usize { - if n <= 2 { - 1 - } else { - fib(n - 1) + fib(n - 2) - } -} diff --git a/benches/helpers/fibonacci_helper_iterative.rs b/benches/helpers/fibonacci_helper_iterative.rs deleted file mode 100644 index 59283be4820f7..0000000000000 --- a/benches/helpers/fibonacci_helper_iterative.rs +++ /dev/null @@ -1,15 +0,0 @@ -#[inline(never)] -pub fn main() { - assert_eq!(fib(10), 55); -} - -fn fib(n: usize) -> usize { - let mut a = 0; - let mut b = 1; - for _ in 0..n { - let c = a; - a = b; - b = c + b; - } - a -} diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs deleted file mode 100644 index 441c35f5513e0..0000000000000 --- a/benches/helpers/miri_helper.rs +++ /dev/null @@ -1,68 +0,0 @@ -extern crate getopts; -extern crate miri; -extern crate rustc; -extern crate rustc_driver; -extern crate test; - -use self::miri::eval_main; -use self::rustc::session::Session; -use self::rustc_driver::{driver, CompilerCalls, Compilation}; -use std::cell::RefCell; -use std::rc::Rc; -use test::Bencher; - -pub struct MiriCompilerCalls<'a>(Rc>); - -fn find_sysroot() -> String { - // Taken from https://github.com/Manishearth/rust-clippy/pull/911. - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => option_env!("RUST_SYSROOT") - .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") - .to_owned(), - } -} - -pub fn run(filename: &str, bencher: &mut Bencher) { - let args = &[ - "miri".to_string(), - format!("benches/helpers/{}.rs", filename), - "--sysroot".to_string(), - find_sysroot() - ]; - let compiler_calls = &mut MiriCompilerCalls(Rc::new(RefCell::new(bencher))); - rustc_driver::run_compiler(args, compiler_calls, None, None); -} - -impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> { - fn build_controller( - &mut self, - _: &Session, - _: &getopts::Matches - ) -> driver::CompileController<'a> { - let mut control: driver::CompileController<'a> = driver::CompileController::basic(); - - let bencher = self.0.clone(); - - control.after_analysis.stop = Compilation::Stop; - control.after_analysis.callback = Box::new(move |state| { - state.session.abort_if_errors(); - - let tcx = state.tcx.unwrap(); - let (entry_node_id, _) = state.session.entry_fn.borrow() - .expect("no main or start function found"); - let entry_def_id = tcx.map.local_def_id(entry_node_id); - - let memory_size = 100*1024*1024; // 100MB - let step_limit = 1000_000; - let stack_limit = 100; - bencher.borrow_mut().iter(|| { eval_main(tcx, entry_def_id, memory_size, step_limit, stack_limit); }); - - state.session.abort_if_errors(); - }); - - control - } -} diff --git a/benches/helpers/mod.rs b/benches/helpers/mod.rs deleted file mode 100644 index 27504a2cc034d..0000000000000 --- a/benches/helpers/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// This module gets included in multiple crates, and they each only use part of it. -#![allow(dead_code)] - -pub mod fibonacci_helper; -pub mod fibonacci_helper_iterative; -pub mod miri_helper; -pub mod smoke_helper; diff --git a/benches/helpers/repeat.rs b/benches/helpers/repeat.rs deleted file mode 100644 index 0e8c5980b82bd..0000000000000 --- a/benches/helpers/repeat.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let data: [u8; 1024] = [42; 1024]; - assert_eq!(data.len(), 1024); -} diff --git a/benches/helpers/repeat_manual.rs b/benches/helpers/repeat_manual.rs deleted file mode 100644 index 6ef6f724efcee..0000000000000 --- a/benches/helpers/repeat_manual.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let mut data: [u8; 1024] = unsafe { std::mem::uninitialized() }; - for i in 0..data.len() { - unsafe { std::ptr::write(&mut data[i], 0); } - } - assert_eq!(data.len(), 1024); -} diff --git a/benches/helpers/smoke_helper.rs b/benches/helpers/smoke_helper.rs deleted file mode 100644 index ef05b044cddde..0000000000000 --- a/benches/helpers/smoke_helper.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[inline(never)] -pub fn main() { -} diff --git a/benches/repeat.rs b/benches/repeat.rs deleted file mode 100644 index f5920e83d9b07..0000000000000 --- a/benches/repeat.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![feature(test, rustc_private)] - -extern crate test; -use test::Bencher; -mod helpers; -use helpers::*; - -#[bench] -fn repeat(bencher: &mut Bencher) { - miri_helper::run("repeat", bencher); -} - -#[bench] -fn repeat_manual(bencher: &mut Bencher) { - miri_helper::run("repeat_manual", bencher); -} diff --git a/benches/smoke.rs b/benches/smoke.rs deleted file mode 100644 index eabd58a86889f..0000000000000 --- a/benches/smoke.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(test, rustc_private)] - -extern crate test; -use test::Bencher; -mod helpers; -use helpers::*; - -#[bench] -fn noop(bencher: &mut Bencher) { - bencher.iter(|| { - smoke_helper::main(); - }) -} - -/* -// really slow -#[bench] -fn noop_miri_full(bencher: &mut Bencher) { - let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set"); - bencher.iter(|| { - let mut process = std::process::Command::new("target/release/miri"); - process.arg("benches/smoke_helper.rs") - .arg("--sysroot").arg(&path); - let output = process.output().unwrap(); - if !output.status.success() { - println!("{}", String::from_utf8(output.stdout).unwrap()); - println!("{}", String::from_utf8(output.stderr).unwrap()); - panic!("failed to run miri"); - } - }) -} -*/ - -#[bench] -fn noop_miri_interpreter(bencher: &mut Bencher) { - miri_helper::run("smoke_helper", bencher); -} diff --git a/cargo-miri-test/Cargo.lock b/cargo-miri-test/Cargo.lock deleted file mode 100644 index 8b2387fa64109..0000000000000 --- a/cargo-miri-test/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -[root] -name = "cargo-miri-test" -version = "0.1.0" -dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" diff --git a/cargo-miri-test/Cargo.toml b/cargo-miri-test/Cargo.toml deleted file mode 100644 index 5fbe923f23d3b..0000000000000 --- a/cargo-miri-test/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "cargo-miri-test" -version = "0.1.0" -authors = ["Oliver Schneider "] - -[dependencies] -byteorder = "1.0" \ No newline at end of file diff --git a/cargo-miri-test/src/main.rs b/cargo-miri-test/src/main.rs deleted file mode 100644 index 07b0e4cee4e5c..0000000000000 --- a/cargo-miri-test/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -extern crate byteorder; - -use byteorder::{BigEndian, ByteOrder}; - -fn main() { - let buf = &[1,2,3,4]; - let n = ::read_u32(buf); - assert_eq!(n, 0x01020304); -} diff --git a/cargo-miri-test/tests/foo.rs b/cargo-miri-test/tests/foo.rs deleted file mode 100644 index fb7fad21c9db8..0000000000000 --- a/cargo-miri-test/tests/foo.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn bar() { - assert_eq!(4, 4); -} diff --git a/src/bin/cargo-miri.rs b/src/bin/cargo-miri.rs deleted file mode 100644 index 6eff6650fa9c4..0000000000000 --- a/src/bin/cargo-miri.rs +++ /dev/null @@ -1,180 +0,0 @@ -extern crate cargo_metadata; - -use std::path::{PathBuf, Path}; -use std::io::Write; -use std::process::Command; - - -const CARGO_MIRI_HELP: &str = r#"Interprets bin crates - -Usage: - cargo miri [options] [--] [...] - -Common options: - -h, --help Print this message - --features Features to compile for the package - -V, --version Print version info and exit - -Other options are the same as `cargo rustc`. - -The feature `cargo-miri` is automatically defined for convenience. You can use -it to configure the resource limits - - #![cfg_attr(feature = "cargo-miri", memory_size = 42)] - -available resource limits are `memory_size`, `step_limit`, `stack_limit` -"#; - -fn show_help() { - println!("{}", CARGO_MIRI_HELP); -} - -fn show_version() { - println!("{}", env!("CARGO_PKG_VERSION")); -} - -fn main() { - // Check for version and help flags even when invoked as 'cargo-miri' - if std::env::args().any(|a| a == "--help" || a == "-h") { - show_help(); - return; - } - if std::env::args().any(|a| a == "--version" || a == "-V") { - show_version(); - return; - } - - if let Some("miri") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) { - // this arm is when `cargo miri` is called - - let test = std::env::args().nth(2).map_or(false, |text| text == "test"); - let skip = if test { 3 } else { 2 }; - - let manifest_path_arg = std::env::args().skip(skip).find(|val| val.starts_with("--manifest-path=")); - - let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) { - metadata - } else { - let _ = std::io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata.")); - std::process::exit(101); - }; - - let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..]))); - - let current_dir = std::env::current_dir(); - - let package_index = metadata.packages - .iter() - .position(|package| { - let package_manifest_path = Path::new(&package.manifest_path); - if let Some(ref manifest_path) = manifest_path { - package_manifest_path == manifest_path - } else { - let current_dir = current_dir.as_ref().expect("could not read current directory"); - let package_manifest_directory = package_manifest_path.parent() - .expect("could not find parent directory of package manifest"); - package_manifest_directory == current_dir - } - }) - .expect("could not find matching package"); - let package = metadata.packages.remove(package_index); - for target in package.targets { - let args = std::env::args().skip(skip); - let kind = target.kind.get(0).expect("badly formatted cargo metadata: target::kind is an empty array"); - if test && kind == "test" { - if let Err(code) = process(vec!["--test".to_string(), target.name].into_iter().chain(args)) { - std::process::exit(code); - } - } else if !test && kind == "bin" { - if let Err(code) = process(vec!["--bin".to_string(), target.name].into_iter().chain(args)) { - std::process::exit(code); - } - } - } - } else { - // this arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC` env var set to itself - - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) { - format!("{}/toolchains/{}", home, toolchain) - } else { - option_env!("RUST_SYSROOT") - .map(|s| s.to_owned()) - .or_else(|| { - Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output() - .ok() - .and_then(|out| String::from_utf8(out.stdout).ok()) - .map(|s| s.trim().to_owned()) - }) - .expect("need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust") - }; - - // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly - // without having to pass --sysroot or anything - let mut args: Vec = if std::env::args().any(|s| s == "--sysroot") { - std::env::args().skip(1).collect() - } else { - std::env::args().skip(1).chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect() - }; - - // this check ensures that dependencies are built but not interpreted and the final crate is - // interpreted but not built - let miri_enabled = std::env::args().any(|s| s == "-Zno-trans"); - - let mut command = if miri_enabled { - let mut path = std::env::current_exe().expect("current executable path invalid"); - path.set_file_name("miri"); - Command::new(path) - } else { - Command::new("rustc") - }; - - args.extend_from_slice(&["-Z".to_owned(), "always-encode-mir".to_owned()]); - args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-miri""#.to_owned()]); - - match command.args(&args).status() { - Ok(exit) => if !exit.success() { - std::process::exit(exit.code().unwrap_or(42)); - }, - Err(ref e) if miri_enabled => panic!("error during miri run: {:?}", e), - Err(ref e) => panic!("error during rustc call: {:?}", e), - } - } -} - -fn process(old_args: I) -> Result<(), i32> - where I: Iterator -{ - let mut args = vec!["rustc".to_owned()]; - - let mut found_dashes = false; - for arg in old_args { - found_dashes |= arg == "--"; - args.push(arg); - } - if !found_dashes { - args.push("--".to_owned()); - } - args.push("-Zno-trans".to_owned()); - args.push("--cfg".to_owned()); - args.push(r#"feature="cargo-miri""#.to_owned()); - - let path = std::env::current_exe().expect("current executable path invalid"); - let exit_status = std::process::Command::new("cargo") - .args(&args) - .env("RUSTC", path) - .spawn() - .expect("could not run cargo") - .wait() - .expect("failed to wait for cargo?"); - - if exit_status.success() { - Ok(()) - } else { - Err(exit_status.code().unwrap_or(-1)) - } -} diff --git a/src/bin/miri.rs b/src/bin/miri.rs deleted file mode 100644 index 8d27f9057f540..0000000000000 --- a/src/bin/miri.rs +++ /dev/null @@ -1,211 +0,0 @@ -#![feature(rustc_private, i128_type)] - -extern crate getopts; -extern crate miri; -extern crate rustc; -extern crate rustc_driver; -extern crate rustc_errors; -extern crate env_logger; -extern crate log_settings; -extern crate syntax; -extern crate log; - -use rustc::session::Session; -use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls}; -use rustc_driver::driver::{CompileState, CompileController}; -use rustc::session::config::{self, Input, ErrorOutputType}; -use rustc::hir::{self, itemlikevisit}; -use rustc::ty::TyCtxt; -use syntax::ast::{MetaItemKind, NestedMetaItemKind, self}; -use std::path::PathBuf; - -struct MiriCompilerCalls(RustcDefaultCalls); - -impl<'a> CompilerCalls<'a> for MiriCompilerCalls { - fn early_callback( - &mut self, - matches: &getopts::Matches, - sopts: &config::Options, - cfg: &ast::CrateConfig, - descriptions: &rustc_errors::registry::Registry, - output: ErrorOutputType - ) -> Compilation { - self.0.early_callback(matches, sopts, cfg, descriptions, output) - } - fn no_input( - &mut self, - matches: &getopts::Matches, - sopts: &config::Options, - cfg: &ast::CrateConfig, - odir: &Option, - ofile: &Option, - descriptions: &rustc_errors::registry::Registry - ) -> Option<(Input, Option)> { - self.0.no_input(matches, sopts, cfg, odir, ofile, descriptions) - } - fn late_callback( - &mut self, - matches: &getopts::Matches, - sess: &Session, - input: &Input, - odir: &Option, - ofile: &Option - ) -> Compilation { - self.0.late_callback(matches, sess, input, odir, ofile) - } - fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> CompileController<'a> { - let mut control = self.0.build_controller(sess, matches); - control.after_hir_lowering.callback = Box::new(after_hir_lowering); - control.after_analysis.callback = Box::new(after_analysis); - if std::env::var("MIRI_HOST_TARGET") != Ok("yes".to_owned()) { - // only fully compile targets on the host - control.after_analysis.stop = Compilation::Stop; - } - control - } -} - -fn after_hir_lowering(state: &mut CompileState) { - let attr = (String::from("miri"), syntax::feature_gate::AttributeType::Whitelisted); - state.session.plugin_attributes.borrow_mut().push(attr); -} - -fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) { - state.session.abort_if_errors(); - - let tcx = state.tcx.unwrap(); - let limits = resource_limits_from_attributes(state); - - if std::env::args().any(|arg| arg == "--test") { - struct Visitor<'a, 'tcx: 'a>(miri::ResourceLimits, TyCtxt<'a, 'tcx, 'tcx>, &'a CompileState<'a, 'tcx>); - impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 'tcx> { - fn visit_item(&mut self, i: &'hir hir::Item) { - if let hir::Item_::ItemFn(_, _, _, _, _, body_id) = i.node { - if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) { - let did = self.1.hir.body_owner_def_id(body_id); - println!("running test: {}", self.1.hir.def_path(did).to_string(self.1)); - miri::eval_main(self.1, did, None, self.0); - self.2.session.abort_if_errors(); - } - } - } - fn visit_trait_item(&mut self, _trait_item: &'hir hir::TraitItem) {} - fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {} - } - state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state)); - } else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() { - let entry_def_id = tcx.hir.local_def_id(entry_node_id); - let start_wrapper = tcx.lang_items.start_fn().and_then(|start_fn| - if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None }); - miri::eval_main(tcx, entry_def_id, start_wrapper, limits); - - state.session.abort_if_errors(); - } else { - println!("no main function found, assuming auxiliary build"); - } -} - -fn resource_limits_from_attributes(state: &CompileState) -> miri::ResourceLimits { - let mut limits = miri::ResourceLimits::default(); - let krate = state.hir_crate.as_ref().unwrap(); - let err_msg = "miri attributes need to be in the form `miri(key = value)`"; - let extract_int = |lit: &syntax::ast::Lit| -> u128 { - match lit.node { - syntax::ast::LitKind::Int(i, _) => i, - _ => state.session.span_fatal(lit.span, "expected an integer literal"), - } - }; - - for attr in krate.attrs.iter().filter(|a| a.name().map_or(false, |n| n == "miri")) { - if let Some(items) = attr.meta_item_list() { - for item in items { - if let NestedMetaItemKind::MetaItem(ref inner) = item.node { - if let MetaItemKind::NameValue(ref value) = inner.node { - match &inner.name().as_str()[..] { - "memory_size" => limits.memory_size = extract_int(value) as u64, - "step_limit" => limits.step_limit = extract_int(value) as u64, - "stack_limit" => limits.stack_limit = extract_int(value) as usize, - _ => state.session.span_err(item.span, "unknown miri attribute"), - } - } else { - state.session.span_err(inner.span, err_msg); - } - } else { - state.session.span_err(item.span, err_msg); - } - } - } else { - state.session.span_err(attr.span, err_msg); - } - } - limits -} - -fn init_logger() { - let format = |record: &log::LogRecord| { - if record.level() == log::LogLevel::Trace { - // prepend spaces to indent the final string - let indentation = log_settings::settings().indentation; - format!( - "{lvl}:{module}:{indent: String { - if let Ok(sysroot) = std::env::var("MIRI_SYSROOT") { - return sysroot; - } - - // Taken from https://github.com/Manishearth/rust-clippy/pull/911. - let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); - let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); - match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => option_env!("RUST_SYSROOT") - .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") - .to_owned(), - } -} - -fn main() { - init_logger(); - let mut args: Vec = std::env::args().collect(); - - let sysroot_flag = String::from("--sysroot"); - if !args.contains(&sysroot_flag) { - args.push(sysroot_flag); - args.push(find_sysroot()); - } - // we run the optimization passes inside miri - // if we ran them twice we'd get funny failures due to borrowck ElaborateDrops only working on - // unoptimized MIR - // FIXME: add an after-mir-passes hook to rustc driver - args.push("-Zmir-opt-level=0".to_owned()); - // for auxilary builds in unit tests - args.push("-Zalways-encode-mir".to_owned()); - - rustc_driver::run_compiler(&args, &mut MiriCompilerCalls(RustcDefaultCalls), None, None); -} diff --git a/README.md b/src/librustc_mir/miri/README.md similarity index 100% rename from README.md rename to src/librustc_mir/miri/README.md diff --git a/src/cast.rs b/src/librustc_mir/miri/cast.rs similarity index 100% rename from src/cast.rs rename to src/librustc_mir/miri/cast.rs diff --git a/src/const_eval.rs b/src/librustc_mir/miri/const_eval.rs similarity index 100% rename from src/const_eval.rs rename to src/librustc_mir/miri/const_eval.rs diff --git a/src/error.rs b/src/librustc_mir/miri/error.rs similarity index 100% rename from src/error.rs rename to src/librustc_mir/miri/error.rs diff --git a/src/eval_context.rs b/src/librustc_mir/miri/eval_context.rs similarity index 100% rename from src/eval_context.rs rename to src/librustc_mir/miri/eval_context.rs diff --git a/src/lib.rs b/src/librustc_mir/miri/lib.rs similarity index 100% rename from src/lib.rs rename to src/librustc_mir/miri/lib.rs diff --git a/src/lvalue.rs b/src/librustc_mir/miri/lvalue.rs similarity index 100% rename from src/lvalue.rs rename to src/librustc_mir/miri/lvalue.rs diff --git a/src/memory.rs b/src/librustc_mir/miri/memory.rs similarity index 100% rename from src/memory.rs rename to src/librustc_mir/miri/memory.rs diff --git a/src/operator.rs b/src/librustc_mir/miri/operator.rs similarity index 100% rename from src/operator.rs rename to src/librustc_mir/miri/operator.rs diff --git a/src/step.rs b/src/librustc_mir/miri/step.rs similarity index 100% rename from src/step.rs rename to src/librustc_mir/miri/step.rs diff --git a/src/terminator/drop.rs b/src/librustc_mir/miri/terminator/drop.rs similarity index 100% rename from src/terminator/drop.rs rename to src/librustc_mir/miri/terminator/drop.rs diff --git a/src/terminator/intrinsic.rs b/src/librustc_mir/miri/terminator/intrinsic.rs similarity index 100% rename from src/terminator/intrinsic.rs rename to src/librustc_mir/miri/terminator/intrinsic.rs diff --git a/src/terminator/mod.rs b/src/librustc_mir/miri/terminator/mod.rs similarity index 100% rename from src/terminator/mod.rs rename to src/librustc_mir/miri/terminator/mod.rs diff --git a/src/traits.rs b/src/librustc_mir/miri/traits.rs similarity index 100% rename from src/traits.rs rename to src/librustc_mir/miri/traits.rs diff --git a/src/value.rs b/src/librustc_mir/miri/value.rs similarity index 100% rename from src/value.rs rename to src/librustc_mir/miri/value.rs diff --git a/tex/final-presentation/latexmkrc b/tex/final-presentation/latexmkrc deleted file mode 100644 index 23aa1a481b3eb..0000000000000 --- a/tex/final-presentation/latexmkrc +++ /dev/null @@ -1,12 +0,0 @@ -# vim: ft=perl - -$pdf_mode = 1; -$pdflatex = 'lualatex --shell-escape %O %S'; -$out_dir = 'out'; - -# This improves latexmk's detection of source files and generated files. -$recorder = 1; - -# Ignore always-regenerated *.pyg files from the minted package when considering -# whether to run pdflatex again. -$hash_calc_ignore_pattern{'pyg'} = '.*'; diff --git a/tex/final-presentation/rust-logo-512x512.png b/tex/final-presentation/rust-logo-512x512.png deleted file mode 100644 index 38484c670e01f3f355672d6c95f79f035a963a44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96029 zcmZs?XIxX!(mkAn0HKGD)X+hYqBLm<9YFy_rFRq*6cD77gr)(MA}AM|8MW7b3UBkIWu$C%&awg?>GxHBPM!YdH?{xbi-Kx4gdhUxCH?q zG#8h{;6LX8VBqo%eVx0(Q=2X0_w6QYsYA6R<<&ATM?>BqoV}wf9YjpP)ALz<-8Cgy zRM%-;Diocg{Yx*NHg6gvgS8_I2m_M40l6z%ZZEzxyn83|2KtA;?1%;e1brSQtcng$ z&V<+{Ze}RBw8zl>|K9M&oCXqtS)plCDk#?fJ+X*jN4c?7P%F^ZMnWvy$tM4MK_@VN z2P-)1D~yyh<_8Zr_5bgUYJk0!qYgg&Ij=Q62%y$_OB{4yb?txOJ61j$KZ5MTbC{7C z*sT8@r1JRbuY8CfmB#gzy`qY<$a$#AyK#`UYr@ofR$Hiq9DxZ*Zs7`0W+UUZ0rJ+ut!y zB{DRk+Ws5JP04-k8V`*HOT(>-@i--lm?VN~J3VN>(@pyPU_4(#(WYe^G+#Ld!2{2m zmx8usVz5mQk4jPumO%fDR2J(x3*T_QrM5}VOSh|D+)WXp*r#24LWex;Dd$1ygB!lm zew*Qg`I*A%glm1o=NXQG{~2t9UQT$fkyU!g)0AbnY5^L8bBlt`dG|#?tuv)8$d*xD z>l{Lo@|G9oym0U{l)f345;M$3Jc-zj4!M}flPU8oJoa3N)>WbMhpPaV; z#m^ErRNgz2b0+yB0gyNCZ;~feE+l;(ytJx<({_aSY3l&OTdlkH)=aKE)e!OBf)E0V zK14Lm2X(zHc{xctv$FJ;H-xX1>{=5X)+RJigl-sxo=%O|A8?B0PMGjvu)*=wP=X{L|4DSs9YAG&BVDS0RiAwBjQ^*Dy~Cfku?4w~YJ!>X~z3tO!c? z%QaO#9y~f1Yra0sw7bwiOeA9?TuU-D^Y zg0lB46ZdX|Kl1qN_#pn<68RUQS<6n^*YKn6ytjeaMiRxuVV+den0?|a2od@w@IdGr zLwK{_&3UURHrk^*{9>Fm8~@&W|2K<&a{~BoR7bRO&npmK>?6`vlcYa+L}dQwTKp}vbSae=>|}hUWJ@ibCN$i;rAd>fJdC7q8tzvsNI3uU z0hifdqX7tGI9b?wD>dg(R=t};ekumF=$>Dkr_qUvu5Wkl(M-MdADHPkf}AB^Y?hBz z>{vbU7f}A`LXT$G>Rn||X(_Elfo&oHk0SK9h)dVyX>GQJLS-7hnBCX7dRU4<*T|U* z;UMMD1JWz+VE}xgL~m+qt&IoeFt+=)Z zl8^m{oP216c0~;hZ4}k7jt|d-CT%0dsqy3-|T+TrCj4?)PRdMj){~&z_ zy-DDhL|M6`8f*i8kT-Dcl|j`KDH>D9n*X+5}AO9}5$ ziSLc!lEgOxXup_z>#g02ztyc$W>!^i<)Fbv(so}4o1DK?GoBp869Run^iP;%YzuWg z3!?5ZDGadcY@Aw?jw+m3)CDI-x0m<>kJrOF%ITzRN*xL-!R;S7X!w1Z=B$6|WJ@q6 z8oT)Z*#!RwRB=(FQyVV{h+00}*G`u8W{OwR_$spLE@??d{V!G|TC$1~9yvc=6{^(P z_IvyXxvaDSs}pFhe548^s}}TMkBM-UfK@+lFS+(sqqq1P7f|No8z%G>(^BIhf~fr6Lrdq2^hfRIQx?(`Wc?G5q871y3VW*&YfRVJ^xjoUUx8k`t^maFOYc?_S6?`Wv`CjcGRTc z=OZtF0jB5AHSj0S1b%8v>uEJ_g2v9sW>`5aT~Df#EwWwT?i!g3i5Z|GaY!!O>i zy#oYbT5l^w59WZ63a)@0Yu|#eU#~N${JaoiC2qHQ{H)pYWJ230I0Z1M_ka#wE}So+ zp}Af5;8uNe%t1v!ch9?DQ+scQ^me^KALI4ns7zr}F22H-8Drcm7+fr>B{hJET_CSf zfQlrtQq=Vup74BW1YEK{EWYWhE(g_{6{A}OZ>l>Jpxf+%; zobF$R8a4I(GkF`1qOL-HnclR0*ZNxyc5Ood8f+`#Tv`3h4*Fa=Y}j@YCw$CbCm(RX zDpn2ynAy2L`!&ByAOUvlaoT7=(k|PJBv6lp%naI@s<_7J_jYjI4h{ZB^$o{>0r9zZ zM#3*M&%^-1ksLH3cskg%@OL6A18@J+4`|g_-LE@KHrnj?Uame=N)Q?tcLPuQU@dDx z9m~1hu`h0>^^%prfCopHGo8MKh9nK%_d$ic0BmHhuQ3xG1}C1_gn9iKOL+H3_Bk{l z#@N;c2*w&Om*t;9?Qqi`3-l`Z+duFH)2CZ>3PP7xPI7(ZO@pzqV|R{_earDZHI-8O z(Hu~5elZK7jX&T6)+-(Jcp;xTG{6< z@q6^gjG`MX zqIO0v%Gra!om%GL*#Kj}&cR^p?sZaNoH4sL-m&fBUPxjEy1tyjzLmBjda#RxonI+8 zn(7w-eM(DUWRpy(NH>6iLFbiiyX3;qzXzfe6Liq`l@Wd#N+yY~Hwz z-mELtMiLY@uu#o5i;cT^O|Zy0w_MG0kuwjNYSkjU&HDWpS0K+BR)Hc1SH7?|llGd| zR(_8E?8+>+LoO}_%kbW}Lq3$xL@wezzSX>n#$;15<4O&7p$u09H~bH^XCJ0uLEAnN z5$h8}P?9-|}DVXjH7 z)+K}^6Q+FDmO<_kWVrgy;D-nC-Obus)spQf;9~GM?{N1S&64pvcCGRb)u!>uqHn+A z7~M;Z9bL4kWmQN6j>?>-#7?8VcO8PjW^DX4$9=TN)#?>7dElMIPY#es8#FelhchLw z$K;4~eap)dh;by$RXUX;$Gq*XQzpUFZf|_q>Gr6j)AhJ%=>1}J?q4|KSh$9IUKzNe zY{()ERvUY?so<|z@#z1#0CO!NMNdfg2icNM0K3;V|BkiDhcD&K)EX81G3Ys{9WKg! z`%ljL46YxDGJZAOwvI|vCyTruVJ%E`9Nf%yH$U>$${aV=ZohGVV%NQ{r*c?lf^>>hEFceuOi}wwEbfUG3g8baJty1e|(PdrB1s3fm zh*TPGPh!Oj9mWc4MPD%i#fm5Y&o$=}|t19Var3Yv7I_K-HVqi~m z90RX(Our6Og8ZI8UDkZ+r?gu?cD9bs>f3=>)rg+~nlWyQ?A&{?ZL)px{``)y4$r6a z(_!@iW7ujjBdF6pUG(GH0V9?R3|{7@QnqJoHoHty=rLa}PBZZKKNO7iHo!Y!k}@$2 zw{0RYT_SUbV3-B+4s4t{qfhV-_ot6NM{=wis}mE$r@r-^%m=-D+PeM?4r_B}kCbxS>^9I(m zT9UfWnVTujnVb5pp9{%h2@+!y%bfTs2$YxZoUO`jn@WSc$$NS!E>hf-@ve=lbC)!Y z-1RO%4DQGcN_DiU6@%uW(($kPZ(U&r+~fwJ9N%(bXR0Z&X8lcaK^r5DvvVeu5iFBr zzVK3GrzcbSe>KXf_l)Y+V@(1rI?S7f8T7FNK8B93hou?tkMMT2T2Bl9U03~-YX-aq zGk@zhxInPE)Bws=$h#6Dc+4-v&ZV)u<-TMJU0}at{vyJ!5}cBcV)AkA)CVq5Q?dZ0 zZzHe`_l&5CjJ%!M`|pXZr&^NJd#AdsV!O5(CcqhSh`2wKYyqsS>h|Y;K3wqPSa@BA z`aHsIHA;e}9;U{}36^4q==dhiLc53U}n&QlM+I6Z2evk`pW~D~ZvPLv8T$c4^z}`Zq zN=k;J>>;1hH6WgDu-BJOG->x_2Hu1XflFGy*f&#uU2-n2iDGYh&es4vJR*icZoaX= zEs0hpa|mlJ#zhCjv=J|Bb0 z{itvQ{F81-vYYL%=cgm&%ZbaU{OxE3ylnMzku7E{fR3SR+ph0)C2Ky{zaiU%YMHsP z=N-=)Lkwe#&hKmhl-|IX3E5i}Nj+zj_m!!!b5VpklgDBk5Q7${38nkMqvpd+Rpo)Z z`5&FUI1e_4@6#zGAM9bV@ej4#2|mK0xxz;6%?Q6|Ik1SDVIdr)nsPO9l6@y3HE zpUahZ4ERp^uH4CFx!37_7j`a^$8rkXoVXgU{q=qye(Hyyje1Hg!$J>~5#w81l%^+n zBK_#+!Het9n3M`%y;u1w%`gW#5+4!-+7dR*fD3M6A%OFM8E%!#OQ2`Gq-xI0&iQVAdbngm)!}# zjS2gbZ{=GP{~zdtZW2iznruX$gQ$!;8p!J52MK^mE+pyXyK0O?}n^;szP)>oN9heFA!S72$rquTb z0rKopiU{4jssBSo=gYaiC&)%YOuXUATvl0Hr%>4jcFgQoLeZLKx_b6wBUq-bQ^|(s zoaM%Y6(u`-@oZhX-8*d|tiPS7UguWte)hrEwa2B*6<2SS{YY$Fbn6wkRCVuVy1)Vb zwbL1pez*Ut)iSiEcKhE`c9dj0<)gmt>EKaE)^|Eei!5e_iPE;LpGGX1#P02(;tmB) zRa;qfph!*rL7p8sp=b-84nA;ug8%c)5c!z)HC~n3drZ&DP1xfM+JgD(oHe`1AU10D zy{sr-4!SPHOLUq2Jd}#HB?#RK*txXZbp4`=kNyV?k&u#)=C;I*iqo}K`{y`j8{++1 z^UwHi*=dqV^$uy->wU+Lo-zsVyH>(>1U|zzRHA4X0~2Sv27X#{m3-*&?9bNj{`Y> zl@p9jJ22hOdSe$uqpJjN{hXpr`!E__9XBtm@}lP^&r>-!D$l8PNYko5XQ(r+5>eI z-WKy%{6b5B+2SH&uaUL?=XYz;kw1C<{JlwpV{VHsEp}_deyV9l_gvoo;lcIE=$Su{ zXG5mK%spkQA>L+XU!E&%Ne}dG!nfb>YEL^peQ;t~_0dQDlKVS&v}_R2-CUn?F4Eof zZLEJl?Sn%J<1DAIv3BGD{`L+PYmmmz{dAkpq;FNA>~C_59;9wFSX#}5%$2LU0HfVz+vI z;9(zv7bkGx>m4Xn=zC`!OeCh^*+pG*#nZ}?1^bSzZkIb!7nKDh z=mox8y%}UPUf@9BT03xiwp2PjTD$%8$5G>bEo{Rdl@F@ZcewP@GTfshmBS` zJ>o0K{KQ(vm2{9@i~*6iYB-*$hwwFXaBL4Zb6J zRfxJPD zSkS{>`KMLwIZN}uq*s~Rb`xdmfa-QU0qK=Fs!Z7aps~Gd1HTUV$+>yM@_HD@XMFWd z3^kM@Bnji-HI4E2)#N%1Iky#=wRvU+W7I|;vMC0{8r{w2(tbYgzk$_bZO#4J79|0a6ruQ*~ojaXs#&DIeT908jVK`sc z^l&oH*qEL1iMGlgi5OjYOglTYg$)uU4DG#iDhbB<$T7ET&3FKuG^l%==_cVvOk!-* zCeVR5u+>77-FnMN_>DE#WttiN$Ps}d107vOfUp9Cu=6+m3-Z{jwtTFm9#xk+hcD@g zoFczlUqqSNZ5&Rub}WvY4==rMRVn60 zkk_Mc<#x((sD}mS@l-59x4sT>s7WgVf6ocT(Z708EbFK6E3T5Qy}jS?bAkdoSC1LD zBq7ac7FsW}Yv(526w%!I;w6-&1Io3`QJRN^eS{$;U?P`aW zg>rZmVMRyc&m{SHzp`GLpqhblLby%yAm^5xOd1@@$5(Ri+!8!G0~VJrdAzPmz9(b_xFRm)Y_0E&siFlg($FDZQGk57oh^Y4S768l-*gO*URv3pS#6tY5zte@_EIObSLS_~^`BneEMok%y)4#O8;`@;_moBXN< zb&k_UOQtmH)*i9D@I6=+AG4uK>$2+^`Kq%&EVtjeoO4y(VXp4oG$bl#)mOOJ!TEy( z*j~XPdGe?bWFO6{s}TpnLaEDiIW(UZBlKLae}Jx0T!ZN9VK~ym&w9Ip%jNU3+!lHZ zcdc;3cQ=zl?xJq;F22+Ej>37gIWYFpMf;Y*&;ZQx`B6jQmhs+sO@4~1>Uz-Jthe(H zVaT@#jN4)#5T*&dm}-4dc5y|eV%5@&oDVk2&a)oyR-A>YEm%JJ@c1m?^HYD>H6!AWYJ+{=wuI8@Tos9GG0@jL#E<}Z zNx(dqn;I!iA7hDdA9RRPi<;jqx+(_~k=kf(FwxCZr147UgVC%{h;Ko{{3Cdk1>5o+%9g5t_%LQG$#uKYl$CE1SRM+1_L9aKX&B%36 zARlDD(Xq#h14JIN!3ksIxYilAmj~g^NOwZVXk+VIMY&XF62p6>{sThu_16zi;vmYu ztuj*IHx%wzS!*2MqeaaaXxanxZ=g&BqVy8NcNIquiXwNWV*&X10{MxvAo<2K4bAZG zoU5UI+nlRzBE$uW?A@)&h_qey>67C7LYBFMY(I>i9MyL=QV{{?k$DUvxQCj$Im@53 zuAil1GVZ)b#zUVXp7TSh1ccwbeP1gF5maS(InM$Cc?h*FJ%rp;qHArn&$VU@7!x}^ zJMKw&!!wi}S>brqmzxSPCJA&ryic9}>z+@mX3`t(PPrIBG}PGyQkn{_2MX`X&_yoU zL+aIl|4Km@X>b#;`Pq!vb7irBDpcEEqkYb)Z0Y4ebHm;0YNR66M#CwaQy6`d@-k;D zAwQshv(sn6;#G~j#TqQad=p7ZT%`+k-y5+f+XXD6x(v4ak3x^Rk><8v2q-jBl?PdD7Q1(-sk+t^yYJ+3k@Oji*CWQh)J5ic$gRfcFkA5y@3TvV*i zl7iAb<$F!36n(Xb{alkpP3YNgw@Q9NoAZxlNVno#v^mWA9DNQMvJYl41Ki?EhODO6 zM^9r4)8z#dL}}I=czAXS&2$*WxM;rV!gO{3bdlZ;LOI!cCs(qLe6IZd5u=5?S#wtr z0(@r=QK|^4v$#4OgQJZU^dD7r`J~Vb&qzBIUp1Z^RHfcSeaBD~KHwius>^}AdfzzZ zmj5H1IR&LZXuVeb%+bwpNl3}g&nVz1gLzadqLSv1*%HCQwai1819Ohnz zPdoe^`*pWczGZQUPo4M=ze3uCTX{{WV)&-i)n4roDn44v{<$vKc$nKKi~6K&+mW=@ zon(GGDMnZuh|{(;O%i#tMqzTY0~38Yfsf>7-VdOO`L23Ua=jRlt|39V(sy_ITt`Cf zW9X!HQ~C*1Gm;9ieL?Fqn^P3-DON;HuM8tHa-hE4G}|Q5w-%~Ps&f|$K}yqqH*z7pg-{*eG1()5-^$^ExgobG-7TjFqqKJI~45wUAXlBYwc#uc5V z(Cz*Z*RqJ`80Mc=GQV@zOBb)5yN*7kA|xhE&=^y&73ZqXJ0p%nmw@>mTY`AuU(159 z_r3ca+n;m8s_>j`JyZ{NY}O{aPu8lB?)2uP9j@#oTB~7I1Pa|=N(LoV;GfZY-c1UH zyqGHbIMkEVE|5n@b(wR$SPst z;~WbHpdwkvxPL3!1cGC--n@OGUWLJr^L6KW=_1|ULGuiC)Bu_P`n=lpBk9mQi$>X$ zjhc?l{=bip&s;eP0c}0ndBE-2*ma&oCj*sTdZuUk#1fYTVi}2;fye7xIyvz!Dp@rOH_PPMGhFVllnPUFPE1=$)fFot;fHl|JE^`tAcg|6t9#!y6R3vfq zt+-gx>*iU}0jD~xMPqPZ3Zd4Py66*HB_5!2yS@tq-JO?u8j>w^pEBMLJU(M%#pbe8 z!rZ%dLN8Bd3YLqJAJ@|FJAF(&`*qi3QQnUYj-TkMJF3}DBtHo_I01g+{@TE04z;03 z?5q|%)b6WTi%_BrPfORZz8*g|-5TOL++{++WXeQ*uFLLBfWRl>^zW-F9;g2o%}wS zXT*3yjwalmQ8UFAolf^XClZ+kd<%_+}38lH2M^X+YTg`O?GxA@E4t8`T+7O+n2 z!adZVzP0mkXJokXJ293@4uJ2JjLYR3((E84C~4rOF}chP=fR3Nm2X0|DcW?A_mq^F zbky{(AeHuA?0vwm!}qpG-t0&+M*GH#%#m(c=yqdj_>twwpB}dbdk^vtfylcG|8zU~ z8+qp0so!o2hSC&9TF?PQbB(8-yqO>U5~>pP&(}~FMsKB9p;b%=RHKSmWu1sm_59c*z0EeyWSx5djy+LJ>Ce;`+F&eIS|iQZgFTu>V2fHUfmkP#7?jRGtktp?hO2 zizuT`6ip|p@m1OLFz2J_O`V)Yv|xy_xzT$=qWHH?%FT^`xufQb3BqJO<_nb*p7i8t z?&GtxF9Z2NpURe3S7Q3E@0H^|5aa)8Wgp(Fg(nd7dybL+uANf__`ddDW}j>|Jddj) za?rs?H?@1jge*nV!5If5OS{+N(OUt=A)2PWZ#{WTjk|AqI7400Rg{;vI5$}iX)WUB z$NVCVppd8EaOx&BtZB5}7J@vL56;)so}-!RFMny<=s|)^ga5F-4U@feH$M??Hn)Il6H1R4?Vbv zmEDc*gh{Ova86D0AZ{L+Y!6Hl2OIO;44_uq!$t!v^7e6y4WizlzZX76naeUR#sfFJ zV~aUdSVC=P_5lr`?9e2%6Fu-R!Hvm1*vwWXNQB~z zq5WWOY&dYf)d6#SEGT9*&)NWz?6(#QqBc>Z*=w+pVp-KiSHx&X>g&Q%q>N?3pNw0h zCjK&l>>i=@(Exd-tYQ9(CF+%xdZ+>j7Zaz8Ng7Y$ib$I!HO^wkHrQ?87^j_0C*3l< z8PPE(#Ikx;(`nSM{UbXThMF2-c~g+mI(D_B+vmz!r7^bCkXXIHYE!%=|K0O5UWA!0 z^rc`7%At-OZbMN@qCnist4KWsCKJg6#|7S8l&96iO*Z6X_gv;-$(pLLeObU5%NxK@ zTMxG-%)_A51R`aH4y8tV9ZvL74Gs#;zJ(ee9 z&e0xjvYE7{u}kv+4yAH^d(u%tX*VSF6fQF2g}ZT zsBxfFitPun5B|!+L!8gShgBD=0sm+5y<5op+4zq8jRx722UgU)#N?x(dxlBNPh{S9 zqI&yI=~g$njm8?Gl+q_&xgy9Xek?oxxN9QR4`1@0XLXuu(d;z!O?Hp+zCKN=2wGJV z>>4+Sqd*N$P0pUAe$QsXCt;kj-{!Q|yFJ+)iF0~LxCIJW6l8VzJmRH%$hs!~xRc_O zCQUY#+;u2^)9K{KN^!VBBo(*HKS2UtpB`lV-O1Utlq4|Tcx291wL4G`t2}YldA?a9 zu1At$v5-dhV#{ySMLtu>q!m_p^A*Yy#0vUVg)3cUfqW>r&?eMu<^Qt^QRXyBx}Tkk zX&7bKBd%LJ@~&mptP_5{!p}{be>Nwao^@-lU!__2`FI%%y}Y!HP2AHpAX2nes1$O7 z8{e@1$1Ik@$|SSdk#z$7nHcHt!{MF&?P~o)YH~k7YMBdO)4Otgoe)F) zXF4}lW@y>SD?k{8sM04Ecfdv3ZeYJCi7#?5J3Y1QGU3LA%-zJyMUCA#R^N1?td}i^ zopJ7=^le-hIn>XRNYS$?o$!g}N({=D6+U=;O_DVG@T`axxj&%2!SuLtf)6-!8?l^A zwUEs8(K!fA#Rhr>MXK( z`G3XDBFblvA?~O}e)($dO4-X`8ngTGI)ctN>z|R3Y^SkPQ9m!@Wozj-J( z-}zk<SRf2aL~MseMN8ChVo3oxzP`_kE13hGKJ5Uo;DV00DmW;f!%>s05d2hICk;|(4h zpNzqszjgE8zR85zekDfGVxy0|{F4hG$#_D@8H~PA{cJGGt?W~7 zrNy9<1DW4-iT}P#I`#ciUL03F>FQvZIH9b^u~AUTXIMiz1S>`K&pEBjNh%4>9@7M< zR1oD(TuwT)9Ne$gIbBPR_;3+!zsQ~bYQTh)J22UQIGM>0THue$P-O@mmbH%D(@Cna zNj|5YvoG@yf&sLRg?B+DAba-cjal?*6wH}&_VKb6>wEyXI|d-7^1pI~AZK#zsZPhC zzKwRMp3?2p0_!dS3$9vd0KhWgOp682aYDaNCs0a@l%acVTM;fNl;|T>j_SSCsMAN% z(8zL@L}7CH8?qy`m0_~LeE)Xmo8R{Ze_(q>elf?jNbDwx)jeOnEw*XM-^F2z|Bia|OJt#p^SAm7 zHe`W5RptJ!PH8S{E06Iw*C$s0{8HOO;##Wr2?2;oupMJd6P$UtLwkPT9R--?!k(NP7@j$=J=+wb3@%{dX-39Ews(geq9s zUzByJ`U$(;{?ee-$JiB%; zHgmW^fV|vdtiJ3pzY1hcWD;Y1Wm{td{c26bXD`MHy# zh(FfLzY7d!%I&eg$8PUep|VS=FqDWOamg?dGHnHcc4%I8uV47~O2H%0x}@>5_BpZ` zDGEji#w_n&rvc6J4+uA`FogJV1d1}+=ctN-Jl{i~aE4>zBJo2)2$6Y}J}S#u<;~d| z!^D$uzR8XLBVw(Ck7}A68Vv<4-C@l5&-9C$qk4;z@6T*GCbtwRf*hW7;+5Pmze&8j zK^bHJc}j_zHJ2o??%`?o;5yl;&E=0gO{f4dQcK#XW~G@Aa@%82^bX3*({6Dop-5DZ)pyeZM@Ip>RfqZY!XI)hZW?; zd@b-z@lsjL3o8jKLrDpKER2vieeZ&~Hl#HM+iGWCUlWbey}Y z1T1=}+z5nk!x_?@-i)}1L{ng9EeY4!qI zd*9Go-jU*)f{wK|lX?fre0M;YlVcK$_;u)Gm-6)8ggQGxhWit3}j zyWwP1=EO?DhDmRfJ16mevY{Z7$4-8)=7~`3*-+cYa85h8K|B6VOzsZ~lNi)jW$Zz8 zxLD#9<}v-%eec=>qb%s(90Q~AE#${RWsR5}*bsYFGtcgDS6rC^fI z7Rq$BpKd@$Ky|#ae2Brx8ks-*6TN#T4M4v@w$BU9%i*NM>YVk(*f|7#vEjpO?iH`P zbUDF?^77+~7yu0`&JkaTcO)DIyOaLyiFD{n9Ak({Ps6eIP2C4uXVuz&P^Qb=7MHw~ zH~ZfLF3v*4N7T=I?EE53WIFErTYn$l`z$DA_d&X3iMhQsX~X@5T`^&n_*R-79?1;3 zBS6+CqCRfJ;~L2=`F|FY-jkQ$m3|YXR6h&_mW;gGp3Xh{RQQ@d`0fCIuDAi`RN0uA2Az zAC2rsRdlWrgtY>Bq?Wl;lmL&)_@I$|6qjmTAg_S`yOq0hOu}Rx?+oM?xgL0wOzN5g zwhfrVkge}p>H)02MTuARz@?5_ee>Xo8YjM57zp&@_08q!ef%ksq_6}+EKZjj!NBVi z<0Z}d2FdWMwV;*e%_rvKc3M_H--u@e0ov^_S;w`3RoUWwW+ftY(`EyN^<^+x-9jWWJ!cU zbh!{F)<&UwJq^fF1^?G=u(8E^;d{Cke93L_HZnZY-My$T{z^7W#a{+x z@3Y@7wIJ1tJdzOx)sDYncNR%n7K%9%5~pn3hU}L=6w)E@q#R*4IG3@=uTd?VW!>zJ z@mB(^(8OUEq00{rGV*gbcB>kWuhtIPR<%srsj3_bFQnKUd_>T9I)S}c1;}E6Q=%NJ z!q%=if~X{3fn4Ju#9Ue|&k!aDen}kMp!gA?O;noN_u8w;YtBWmP+sY@OIYj6%auBD z=x}$!dzIZU8GmJd@6hhw_Yq@F7Hs&VMu+4!PU_8l_@b=f(iHi9TqLg=uQHTMnGcsp z*r~FMv`DpbNsfojKLx--sV>_H%>dYdO3D=h3p`=|w=n1-Vw;Iecd$!T6P&$H!~7RD@?oJOsd~7Uo}T(-FL~Nz%j`WqFCuOYgey z@{(@k#W=Hc?lti(yR#%2Bu472J_(VGNIwsBdLyg7(MeW<_sX0#Q6rH;yB%i}Zz!+# zR|MTnp^Ptz-To6UF`7b_i;+V6H6@6|lwPtv<0hcii~R$b3W2wtsEe#@d*X*lPQN7; zQ==^|4p#r9i--f5Lb6zOFQCNMmnts|y8p{c4-Bq{SYw~@V2uRyf2O#0m`Usl1HPA1 zD#TF5Csqfc@Dz%j0r}#n$ys^8TR#KBtDK32XDUgrP;&Zi{+!_fa=-6?Wyb~vCuMBp ze%;J88}{1sjye>ze8Q=5H;J1I^?l5I*{tV;9*Dk!PGH!jPg>Y$0Q9I%c$#i;&TL~} z8e)5x;FT#7>$@i=O?Ym%jcH>%@UoVB>gfUEeERpCd`3bKV9>T&Z{M7Oiu!GK3N*#l z`PwL6*bXE-{t0&rV&Q{Jy$^4qrJcW@?@J|^Z#A#X3w{f+ZoQi{ehU}Dt@GH`SsTsb@kuaN4@;VYep_SKJ{08b7F3XC!cy8yv~ z8ESH~F0eJe?2SQeX>KoydKt^O=;SGH@KqjzdItuuqWXpXLY=ComV6}1lRS$_wq*B{ z9{9T6U+nmQAUSY+lGECiY zLWFw<0jg5BJl?GJOsC6p?{6l&5KcBm2}4P#1C{0LRi_SNGbZ3LVa6&sdfj@VIKUds z%-NTy+2yBzx8TQH;*oQrMNgz}>M&kxW>8Xua)R9I6JIr@fj@;Ae1@)a=prxx4<~>! z?fvR>PiFf_h;@s#8yFoo(|vKQ$sqTsa#fNl-$aj0jmkbsZyELVjVd8U*C7fJe2Hrr z3*JWo_fgD?s4-nNF<8>}YcXNUqx5^N?c3$P;*`Rpq#iUk+u&&=)&g#)OQLKKy9RPE zQ&!!j`QYNMbs~Xv=h|;!5?=tmqR3EG@t0=kjocsJD5pgF3l@TiK|23{bALmGw!@k} z!M643xPhaaH!_8oq8!J@t-B3yZkVWXD`P;`ya?Km)<~VIs9e@ej0OfUG={_|2}AEB zBJM)!8-R^a3<5l0%8Da^eigR@o>3_X`yx;J%Wq~F^X@1H?sF|kZWQL0=hOYYVo17M zji=u%GO*rY@F3b(YU{O74D8Z~+*4Z3ljakoH^fZSMO*SnVk!RVN%{{%cmY6aMZ}pV zrg`kIQMc32IEuN{!>ZFb;!el@4yCKZuxL2RgsxsbMbl}Gub|aD;l=98^|;(rcVfAXP<#2u#)h(Yph^*t!cf76JV$44L{zlP8Zz zr28c1d<@L!jc9H`db^R1w%O#*w!(lMmp?wNFhvC+ z8pY_{0_QXHN1eI!XLdE=Fx{8iuvAI?<({m1J58ckt49S9JKt3qseWlebY~yl`GXSq zohbP|8(3?LwHM>?Itn}e+IDk;&{VupfP5xccg8a1|4SN_!y(7)&h-K>W85w=xr0sr z1QXCvfF`~JOA#nk;y^ZF-G!V*eq|Po(gl|~JXsbhX#A+KproUA8En|T?&$)DI}AyH z_;qp8pohcU3X-#Ek2~Nz{tCB?PUh&k)t90zLK#h{@kCUnQu+l`&JLLGPj0H3zlXON z^iaI(-=tE+dQnj-Ctuvp!n~I$rC;veWKG85+yU9YR9O}!lX-$8r^)M_o!H1aFCNv; z>hKi!^RuMvPq_&@rpn~x@%6)Tqf@Erq?$9WXFX^h#n06MPg_{3H@yFIdEhQB%l?5J zCuqv;P~dP}8`n3|=zcVDTA<1xVK-!w#%>iKiDP8%zg4E)LfWDnPPKYd6-Cs$F z_-+V)aAfXvji|qC`Q&Y)UUUr4e2w}#ZC)aZE_C?chM?u87?geHqPf3=jMyGY2~V*|!(?|bdg#GrSBH}(r&z0Yud@Mueo@&M@((y0c_Ip1O?epgQZI$V|KKcbtN&GQ6~)-A z!GP?ec&((DVy>C9m-mt#{`!_W(%GPdm7Sch58cix}h^}8;A`R6>ZbMATG%VW7Cz;Y8S z4GiHUMLLTsJwWN?DZ$wHdFa&!iEaymHA!+cp~+kG29N-(3m$$ncy8{){9qe|8GzT* zS`?tYG{s+}>k`iyvU*E4?;%?;!0Gp85}(u>_PV6ULh-tKM+}pEx}2m0Wv!_EBFA*V z<22HTdyv8eDdqW`gFVxDy+G=}#76RoUn;rHSZy^Ap7=V{RCR{HY3Rw`U|=1N*FxTM ze%?_3+MM+2Cnp&9R`d#<_+AA64uXkoCuqjz{|Pg*=SpEK+;+2Q+bSFNH=g9iI{%dd7N&PZmp= z!2XQ#HT+q1u^r4kWqJ?HCoqH8y54qTgDwqyZ>m?_J&cHTNBZws)AE%^Bfq8_>D^c1 zd4dWBFx??#ITvD3bd%XH2}JIbsJyDIBM0SywTeH)v_P(69u(>h_Nn11AIlrmyQe9s#Kj}s8{r6_t#Bbunab^y4?tZ5EM5a`n5T)jst{B+#I9F&#>MMGd zH~92QTVY$3YHOi8U@*p)#f5!+G)DnNC`Ru9(`b|d|RNTan5&wM`w?q}1!+S)T?Wrvyb$4d2l z=b6K3FiJK7xFwa?>2c(JN@w$%hcbHG)`Ec;G!a#J8E%XPGPoiw1AqjgOP{oLHA3@n zsr*AeZ#$H=3L$}b=>if}obB@@!9>86!;@i$H&aa(T&D07IC%Uif8TEVkq6=gcFdB3 zZb3#}BGco}nX1Ql?7DdYoVOX#UWq&qXS7~=dr=olIDm? z+TQ@-)geX9s6swgye6kqYDVDg4Q_J+M+S%y(|pV#6E_O76rI z^PcL$`b#;i+I%aW*|%5l&e{s^4s13>T@{o0H-$a8Z?!Y(wzzhb{ua$EihIs2uy`fe z=tri}gbGtT7_zsdY&G=RjHbb(Y_dX|#mGfebgVtFic%O$RTc&Nq=?r#o+}A#cZ;i8Pq|X?#$-2rK)^9y(%+f&pDqT^BQE?OilHp{6 z;KL)Wq^GR3zYbGyS}QE|g1N6?{oC!$)D@6cT~k^C_I}Bp#7*!-=2_9>?pSnq94b*D z-aDf2bg+G@Iu7TI?6bx=b}k>@sYF%!B@okPt5DYe!lUyzxrpd@0qLLZlagQ}goe5V z_pQa;W!WS%6E<41ZNKT^>vhA(z*QC+QFJum@a&=bV*k?>oR2Ky#NloTWDtJ-$gHIdQBP_o_!E^}$S_K)&K zu}|(ES(_7X6ax+NP||Uzb6+e7UJp@28S3cQo8LKH44LUYUYAR_2|c$_ox#uAUE+35 zevN;a6Eay5cK55p)Bdy~x9`)Wj4u%vfQP>45IfKvdD!aE6n11RGcbiTxTQD2JC|yZ zIifwFq|JXQZT|#S-!3xjAX2yibL0L; z)8{xv@cez5Uz2SuLR1@BT5VYP$bpc&xXc#ips}*lD;nMwq9omQ=(9q@=Y}hlzP3uhxji6j% zDW9C`6ly&qxM+)>RT#4f$E5i(JkKBFxEg7Dy;I&YRW=_@wlQot|M8vpf?89;y`Kjo zbuV3k(fmc#kn=Ji-R}~Rw@q6dVE8o1Z=sT*tt{wPHB=Ao)#|&(Oo)f_Y!Jd` z4;sn3o_?I?EjlCf`ejNpmnmMN;F1!8X=6$0Rq_$d!y$q@${Q@q8qZZ%1W4Zr+B+`u z^e^+w47T^{&VhQ8ycNL^uy<^rdOT)Qzi*L~I$+W!zC4cX=;UcBxKm)u6tmc{i(Y&O zp5h~(Dq^%KMkW+;rHRg|`)3tXV-t$`qpUS0jjy^1=BXnd?{j>TE56!btbS75g2+sY zGu!p+3n;WXrn%LVxzp`Wmf?7p6!*1KRa!lU*>+c%Qzr8&fkqh)M@2r1K6|>|ElWR#s%7{3$D~r}5}_^E=NlcggZ}qMaortzLJN~aB#We5Es#pLxF_60I_@-EfCwrYci92UPHnCGL2M=3!*16^(SoZlL-s+QH6#&fKJ zyMN9|cE@`>f)X1Y=$AAgG37Nim*9|%ICLJUof2&SD3J9!V_v^J7 zbDb2=0^$YrS{~ysA-*!jKm6nN)>bG?=in!#T5yY72y*D2wR#RWG>eWpXe)nUhTdV{ zL`7LNVs%>_+I4X}N78miL`EqyBTU(i$i&8g|2=oh9+|5YcqBhLbw^#SGM=c<8R1mN z6OYCdRc;JJR`tphyLGC;Deb5#2$&*IDzVLmxd_j9mH2>%z%O~c1zX_yzsJn?X6(o$ zCYG{=jauqD7*>%5J^slKEm*%=`9%vud(%#CKF8*STv>i0cjkI zqMP3k%zI3jI%fV5#ld_%JPZ*Z6$oO8h-q@b4x z;#xiWSXTBKx7NsmtGY6V68j;x$<=gfW$Ul5Mhp1=9VU4Wcy^mV&3yNpMHGFcUk^xDmMs_v~dS|${ zRfAu9J1FDIaY4F!r^>kO_t}B+KXZJVcb;-fb#XkS?89P$#@EJfU$Y6U%2#^hAN)H1 zvI%>!wxLJ`1P-mvJ@j}C5tDgMa+oW*w193ic6b2ZZ>y#byHzcU$Ie~*aPp=H{4s&# z>2JV6QIE*wY5_x$=&(ws(I#Rws&HIk%pe}%^mr9oA9KbKW1E{|{-3M$>rjDG2)F0@ zxKy6@=r^4b-yfxsyv|%Uc9)P{DV!(G7AKwzK5O~5>b*j5!FvT=AH<_?a+8MQay04< z4fZY8>U04IjA+RB+gd&*5f=2bWq?B{eXlFw|D`(TV93)?nfvk=EnT?Xjx7Ceny>L| z89b1gdxDINbwfl;sO)wkJ`X}!Z*I#BI>^hD_M41edb!k z*V!szgh=A_MWOwex69Qlk%r^ImPJO>2r@iz4heg z!(VQ{6QV7ySs)O=emA^~%FGwBd^bRWXof6(uG{awrSb8viQdSg(Y@vWJ`-B;t2@rD zpFSKE2C`=dn6Vd3E^eKF4BKYn;CyW)A?$z~^5vZ;}4n_=uchvet~>kV_hzbpt#5rM`n>?n5Sbh34czmWNw${I@Js^Q-?I4uB z@UqJ;yguboFX?t-2%!VtMZ`lNhhJ{CD8g;ZMRP8wHIbck zZFZo-s4AUR=V)`+u2ZTToS7U<(7swbU&p@k5%D&1oNsMH|$9*gLyd` z)19C%ZS8!tY-Z(Ohv#({XL!d)gctuS`JuXyYR){IbglTHn6mIo)R2t^a!rfV!d3Q8 z_>eEFadI5<+dX*%ad`BND+V<0%;1P`ED86O5kEo$0FQ`|IlwLN3?MKOczCmm!uRZ- zLhb_#;!SW1$5`13Etj){!0oqq*KH`}EqMk9-`7V0$;`VA9zh2x*adF?)qXT>wo;Ff z*bx9d6(LWc=;ohCo2hPg;s5^VDExv2*Hnz}|An(g<09I;ZaC>Wg zv$k);YCmzBe`J1CLliH6`Q-(U>rqW5)>IY<27>YL(AH-K7cZhzF{^Rzdrltn#7sun za?*-v&d}D1wF6*rpU}rwdJjQYK7$VsmThQJLNs5nporUkb4SL%B?ReQs+(;1@Sv5k zU^QHwl*qjL7ry;@ZpG5)=nCBfxuf-&f5hVvp7#-60AQBShr8seB^uhqH@%OT56v zi#Np6!7P-cwGyI`L485jL%vb)j28e7?IA>aYNViY`KCBK65Msx zG6zpzKnk9~5CW|J)d3fft4kkWoDDY)%-RJF!{7S&X%@BGMXgT&JQQ;UUrCYplL;ye$HEXnDFGh8OH}CEK(ANOJncBkHZNj5nLap12etyaSg1IkS5Z@edOyHD8vjzXDD%`c-K*W5A zTwuMLPVD0*CW#TKncKnyl;dgrc@5sfZvTka*EN2>;Xq_gHT(-6X?Smz88f^yAT!e> zeVCliESQR?YU@Orv0pDcAPBC^a$BTh1b%U{U@iH_9Pv^lwr2;QzqI7Bu;w;^j|i91 zgQ*Tr$_o0vI5>-bSWDLZ-D1S(#j!8##CE39C3doxA~{lOI*B14Pvqnc)jOE9eTULK z+tAkqEWDwX9&u}g=MMiacq|L(wjc`GxC0fUpdF7YhJYaG@uu*!e>hFFLwww(}c zmH;#U`vy3h=iZ4V4vFO^0j|PBhq`i1g!@=Yne~@@vBPY4Aa?X#a1&JS0QHuy(BRO9eAM#w_Yb796If7%v6{j)IoWvD$N6~8jin;r4 zm!kaOM~;^6YX!S-;EDSk%s0b$KH{qZ{!d`(w};h0`e_u}Mtwr1hadw;On3GDxQYia ziYTnXT**)jIli3hxYM%*>{~lsQ70G%IPdF4<=C4$efRUS_1so9(_xZ z=kYe5u@Lu=*b_t{mgr7|USmBs=lLS4^dBjI~unLX~y;lrok5Jl)U5Y^zb^oExVzxLFAS-2A9bcw2`NX%zl5y>&pIl zkocK76Vt(*izS<;!2vCi)9TPSyC)OwkYP4cr(^ej_17hZY6YD6p|k{asDdR$F&;QC zYSF^m`z6&DME(}>ydY(S(8mz!2^rix*%Nw#%A&yQ1A&fDFTS&JOe9`j*Eo>-Y+-Gy z^B8b4u5jdH-){f7>HKZ{`7!OiiQa7Uy*o*}k@+8b0xW{*4!+48%Jj;44QIAjUogU-p8nCb0(ZI zUxMzPo0b&Z58gIQ!qxgnV3~a@WlRRqsrLo#rM&p}ZF0ERnf5^>F^e`c87^PMaIsmS zN?l)`e8D!=w-ve@u0VLWYv(7;<%?XsQ$b3HXI}2r5B$;41~1dhf9*=`fzPQ0agc9Y z-35mj`sBo+{sCEQhN^>0<>313`B4@(sVw8!n&sP+Nx5Yit|}AmKSKElfug?Kwdp5f zQ#m-B@q@pfA*lhpLr>xX76Glj=f#OJz21V0M)P+vek~ksKSp#1s(4Q0DYf4CYL4S; z7{a+k)+a#Ip!7>pOQ6t)U2TdW2Y=d&ug!@GioCZ za6$8!=Ww=ee-7OSFg87|4{Dpb6_;BQMJy~gD*v7L0llGw%;_|5HmF!f~HG`chKm!d| z{t?8xHK3v?htQy^taRAt2;k7Y7$PtQi44Gw-IIRXy$|rCn}H9Muycyx-St#%yvayC;*{}yJVby zn(@bi)HD{=*t!13KxS#buAVzkeBns*al1hFjSYa_5%X2VUPs9KN(wb2px4?tmAv-|u z#2PjYLDYe7-o3q|ds0GmjnrQZ7hGZS-rvL{L!yc!@6{(?`PP8Py25ohHtX0f@3U2S z$VxShJlt${P-N`OuguR>WpkG?vun6lz*q#rPmP$$ibm0UGLiq*c zxlK5*Q`l4a*p?HK8N;c73Xhjy*tl-3L#kmQPaYusmSMd{dNRNv{Z)Nt2hi^0pJ}3= zHho;r_r_ zo>;f0EePL!&i{;WM^_ZQokCOw287-$J77v}rtrD_3@g1gY-z35%AmLe|N9GPIraD^ z{{sBhovyo@WK|sdN9U`9 z`uEn^3j{|LbkvAFG+-^%FQ z;M|Il0#!i#f%Xw?6GW(;1UPb|q{-h9abxfa?}z@HLxH)j8q2TV5sjYl;b<=5|RebB3HQy*Ux${twai^?QDh0GDxf9$@d)I4Vx#%+`JpEb#PaH4GPnz z+&ICp(Mk~2-#FoVyQ9kzm>O84x>Z9|3}k8p;V}`dxJ-n66Fe#09YU}m;CimrvJ z1fXQUl4`x?F~3@#ii1T0f%wG(4EW7>TF#DEMZiWn0+;y|GA(0=qapiw25-)WP#ae8 zX4)83a~J&s5!$(_686E{4P~5$Aedz!bN)LToZi@U+4p4wi&%uY+9SV*e9o#ux0lo5O z-F>(2^PIk|G@^z6WncksV|X-g5a2o~aJ&i|;uI?6Q*K4c5+q9J?gOU~#`gtQ88^of z55L|UcI!tEU1;xuZYBa1k;J%T08%JSjtG1YXgUn)|EEbv7Y9N~)}&U_s-r%KXafCU z$Gu=M=St%d{u}Mz{*tK)WWbu5R*T^gK2=)s7pRd-B3`fPeEv-o(Z?HzKhs`f<&eeF ziLo*td#4jD&`DI{uxWFjgndl;aM9%Z-}8gh_&8Lk29o#*76Ep)3vc2LqzI2-ryBhI zX1pak=ovhd3~k1RPGYaZ_7g6-8IQhwWePMeJ5-eo<~Ms->%(%7>*T{?#M#?M(Bfor zbZ7*R*oSU=4iq(xGUnqhPG|0(;;#(h80n3g+l+PYyjk4*lCE)U`JCWz^%9t?LwWPo zwigHmkd&`Zz&RiAa6(0_Sq=I3h0ZoOxu3qR8#Z+A2T#n_BK5BG;eAS%UYPb@>~z?x zA=nOK#h{O6EMv~|!(%R>q9csLQU-mM9tC#YM>`sU9G=z9y$EU!zJ5SU&P(z3v0Pc9 zlV1gWyPtrZZ6Dk3FyupVfONVd8l*E+ZcKEfZ7-;0== z^_=I%X}po|R@e>IBn0K>XlN%B85ztZRR%VgvzBBhRnMh#Q};D(rGL(kW24he-$qIP z%JpU?x&Au0CKdk}&-)GWi*IPq6?)pdqqCoVOeMmc&T~`}=@u$86o<%_9{QI4x{~?< zox9*l{w=8Lg535>q57eW74(s6813QZBz1&u38mNj5D%Slm}|K@1-^_!F{Lx!L^PD` zIHDGJT(Gh49u9=HSzgzU8)BKk9Ber2%=lb$RpM#}EL4)h(j`m-vo;7Ps8 z;#*?N{@)7^Pp^|Nk!HSB?x1$m|1^~VSw9CaY2F7^BJ7aH`KfNBr@f%yc8?p$3ShUIv*j-}TZ4%e7ou2N*jksz zfQMrvsl^{&Ik`(-36jNOx+_F1DQ`y>bViO5vBFAWr9A(|c!Xu#i5M_Ird-lkeQ_J~ zHgr_WUHE1W822GLJTZ+w*Ve9cWp8JPKWee7t81~ls3>?#qSx-_*cqq)M9|Ou)|k=) zJ71O0|Jbu{+sHnSc{ss5+&3HBAKA>wNb$XM_#y#d?EX_09+*o9azEiYztyZCVs7j! z>(wY?ByZW(or0H}D;Hjnaq)PMynt2~#S_cw+TD5%l-h!vIO1$g(U!3|{kCNH%VRzF z5-drrq8V|3p#pNI+SeZnbeCXivr&~If3B}R=74&g_YTwm|ebqVnc&+!iE`J*6u0-2Pv#y zx*aSW_uldX)rLcP6Z{WBv}c5c)TPv}e7M?GZ^{YILtEQZPYPKDXI28Xl$D^rplWL| z{z5fO6obtaBg~h1Ps=VEV7f_*Uuxat1Ia>9p`gWA_?Wd)++Kki^%lfj#6N^Z{aJl* zwedO4h(p7MxoqgMacf?cATtOJESk#kn#dXEr|F9cBilpdc-jrH-0g!{-gX8F*&aio zI!PDjywf_f>+75a6# zmz$}+QzhEJmS==1jw8O#OuS*(wnumydSs1InKNY4zX- zZmGP?HU7lOro}_7VI{WgZzki#hR`W^DfWA$7?}O8WB7j}VWEZE@*ES9%un2DaJY<4 z*p;!;?O|0U1*QGR_QkfGCej*s2*ywVtLH>bfH3stUOH*I^%V z=$c($!!DG=u@~4`KOqN-99&@qIV(vDomR10T4IU><9a(w0Plf$)_*NTM$)eBjhRBmUNsk<%z^Fi;8! zxp*_wCZ#GY?-R}(9$QBP+ww>`YcSUW*i>>P)uVSY^zXN3&0_%1hJCT~osWJ}zKbH> zyQGMJaQ`@R?=DR{?CwBU8cX*IEI5qMUMchpx?rl0ksQXWKxy=}MpQq;Y-6KkZ#NRy z3m1zqo6iJ!T-)D5kX@77PvqAGoU3L(lYb8_;)T|*fqisR)e+EeN9^TidXO#qxev>= zj3JOi-_C9dK=q@dl~9X{7_`lt>Jq0eW?Z)}%bP3;UFNA0Uq?+SztQ5Z!tFPUmsOACJDZSIFH;!80%2u~zr@9+WH&bgW2O zB-Pe9wqM-f8Cvf({o1{Bb-jS)|8bJPz^ZzZS|PIxD%s) z?v|gMp-MFdps1d3VswO_Lj2%bu&vwCkORm*z4p7^BK`1619xsMFvU|i8GTTSa!8VH zF|f086v=^~S6%Ia41_8p6$3U3!1%C$uLH-w7B3C#59C{foQH4w`JVJC-5;eCnB?|w z4*A;3`WP|$R>p^{a=)+F4wfwAr+u*;$ND8F&8QP&qRlDshmKFTo_YJ^u};3YXqT8s z@}~<|-F9j*G|NVR0vFNASX-*+J>cWWO$X4cw41YgUvEYr;IFr}5W{!|BU^r}!X;)= z1R#=)MLyy20%a5Fz*U$WO^0>xl)8@1a?(tw%-ESTT&69d+=ME&gT)-F3|c=&%2V9{ z9YsjlY`Pqv$-cLRxBjF{Q#sEJ(rzk;PSCV`HpEOKy#@lb*wWSoLhZZhoo%};9PQB~HE~8e?nh0T`Rf5`uT#{cTcFD;AcCz>1i=5TAv~`M-k1MFe0P@L~00a>ROePSUZJY4)3N_F(X8hq3nhGG`s)XYm;3`9J< zp8Jv&Lx+;LOG`eHhDy+(X){5OUAU$yNrHbjAr~!%z#yEUGZCkP>*1&(DCC07I8JdZ zR0ePMz;XGOTTt;;c&j~PP2zRV>Ig{K|AxeHg{}3=F;BA2Tw!2Um4WGQ1!Sdl?MPD(uD)~iY@d2&um&glTI$L*NJYyO!tQ(mB-|4o9QJ0edLgv34fB}KBQT(Hk=X31G zJbrEPEb-JuZUU;i76^xc6p13B)JcBo2JxXb-*VAg!h1C$K+GvbPa89~UK<|4`Vl>=J0$}2xP#KW1x zrgqG$TV(EGm=ZC*rZ&48ciA6yWb}$2&iSj(%Ch>OE(^ zZcc`t?KtK3APTz-87(n*ycu>{lRP_!qp1Mau`=h^wiOn(^H)PG$YM)ET!RNEc>~W2 z6^kMH%PL9`oQTW&tVW2(0q=peX!VQYhZ(??vC8o}arQalQb(-N67eCAAnS+Z_lpQ3 zmk@C89U@Qx1n|ekfn&e=sH7JZuN99oVFP#Twt#+Ac0XjsJWITZk^R=ue3$$0!;0A{P^f)xFu@ZA;o zOb+AD2dapP$|CIzWh9XtfAVs7?_;R<2W#K>iiJwnJ`9PAa&~yIMh1s_d0}`*zUoiW z)hz_DdbBG5xvECN|4Sy$m_H~o`+0@Om?8u@g1Iz8E4yOSoF6Ip{5(P#Ad=#gSB1j7-%P4Z6U@H{N?#&Q_-3o!wz9&eU9e9FnS6VnZFW9>)1BES% zR@0`>);WeMw=EPTea{c9*nFeT2~;-4k`~&GAW$iwK?#FH@MsY( z5`g8p0|aiu8BhWL=Yc^hTk#nSKKi{08Bv14z-m1(eQGy66$ z+YjV=ib70!fC2+1qvJ$Y%K8(q!`~xD8L+1g-2Zo_g`cp0JX0!e8O-$ zLpMU4CJ_TVlkbV{BNnQynQ}0|A`;&nB4FC0%5cE}48{XBiZoRK_a1;l4ONQ(W1WfF z)P2a-t7d&Xs!m|8B|&$!cIY_Kj{@!y3Y%M~l%181^#CZ_*c-hv@zAMGcH3qufZJt@ zm)BmN#>&E&OU#&CLRE_SbjZK|KDoSjl>VBadU=ei)Dft#r=vi{n)ujBX$y{H8=jkr-vg{fWwt& z11De~4ng;XPb6H@%Kp?C4U`(~ym`tZ_2Jn4&sg0{4pHsUnATA-k&!J%_UW~fia#r- zN#yh;1FNY8Nf%PAOIn}+o1J9JMGV^1Tjoa{S>^RT3+x%VCSAQX@KLU_$`OKM3AQ6WF$`&|_xR{i`{ykx=^QMkCdtAQ` z1a3nwz6HIx3VX7h!eEl;GVC<<(R7O!VGj@mBA%>~jc3A&ufTUfpos3pWb~pFV8~b? zonFw)7HPMaDTlgp*l)`qo)e}QpE6}0ZC3x8S-HX(6^MABSmojQs_8q$;4C2PkT=3D zS#sCKj50l(@mxkN3P?P9^4Br{^-EZ{XlIJn7(V?hF@4W98XkR_e6Ze67Z)iqe?r+? z9DY+6c_S2C)ZnYup&ZH$;Baum^~jh-{N{<8K~MIZe0=CeTv_-5wuu76>eSM!BTkLj ztB zLL{Capp@Z3N(Ybt*)PCBQC(R8(1cf4Lxj?K$=le}7$vs!&~{4`-YShnO8i2;kq8|f z&zvA(ydOU-ku= z@!Ve!_k6Pmi4aCj@EL1zg5=f8zDaYKY=S80VEfZu4|51;$iChZ{S{a0pu7rJ9q_!tWz98qG2HPOVJa^89w$6BnmpRsilP8rKQH|o+UWkb zR^5NEsk}e_+yPqEhGERZ;4416v)+`Ibx;VU-5e%eZcy>N7e#kuX`Q`yO^gnx~{J=B3hXXZam$e@XYu<12_ zJ^ki&$oNL^s+rV_e? zxN>Q)=UwJU9)L6xrvVb0Q_czi5f>{ZB-MI(Z()i2;3u{WKwSRHLv(m#bb+(tQkBj9 ziKCwrB+9P=IAo5Q3?UKU{G9VC<8N|}aR4uCCM;6~Wc#vZ#HR6wcH<BJ?IQHK= z z620~i^HrAe^1HqD0mN=q+^RHBy$}yEXz(Ju84YB>TuK9BPf9n_G3Y@AXHgwrWT8)3 zQCuwuSUYTgqMp_DlmJS3e}Br9&ie+lPNR;jsFK%sCg2O2Z29lpJ(&v;{fi#_wZBCP zr|=RG;TEa6|LCLH-v2*!%hT9zV0|M2RvL>ox0bPPhYrptUd^2>gxL5|4d z%?h5NVj>4Esn@;-#C8{)KfD*@P3DO$@}Zn1h#_2s~1hVc-R*JK5!H5S$FG1 z2w7l=+{UL6FHUDA(LqFkWMjFn+{DKRfMOk@W*{)DVxG&x35;v;A5-RKz+8bw*fx=@ zvGRU|W!l;$0USyGfgxPP(AgR%CsMOv=l|A8H`qo_kwP1Lj_5F6C2+|D>Y_KYSbdN2 z&irVEsCn|ioxw+&71gVQio2VrI;mqTwP=UFJ&`fU6=Lq_JTV5UmJ&h|C!V^zrK-t$ z8kj0IlHv}7(@pC#q-^>f@%58PQ)sAbKsgKr+)A602RyH6bP#Wgp5q3d2@WN4$`~&J zzzW<>Mx&Qi>zZU9vhNEdP=uat2q@FERGaax5=TyPiis=ES}^n=6$5-#+3bYWIBObI zSdrq9ia@0b``1Q@YQ+h8z4P(pnd5k@H)~%{h#EavbSFVF`FK=Rw&Pf_p`+K;v9}yS z#Rv9iCo<|UJr@LoEmr*qz{99ZOJ2LiIu!kau;Gy&(omQ-7YEUOF-a{*z%#3=S35?t z_Pw^|NZuzEWr2wy9v;9NDFMuC1wX0l7@1q-=-%v}2I#Z*S>B{z?Zfl9yahLI_-VXhZ03vDz};2hr}Jma6Emw3`XfIFK@yeuX5Ic_@QAA}@QU;gc_OU2H8jS^UC&dA#YUh%TfqMAP{(V zPH$k=k8)At3Lk*tJ-m+O7ude(zh+V*tO(Yxs$Ki4&>dhbfuD zW4CwN!kk!~INr{-0|Kvr02X_H2qN#vUnB#2;G$bwE>*BYlW9~rKj5jz&O}qq=t*b{ zn#ql#hmIM=@AO~#k`D!W?vNkT2j|ze?+#Eg5kApW|5qLXuU7Hml`i`VL1dy_5|o!U zc%gwn*w{(NX??gmXFkN^iqdtjdcz^Cq0 zTYfm)Z<#~e>@SfVm8k1HYr7o(QsfhSH^RY)S+4Jw!*U6OcU3(tX=I)r2eUIu2I$Qj z@d}N0{S&G1ti=lXMH$;!lI>?f9?&2^y6llU^il6cV}q+H>gh^+0gKXy3lFDB!}B-C5%E(z$Cj{G~$Pl&ffXaE$v1m%Pge@XH`qj;CId z7;ZnQDLv0<;i(G|uYFKae$L}i!AYd7>lOHy2Ay3K1Gvx&n<1Ppohu(i7y|v zd&Eiy)2LjP^Qw1~1IB6`dTczi7x7w+%o8;py6}Jhpl2*Ci;YF91{>RY!t(&z1^suE zT@f=BC;w4B5q~;Qmqvcjm1LB0d09^6eaTT&=qq>XbTPHvpDNR{ZNKPdJ|x4-KN=lX z-5+)D62%-i5;A$G$XHji2ore%jqdtOK3}~10hj^ti>|u8*`>)jGZ|NHLpP>2Z5XeKyEyIH1^tYp;JPYWn$1n z6P38|4Gj543EVM$|JGfZ@<^lcZO4rOoaP6Gv--+KDVzMCob{D!6WzFYM+s43{ZZ#V zDes%`N4#ez76vl%EmxUyzY|0**wQLuiRZd+83K`VBZ&2}HI``+qE(+j+fl_bVjjbi zKUwB^gwc~y_0Px3&ZzXN>;*eb0j4ae+HKYA8g8+qIm|+am<`^nza~%@AP@%IEzs=r1dR0s3F9pq`BO>goODmbRk2W7l2 zInE3GXdoab%4oQ;t9sw>W>Acz0|gP7%qIN5U)P3uUmlH*FlbB$f!uKN^&^sJ_8N)s zdhX4QdkdPH?EUo?{HixTC{Hm~KxIs4|5UH5S|oFl?D_VMgZJQq%llWcUGZ#i36gMA!RHbky&Bj2v<}o^#3@z@_4A; z?|<%{#n{Kbg^@%hltPIasZ=T|NhKqx6rqg@GnecX?OHxesq~?;CHkahh$xD7vdo~O zY-1nH^1I*P>*Y0ndGXx2&-0vf-pe@*-cg%yGI4VpQjQ4TC465pBe!a5MQmTMax2|o z^1uV`o;OPCKSOSXrUhp+Va^BQl}2WC;X`yFwSKhr*gtj{p)2VIUT-d#ED^KkUqk-g zBYU_K$S#^}C2=$f{11KlPh5CVhVS^E8QZ?@$e_uBW!>chdk|h0=Izyz9{FeX2jK+) zc2quQtc7m(td*vnGjb$u~hDG4UQq10JsGTYVNR-iE0dG6J=y<-w_k2@DIeggmfQufu{ zvxe}{2>tI`?-0{V5s_jE2T_;JHu(B@!NYgoe6IN&lx4H}Sa3!0;f zHTxQR@LGhK({3B0+Wz?mf?EN25{R*j9EW4kxVj5j@o124l~zt@{xANw_xJK@a@ zFGU2C^6ZYgd<1kl0Ud2PvAt1us^G%!>R7fv?@g)tu?^xEkH0B@7ybzC>nBAjIv}8h zhNJ!y`!JyCsH%aOd?$lC`iVPEY~Bo_va9acfM+&GPPcx3wao@Un*K zm(`~ev3aK!sIMA*OHF2B2M(U2BJaDHMJvRnZs>tYHhLM-01iJy=gi2z#}>8PRsR0a z&|xUtKg~SzZEx4NSI4`pH^*g_;kE7g^_eeY?RY;<8qaFg$aIpGDAUg{qB^@>-lpL? z@#vz(py!67quu*|)jcs8owc&OJNAB0gdH`}p=s@KXW*S>%;d?UImu!Z;Hf3cMxv9o zephQw2m08G{%M^fsjBz)&e8S95sV_jF8q`CYV$N-1%wYhH1JD%Dz{>=Dm$3|vL_ z4~?!uR)6PiIcUeQ014I8fOcAu{Pm{d;6Bc`e9VAN7oP2p``&rJ<9+Nx6HZrCK%XRM zc-^_H2!ni-G;qkhC$d!$Cs0^+8zFo&C~GN8oPE?{HrN2E+QR^9f3I5E1*cy>)hSMmH z#OI0*34*!RB0im|R5}dMGRr(S%GPG^j)VKdl>1DMY z=z5 z?MuV)(pzt3O$hyb`>(CzBbL^{laG#8qCdd(J=kjpV5|zF&fb=Xr3xTciIH&$5KW-^ zsI|$C+eokLBv#$l)%X%`98Ef{BHEOW9DIm2i|AfdY5kL^y68~af0bllPfh=$z_o8> z-H=S~7yh-R#K_hws6^PbVml{Q9P`l7 zI@XQnC!}T(y}wV~&ABLYjYjWCD0eePkP?}yP^t+63c#$`t=WEs_I>s;R~mTkVf-1% zowg3sX5Xe}4H8C-@7AY{a5wA995E(+f4*)w-6iK=1uPi5MSyNbhDILuvpZ`+A#hB^ z->Qd&L0j-0cn%9NgK+%|v?_vyP@9}jjy<7MLqAZwx5$XvcSIQ|MVptXh*ajIEH%Ks zVn>6_n@?;)AU)>e82!!EL4c2LBYC{F$F_|>Mi|6-9WjfHTCe1IAsK99^$=}aymqXA> zYJ~#f)UBMZCS;F3r@NqF~@DtP0&S_hoP0ThIOZXl;V0sI9blqWrnv_WA2pOkb8lm#tc(g)URbo)@>f0AdJrPiyk7 zOC}v^qy{xIZno++napWi7I{a;_6g7(&;b8NY16@(e@Wpj@kb>!H6xr7CUR>sZE?a zks>=?)A~MkYaK0fQXtA8Kt)lc^g01UCZ#NEgtzZ_X3Ux8@140R$47MTpm2eFFhD#y z5PM#YiufBZgcS(EIGWc8)Wwg{pDtk;JuWe*)!*0+!W`?kx7<~K57eHqkkD0% znAyaZ>u$*8SUb3m@y)3R(=#e+Rf*1y=VT)Q)#AwcHO$cQ*8@2}kawNz6*BgmUugCc zgr_zkldBg`(eKJUW@UN9nTW8S2sg5(X;j|Y1>kE(g(nlA*qp$GpV`N+eYzto2$h6# z35=IGC!{9D4>tk!<$-`auL2=YfJ=;x=OjOTuio6sCvzta$o=HlH)i{bo2ON-_7Hs_E7taO(r8sG8VVH8uOe`Z2LIkKmrn4v^+B*f9j){+OTajL=Mpg9JCC-VT3Z) z5MXLg8<##Lk@ij-d2gC53X`uFBj~Gnvt^_7H;1TEhoud8v%33lBvgdNo98d z`zyZwQkv_-z4qtw!?eZN@d4}oouh92wnK_gF(n(ePLd}E$>@zT$8UI;@oAV!K>X zqB8)dY(tt0n#YWRGW!g}o*qP%n&tOB$Bi#1!;CNS32DxB7E80oJpQYhOVsfMn^d~g zAyDk#a{b(tXoWta27y|<7&!eUYPxL>K%k5WBYt?U;0+uMs6D_S)AdrqQ*earbJ&m3 z@8-aHD$cm`Ugr7&nUWQHj7r(h0%w^yYbTo!+I~F2>orQD>&u?pdmiR-N_MM;9T`#N zJu+no6}Kk75B)9NEDF|+F?+BQ1Xi#nAHmi^+#W_VLwEgiIrtWwxHm{qaHPvhwU&M7 zQ)xl_!jnYgs4?#(JxX~UZ|X89&Dz{&WRP$z$KYEHUkN#`*K8Ejg?OP}BXsqzn9~cM zA<|-(^e;hS&Yh%Z$TPTdkueN{ri`=J4lrI+nBuokhR-2a00(I#QFiX8Xpsv0b)T_R z^sPUpZx+M8d>w~^+hu#5agrnJE8 z-Au#t*@mmL5^Hf{(kVIcC1FWtD8DUl668W5_Jr<3eWLFI#p@_zf+hg;NhJop_DaWk ze(D`m1mW#(4-ljA(nvg!Z5T0{Jh0^5E34X;x79JbDS;%!hvcB%fL*A4#&u2m$Z!YD zQbv1oCsyfPK+=ceW9EQB;w;$kFI#sH$a0a%U&pAxDV#Tp$D4y((-ob02%|+E!slX# zWHFOKdnaSAkr`*e;tW~JZ<*?YS}A*Q!jU&H-c%*4&-!WbmTD`v=TdvUDnx7gdF}liu$unkg5IFPi5I} zA7K3PW*|_ExKrvaSU`mI%70NBK5q4pYM^W{GbbUG6{G$Wk1%1oq%ztCiKc%9J%@6s zwa^j-+l&^p*!^P5L#?^V59yT0=?pSYb4bSK)hU|6!*1(b8VD?YFgJ(~_!@uNr&M!{ z-a1zJw4+G{ZYyw;3l0*n(nm5odG7nyK5YL;;N35;ole?2l*)DV7id$xlnG@!NdN7{ z2d$Mx5q>dv8}?w?dK9F?YSrg$cmcVX(mXAo$wBg0IRr50XNt0yO#a%|ux5p36}cO` z!Ex)5&>8_vu@4rFL~*W9^omra7i5UOim>rdMDN>Ee%AKKB_zAqq+#M zt@lIHv|w6~;yBbJi;tDV7~@Uh@|ixwlXGii79Sd9atpyfn6ZWEb5Isj$+vbLpQJaA z<4ExG)ry!fN*e*Yp|NRM913R?A>4DaJUzzgiOC0((bFGxzA?&}TSFS33~Suw{6w_e zK)PPE-z9h21HtrbWCz=7UG={LMlY5ANZ|fm%Ko>n;3EQ&HS_^5v4Nue5aROK#^1-; z%ZT`22MK$7qG0*!&N9-v9n`)Ep^|GZ|4i#6mbW2c*&DlS!aq^G>j@Xwr|zkm#6}?P z`wdD>Ll&>B_d}pQfx)M3`AuF9yjeJ0t5IY=jQY#?m6{fI90CMYS3P>ao^xCW>g3dj zI1#b@rvsG|qt+m{m>Cr{?Lui#%5HDX$^72|I6K)yx?w!~_Mzk~0xhNVB?!Edkr+Mf zS~72O->fOzJB(2IhBQR5Mos1_I$g*bDNk45msE0oq4dou?z(dBlDP2`c$al{`rTXX z@+}B&!?&DCi~g|NLXWR=#rTmg9#=i~%MMRN$nO)8wtU9NL9R}_nZKW{FP-9bK)o`t zf?JZszPF$>OggZo=~9vL6aBrOgH9sVB?Sb)mc#;iMn_ zqN;-$dh!D~w|lthBDtXK&o@N>#2LJt0_%P7fgVhz3ec2W+H!=i;F#8BEQHIg66;-!6a@)5@U9=v_hOjaviTPWO_ z5}gY%wX5&ktV{*X-%0j(>0Nym_#kTybZmv~e^0p6W~3AYIcwbqq-E8H$F9ygx! z_xk;5@DlgdF0MgOw2>k=Iw%r2Z_3M;r?B^%RBfgSspM^b62q$gM~dAK<`9=X)EE5x z621fc+tq!@C=Z{VdKrSJqFV9LL}*qZ!ZI{ezlNRsPUaY3z?nm8Om(=#+|Hv})BFoJ zB`@Ti+0J}4-OBhI@?bdenG8=GUsd`^>#lMMWT;>FwRY7UI4*Q*nag_#D{3Y9ooNp& zIS|=qsg4kZZ)!3u31nWh*E1zi;i-_DWA=xJleRQSh1Z`e2E4LPH@J$>IKZf=6xIE; zn#HI1`~sd~ZqD~#(l?6S4dbljC+6yeATjtFd00RIpsfDgisf;JrA) zSV3ud^LCNrh|YY5Ikm`C8$G&d;^S)0dpQXaxUbGowj=v}#fK8FOz;Tz_p|5!kI#?N z!V}G|b{*^8_0rb>_D>P&ZnzK4c>vZ^ITny>DIy^0JeUtN94_Bc5x}U8&tQrLA5`j{C8XqeN+EoW#8??~Ty*BZ?J#^&%3iZ&c#< z5^+N4(`SEl&WqQ$;Oe6lW}UVqLKqacQF{Lc%q%uxuq5jd?9zS4Itz!XuZp@rizZXU z^?yAPhs<3+b$;x`zE|26fuD_yTN^eKXkMTazcurX+@ZB!VIf-QZ+Qj6i81UDWWb}N z%QoYqpX+j@t7?n5e67P$@jjA&Xw5~@iAc5&{wn2uU+fxw(dHyO4uUOj`7e&M%VJpO z)y%v;JnZIG4;$$|B`WhLOeEX7y zFfyD%1>trK$V?cfXZJ>37&(z%-J>@7-M;53F=$L`coTRVFxOho)A|3*4Bn^Da~$Do z$N9Y-%#g5&*h_VL?$zfR*2irgiSOt1rgjalqp)3*{ZNK&4Zq+x`&Q-Viy8>ChsdYLEcH&NW&sPX*5~&yYmWcQEZi55ph3O zp)Pt|Am@cR?8+)3Voz5o1QHO@v2`CLb($Kq`S%_!D2}~S$ZXGi8EI^OZl^wASvfUw z4~KIu-p6MW&#MW9X#)A_UPL6(1T+anot=yiu$eal*ikDQJL5!LgV~FN78;<3!wQx~ zdhl#>S5UjPHcgfBZ!Ukec!vf@|E4iEB`MHkTTbLveh@tdj`R|HP=!rblwGpz8V0OP z;2K8X9bd6fE2cd6hG(;vZbf3>kx9ON<(BQg7NW0W!InBNu>pl0=p%rIFB(1*27dXd zI*BlKODJM3*4mPll;GA?5}dODoWb6cqC2^`p^;XCCE(TZDlN8 zb1-8U_s&9Ce6HFzxllb(896YwVw-+yE)dTd+lDR?vBGc z{IJCjjY3un4*Q={hbg4*H+??|=y^nq%7ABXSU}=8p7NS|_a7S9a2FY?`rdiC{~*5o z8l~@NWu<>n+CcCAMF@_clwTewEFAi{ghs@!rsT9_lVOh0PZeYw=3G%@r|->6W5w^` zpMBI+*=6lJyfx>9_4}9$i{M1=^`SIw5}WUW(tE!8S;d@e&lu)Oj|^k=G&{lDQgSb7dmo1ye_Y zy~Mk2jnqk5#aksYIHnO;?9mV?QSp^bD00j1@-2$lpAUYdSUfCWDWDQxmB$$+cOYw% z`_JYhJ32IYy*SIQj#7wg>_T6voUU(ep*>Mf9PWtUfZ5x~V^;weT-0pFE)`(KHs|Gr zs^ayGM&IWCX<8WYlT;DAkrABAMTmD}dXs|2H3B^Dv(0AdDHD22W!sN>Fsz@dCbkTx z&4EcCF7GW`Qo9oxZlM-Z2j|o9l^mabyqo)2L9*##_ci*TA-8~14fgK8+dFn}AB;~K zmVqV+OMVXHv#0$p@Qtg76Z#_KI~{F;{7&(H^IOj>IN{Y=Ngsi+0w_i8ch6iKjZkDZH)(^0$&<6!>RQ&oK$ z;U)GQJ4#DxVhBz{V`mIFTEO9i1z?=8MwPLgymWLfM90|WBfJZjCJJKh(UP|=>ayHz z#yI%#UA25r83Zr%V00_1%Gx70Zk??$%1e3jx94~p$HldU@-nNVPxmd4nVxI5Oq)WYvg8_exe z==t@1&ao*5_Pf#3Skl}D*$0Fy#WhiiH5IFf#J3ur0jTCq?fa`sXG!(7Z!Xu-qG}7By3g39v5*KEtR@>;<~)`wT5+R?2cVrLRaL{ z58oe#@E3m^POfANc$;bAKq%iRejI~pe4p?li1a$C2c(CI$YVpBZQJApG`#(DVnZjD zm$N7V&*7c|c69?wgJWaA9)!Q9E4pqAFZkM8c89AhY|h*?v~}*rQt9>C@?Bzg8ZTgh zr}tqJ$6nCZgR7mxnyR%;u{SyHdyw4G`M|ZS7``b|)zy~ZGsrPhL*2Hw7lWx1uL5aD z7Q%ap-}Ae;dt#)2H%cZbT<%)Ffy?>j3&ipy9E?(?wC^6VPKcEk%5U=hh9ZA-7sCG?4mFvjF-s?5|;~!O1xX-3! z>a4=`1+te9!Lv|g@7N!*b2|?aD4ynv+nQ*=JvdVTI+H`sB$ZqsckWV~7 z-}B2l&<1yxVpNVdp=c8`^aO2EY%hrFdl?VQCO>|V(e^~?23niX2xi?{^Ki@R^n2HQ zYRHWTit`}w4hd_(CIey%B4!=e0`3pMx;@l>-H6&!!_8Lyq$enRi##Jjk%}|ss*5x( z1fIFl*|L1zMF8>2;es#L($QoOd1#FP&=vI}tbw0KgfMsM#39Mh)O3f2^k6D0N-?HK zz}rg;A0PfXdek75J40@|8j1%X`>QmXp`jNIFX(9Rg5G?tr3V6{j0%nLl=CU#nbXVv zGPnLbwtKmM{A1UzUA1fIds5?9Kqaew(q9vAqwk3`1|xw*ghGC4_7VNmZMwRyq0-^ zchIkxzt|rWuxR*WJDk0m47pA zi4X0G&2xgvZG!gfr37}8utTvrrUSjTx(&{^?%u#tvDd<2Q?O{0RUe(uiLhxXpM1;0 z0ikq5Tvf(hMTn*@CcxY=ercA3zqN+>=E924Ir0nTu#*sUovTBYxR=*(aW&Fd2`g-7 z_Pn~}Ldz^c$u|L6#nJpze^%M(L7%^UO=PkGrSG%qF2G4>$YJb$irCncMwYB>3D@?q zB+SfAnmGDpeX64zyM!~QZVI+2)R1|T)3eSSReqcFePXh#Y7|Bg{prhFsh;6Cm4bj{ zg)9YZ!ED_nS@vcwyFnXHem4GZ=J14dV|;fiQ^6+osRk^|=QG2;vD@PLidDmuv$h|K2Zjhf;n~&TR z-EyO-1|UZ~(gxYG;m)Ic1iFo!vlW>Kd&z$m3RnS|Due?FE`@YZ`-(MVy3Y8Ye-x3q4s@l~B!&xlJjl9)N~7kD{g)!q3{N^^my-b|P@x6RE$2F!n5%^DwME z?7_)zIlGZ!|62%VFOiOR^B+CpdsOr1e8gP{Ogocyj9|>eQ-ccA)+YqU5xi=4ozy^W zUz-iWshFZ8N?(-Xs_XO4lTip+Lv9(>l!HpGv0r=BSGVZ~Em@ZwNs16YQ3}e?BhpeA zw`tcmCB1MFTPtRE72JqK>}!8|5eLB!{ugQ;_lhSeesKB9nUR} zW2QB8Lq>uQ)^e)(gOSO2@oE6cxbmPTv5H{h7jc4Ch8{d!zz9`dmPjHBq8f+xDP>0B zW%r{^6KWtB<#>eUE>lQueRW@y)^cG)vNWnVR-NLIKFRFe^PWcOX2w^1tYIQpB?@i< z!AutfOE-h~mRnKmfJHsWmBSDJVTai@syYc)y4`G03O7xn%qS)re?KdJyU!48YOL62 zx-xD-*#a!U5yfmvb7rmBvICN@itLxIEL$RfaaGKc;iEXW)J{f!4eM-_7AIWT6eXHN zfc5gQzSeDd7az;`>b&YE;i$F3%6PbK!%>X&Br)JLvLAD_**rPcut5d7gEDv2lU!PL zhhED2lo5O`WXSvd6nARZo4a0&UK&m6_84Mq7T3Pzr*C*#Y#6@ub77Us2QNJV*fg@V zp-AB}M5%RvMWK9m?|M%QQQNz_#_Fsy+S_WhFM1cXml$_QtjlF(CQxA_{k2g#6exK- zU)-7YN~3tHplBA+)CZzY;mTcX0{G(skatoFcy{L*pq?a~S=8>>W}ER}hOUfDim&wl z%wrndj{8=H_wxH6{uyT;%iR1~eXcJ#&>VLr<-hsN({ufGb6B6(ltC-;S0`H3&km^? z%*^73jNUWLrp;W$2o&+HYv~4Mb^`}CJz<|X%tU#Ik7M9}bQ=Q6ag)KW{}L&Gu?wbFoA!xDs1`1FyfpFBGLYTYd1wWX#_~Z zCWwJE)cd$6jOM3@hTj*SowNVSPOUZmJCL<3Lq-5e6-B_()eHm3hZmk^7C4X!l(wg* z*M}IyFSILDY3Atm{C8@ew;X}!J2-uRarkYe2LPcJqJF?0a5YRmCD}oEkiNwB9GWU) z-XUcsEUdbtMs9QyJ6%zpOyd}$eOebJKs zPGPzAy-bMP$Y_2$6N4hfYV%F}4c`eV$R{0tc_iQj$B-C4Ql5l;8E|@ra=)jgI8SCT zgdQ)X#Y5$KyIRp+i+dNeKd2mjoH}lK2cS&`rcE zgRyh%Uk=H^T^T#zCV1K-cSY{tqaF#9?}M?;T)WjSTc($^4QBV*&J(eog>hZN+Mf_*u7ZAa0)Pd7_dFQmT^Uhq+AXh%)rT!Olrj=0K(fA%@S0Wh zznL&@6)hG%*vEdD_tyXWhNV97{`k=*LU_Binty^rWzr{c@!aNRV|0T+Iodmsb3rQG zi;re)1;OxX`S52scsL$F;paPGxJ=Bce+kVyTa)NE;+F?@V0=n=mgF@P6<4|a=v|BN zkCh{lFZjdPM6)iU4sFRbO|e^P+1fP3MNij(-TE)OZ2aigyQdcHLGdS2j*XyVT10gg zq-uuhq#t9)dXv_N;~5si9fw_PugJKSXX;gYhg%O$-@*sjZby#$4=aFljaAyW>*H0-44WX ztSA2P{Ug{+3WUcSoHR-Kkg@=wvMI~Hsa6c{oE2HLNcLHsK4zywbTvw*ACzq*(K4u* z9zB&i_csZFx{V|T%2N~9wWY{5c%eKS5(+~I4=>5wG4BSfPi#GOUijpX z%r6MGQKkTznm{|3L)|-9S7e;I9VJ~B^0 zmE%m7+%q754L-NzcBxX|3nW|)YV4w4IYoM;vfEHRr{!g=e+T`8`kzsh+zGd>0k4CzE4lObBLhHB4V2nxUsam5apSO zS#F*r3xGMdYMA=esU@FvZpoglrBWJ&%CU|PH!tdjIzsq-so(hQty>fJ?jnQvGm9;R zo00f8Jl={mbvgn=@YT;@s?c!HtBP^YPq?7^g8@&JIyEn91GX3*L1nf8glTw}gjF-d zbsHm)khD*)UTL4sd`3NzcW&eaN{Vq6Ac3NXIk<{C^Vbd_LUQwtC)yOz^x+@`Fc%UQ z^N<3xq7gtCE~#7;zaT>{G2x04G<;;3d`YOm|I5$XfIB5XW35t@?WI88MyDvb0QIj8G?KQavlYv114CBY^#YB zPX=vJjI||eA=S0!ko=`pQ1d%uXy^gn%AsR1B40*J5PtDBg+C=nem_os=!PXUo-w`eg6e zt0Lp9d_o_|AzM-75$D*5Po*{ODPwszMQT%y{r4%ZvTqB+fPj5XdoYldLnvh~mPmge z!a+gy_7c2Ou_r6EC&UIPe{i8)=X-|$N1gcSPL=`qdyb)0A~uUh>ck*K_y^?S<$xaW zdK2U)2WyE9&j0uu*jCnYw;nd3MypQ9?kJqUA-U)yw`p}oE4@#P?AN^3p&3l(ns7W$ zBQraI7ar;?8MVX}e*XHfsss-a4?Y@2s27vL^2Kmd;&Y)nE?(gAQW?p1(>H7*ZJ}}p z2#Rit+}vaF*j?*KPGuWaB1vH8_;i6VTf6P3A7VpbEbeCh)7tdn5mzgot9F;K7Ljp> zXQ*58hk?yPQ2+rBn7|xKFnQ$rb<*3?>W5ks^j3fZl*5yz&h5BZW_J9@P07rWRIT45 zKm0Rqi7jb72R*9bgpA{U8ya^x&Os7-Z)1M#P@Q`mlZlf~{WV zk79m%#rtP28oA?oPTa$->e~~H;9RRHjQZzaBNQ8728=I$VpYov!~>khLV3E_J}0%4 zu1}0qcO{SHalMi|?EHxLP>kKkdcSz;yQw%kaMrMj``9L&@G%yycU~jG7Zf?pWV|im z=&^QXJ{wE%#@ko`)dm1RtIFX^Gxh{+6`g_>n=<67H*CQzU$lebB-+rfpzHfRa?q(C z^T=gu85Nd6I)xNmq~rDKI}tv3{9vCkvWk=AX~P#O{hDkLXs%jV=6q?zm86<%E8~fN z#W>H(k>FHwfrB$oD&H+*QXMm=dyMdX@T*4V<7ITb+B|&jS&3%%iAN6pg|(JaA~Y*7 zdUBS*vo=kPh?B#zz2|E9?;r7x;;!_&vUc+>cUm8VaT)RddwkcKYdZ*tcSD>oIJk7b zzxtfKggOrFh`2OAt;vvCDIN=(4W$RqnQlnE2HBN$w9EgA zoGXt+VWyL(2>2H<52BwEpbN1OEaxV_loK#g>4ELuiqRM1eKd3hQx+f`#vLh_7vsxU zoBbp!F4;k458n=-HKhdIpc~37q|U-O;H1mS%wXAWaex2R_sbLIPlV-(yt=(gGD2LM zO>->Zb!O^U6nTnwYQU5qQo=`cwxdgOt!Zo5xu`%PzJGC%0FHFXA-Bwq#v+4WG2av* z5DUf0IjX-LJYR^=Lge7LA@mU}TFwCN^BdfwU^9E*?)@Z81*g(0{{+w!@GuLbut^c9 zMz9DRQYV7l$}tb z0#xZFjkg>glF<2A%BoziOk5T*omMp}7DD=cLt85O$feN@70Oc?h zOXM9@+ha$~yfE6feD=W^*`q8a2P%DCENi8(wCmnpJ$N|8R|}LW;zk%M#=mm@DrA3XTV8j5cvz zWz%lrX1RHIH-alTwz!#%bCYs;Ke|WomFkz&9>w2m2TI$NA3!i1U5?d8I?+a=QvXMWq4};H@-#J zFy)mjma~Bt-g$BCFJHJ~ha%Ck?mgIS*omu4LrlO4q4w*;cne@E$YB%d&kdzQLgv4! zyW#I#j#4e&i1qGTSjUsxbAm6G@}93^qBR@YHwRx7qnHK}c1e>Q>~_K?;O&9=BYBHr z_gU;uR*vnivnj2U$7D4N;FmTp8!*$?@?ESSQyzJVo4dKL59n4ihqs4Z|1cYz%kEa7 z6~fbxx|};<->4&D(Qo;W-^Z}tP2O58YPzXej!@>rG4f6Sd?hf-s7067fX!;&!Yk69 zIN6J1OzQYxLOj=O7}*Hak_p24$3NokE&E4TZA@yS45#^Q#+dv-W4D#z9;m<`a?g%0 z`1V=3o4t>W^|T_cBph2kZQ{9C^%25IpIR(h%5gdBBs7{_s_DQ`I* zlqB1gw$3z`*OSkvQ}r|jpVbWY*A(PGk%0*=I8p&E8pOxE2e|wUcd99DTBL)H=u#Cb zzx{V%%T0T0o=~}5Y0y3@S zpkysLBSP}eHU-GnlttoZ;?fcvt>F*f9nKcbUqzwE{ZIW*CR#a`_!)pM4QgaDzGNNN z&FIuUBw%=vAd2F1&KuVN^U_BACcOyQ2KDg4kRl)B%6<80%Vx6wvy96SJ(8hb_hJ1OL%Jbb`{wt`Ars$b2O7 zWz%qpFv@I3sy_Am-0CzQqc^?CbcEgfGVoZ=8gJwzV0fW-I*bIR8X*S`ZaR#6EsNq{ z8NrGGmbejai(HEvZnx?STTrDcf6N*)B!JM3C$xO4EQCB%NntMD>Z`}%iq(-Vr)YHYJl(sU^{VXkr^b@bx$c#*^=9U z#mr1QtW)E3NdNd$I72e|2L&Ej8<cWt#i^eJg%#!CE1K9ST7hQ7=kkC)v{p^k#fMKQI97_p~dIn1wRPzVDAP@(5FokvC(I2%-{MoTC*Gayb0P>kiEv&^7WBFX$LMiH zwYn2 zEe^=4NJ}2L1uRZz>rTx%Inon%j+v8Ag*mPn1Vmuyg+mRf{q&vZ9{TbhS5-J^d@{T; ze_vrOfs=hGeoAMM0v~GlZ`0X5jV$H&GfrA8>$UWcDJ$>rCP1q7=E^mVb_w&BIeb^xCDB#7q-UeDAydE_`!S9v$BtyL2Po5CNh__Pu@&tty@c z$gKz#T&x_@n*!e|3yv)P4}e5!mngTqkp> z3*$$%W`&tODPWtU{ku;#sjuuEPhU)%EdRi_Pd;OxEW2+xoNLb@VUM1p{`)B6(ZDPT z2?QFzYuF0dh$BD@8TVZ#Yy@8D8+O|lBb#zU#G{@EF=r3?AQevCVcGp*2cL7b*2oUu zikJCw9!&D47bBRv)KW-BJI@tapA&I)iisdJ%13^ZsMI2Lm#*Ie_!%If+_I#y(Hm%;F30`Hb7^j9a@~JVN?i3d2{cc<%a%S&9Dyyxemj zQ?x&a6r?ys7JR{BucsHIo*jXpYohXv-FAZ0mk9G!uK$+;j zt|@^}LEPnsNIqLOdF}umsioUjo98kT)w0Kj7CrnQ0>Zk))?4*9QL9vbJ88h9TUK)U zJK%&PLr>9i^;2n0onJsvfsL*3AMeR+e{lRX%L$^hRI4lmPLs1~`zB{Pb6Q>$<5P{! z868=0yLp<)B87W6NT=|aWQbSDZka#jVngy_?;r|N4cW{_>1K7j*-Q7>rDVs{LkCQ^ zo>=wR`IW2hYTrnV^U+2?A&!|GNpSVae0{HzYuie5EU3yrvZpPVq@eI{YBLCN8M-P}MkhjL(n1Cq1U~;yLL5BCf2Jlm`Z*A`XoWlk+A6|> zWV~LnyaAlAHT23^TlQ_8HCE$D1fwb>0&H`hG8}Ju(DGwIcTZYIngppdED9YZFcbyoLKrL+yrlr4TQ}2zGLIq91t^uV zR}XCIIM4tf_c_uXXvRIC$$u2w*byNt-=V#o3yf=V=7^iZ?KNZXoE4ArLJM9F($fEp z2-aApO5Vs2i0u$;JH_Yb4ZNgI!Z!4b9Z|WYr#tw%&^q8MhloC?SLL91K5XhG@1w@T z2fqNzm{)Z+WQ{Hnmk4Nr=V`kQ5N<<%cb#m{WtBwCHoJhjWE zv3>o($st|?VI&dzV{`-=FoiXu<99Sx!aOIUM5|D`1H}eafC|gDiJ9dUqU%eh%6@fe zcNtWiL09)iv^(oQlp`w1CPPCFz#$g9-;zZ!C0c$M>D5l1jS`Ja36~$1nlfx$cqL1X z>U*+PkgZOQmk|f$qH9^SW6XaN_yiE(x(0;TTCIujUJiXYN>+)EiNLSyp2=W+J*bK1=-jb=&A7ohQ!gExHbgEEgDJ$CfPd7c^g{OW+ z_hwSaX+(+6Myr1LVx8!|wB!$y! zszL{vR*UVgN)X4Rl}hX7unVH^nG?7~mV(wEbitTBPApy#y#wKYo1&Uk(9YU7xP})O zx#L6W^#c|x+0`xYb5o-YE#6D5Tg%Y+shC+D5QhT^eRzLJ58=O9r0V~Xbmf6e|NsB> ze(x}I%`Id@xvwa6usNc1ARQ{PQc)DDPbtUV=ui}WR8-2+K~#FsDWVe{wkRpe z<{X>7f3Lp3zy0IA*Y$ipo{!@hj9D4_eIUC5hg!3ehl}ZVJXunN9#Ebo-hu!dxlagWjBAj_sj5S<3nVXzxCf4!(8=!k(@4vPRz7 zxwuut)Z(g73yC*mM5OI#>Jb+IDX;4CdIfB^F?M8{A=^pG2CTl6uD%mPhKCTF&scXuxK$^SOY6(LzxWJ-~SGnh6BT_4L~tyxJF27gVXB`j(}gql$5thzF{`-<9n zAT0T9q#W=JPhM%AB7Km37jA>+#mO{?oyN){^8oe%VLot??aD}MP(Nm?MCqz*k-O|X z(RFKF61Mp_N>gyww~$vfo#a_RV-6lT?5_Z8Ito+VqT6wbB{ZgZZ<-BCj$?DGWgeK| ztZ{6nkId>fg%!m3Lx?w@K-rsxZs%pg8&PZlQFNa_T(J$yAxfmHPjux}xKCGPG30tt zg*&(&m;>QfCaohBWM?S^HTN3vE2_1zBFssg-GgH76%TO5qqr`>vixC1Tsw>Ra!oHI zU?at$@xp&fTu+dsmI?OG^|3-&H?9EgIv51HkvClqbvf#&$z1?WA4Swy!h|T4HS|Un zUHnlKY)q=z;qfb&F-iXH#Nt;{Hfeb$pnpcyJ)#vDjAazsX7nZ7WOs z1MB(C$9C;T?(Bw6e{46hY1(~Ln`2~8OqGPb`s5|ke7ZK-jP4U%Qo z1#y*C0Bh*`8L(#qrl)(u)Z>6TzuHcZP^wl&0zz#qX^Ktl6R_eVdqK%s}1 zHaFxr?~0yX7%82U0UzsRbv$>KF23pD_}jlWD<5d0I(LdlVU9$RxDq#evI!Q15h-4j zW$y1*V!IhM_GUg?+4~1C{>*uOf;NRE_SZiaHWN$$Z`-g*3V5wPvF#LrFTWmlkP5Ps z9B;2uxpQd~Qu@F!-HU%o9lf`AE|IknPo(=vXWbo&fz$zlHaJ*5M{VPjy3BAslAIQd zd~LSk|5{r{2AloNh977{W2Sfwz2tT(7Tc`~0_yq%5IO1oDZu{a9DBY5e(wp)I>wk= z!R|1mmER@XJa7iC7`8B8r_l`R>vEw%B4xfl;Wxta1lTjf0!n;t`Eg*)gz~J18cp4! zw~bM(FCu7&2-q>npHXo9fERklxVXTDF)>(*9rWCiFtcG z%eK?&k3&T;sSsZSpr;c!xYP3y)X^w$9{D*X#1V^A6&-XQ*Z)@g(Wn6;)x?J^2O+QA z2@LFA0^*^78cDe@g7Bl^XUTkN1H`fV9T9#d3K2}XkR2ew)kgXkvmY=0zreY$N# zZ755kuDUDFi+_c zJz8#ivYI?9+_Eb4FCkFpDrDSh9`--p zMR%o2;`3}_Ro4{AS9pIKULsz%vXnUUzv^xPv+tog%;YDhu|?qXUh$;GU=dAiNENIl zV{0FR4nvkmjpBNT?7fKs?zOff<}{JB`#MPT6@R*TPVhCjAVkX>d5_8#IA~Z*CwkD< zsd#gZ15$xN%XR5sYHr*PhpUvU1-Hs<+g4Y0W>2+esdOY^sc^h>-2(+Xf6st;U-km` zd-=-FS5@{}QlQ(y=3f5njZpym zOof3fr;$u;%+nE4j~6#crEc3ap!4I3Mx$d#=-aZ;O_X^UN=wqoxyS4MqqKkG9kJvI6s*=Dyk zSU3-ZurAX#4Lhn1T7A>XO<1(-wdTY>_m@VR+pJXcA~9N2uR_C>CF~Pf=1WQJye6dz zylgfN`czmb02o6TmAY9K-FGs;^|JC8uE-P!Q|O7DB~ENLFt1*XQIvSK0$?GD7zEm8 zxVd$YCrv-)J$`x6BYw z-4MX5zm|x%HqwMfwE1d88~Lh74O>8bTUh;_a&YI;PoQ<+y&6_nR;GN_`M0v(%@L?3 z@AASRHo46n+*1k7RIZ57M4XvM+nZzP70+H;d^)BG4ljydT4Pkb^Aa@}aScW|{8N9u z>n&}^GEx>%j><^LVHyX37MWKo0Aj|;BksGEt)xnEoo0mX4*`kUq>`oqXp}j?5D9>8 zJ;9f2UrrcJSUn+1##u&*zl5}pG~YHdWY}dfgknI zgI`03VmUpL3PrMLc}nNus)>(f`Y5CCL@QljaErhatyqUS2o8rT@Ko#yps#kKcsw}6Nj z0f62zIQa(yM~CAcXn~PSVt>W6Gf?z>8E_@h=|K2ABpa{~_o{xH3n8*F1wb)0*IG;# z?H`@PZ)(l{pjv(v-bBcoYNI?I;w&KippOVPp)6lnsTK~8y#m&0=t&hBG=Ye5iaNva?~P5ZCLwHLK?d2LA?NTrrSeEd9%LRrd%xl zO<~+4`2el~LIu%F({g_k!23LT{_sTEf4_$b;q zM(<*ke+d-;Dg9moT&>c5o_?zaSR4`S_h2Cn^wiq`p1XI}z;}NanKG-Ppak_4W~~I+ zP2BY6WERuzbPD~4*Cqpi2yOL{s#^(Nfj6?25FvbBhwQe-5>TIj);*g6j2mO`J;!3H zbJ9CjDS+L&_@zpYT7w|%e;ba#pIi^HX*Ty7Blc@JsBY*y@Q@}tqIz|yQ_fA`Q1l2j z328C@)O`Nwg&ZW_A$>K)e~k_GEn~JuAw5V{xh-vwEVGUB zMz0GgieU69yl|fNbzX^SZDkwweyb>*#+T#cNuIt`#>`h2K+ zdp2N5WeCFW>66Y?NV{PD0P#CS$o#2GIIoi8NCVC};nB>6t*XY|(2eNh#XW$S|LVjm z1mr>L3uiN+LCgL5`jJ=3w2N~0%)zfzHkAY%-)QN*g#lrM!K7fGnh$^3Z8f$4g+U*} zfVG6!C)_Q=1$2`Bwq1{@k+JfZLt#Ghg9OZfT7K6OYBh=#(f&N6-+*v6jeoPffw^s5 zzl?+yPpWOrQ_kz_=U17u(zvJaLW(vTxq`iK(?*>e_mp)YYjZD1bTnf7o2NpD_|2o5 ze?MkV182)cT8A%T_~dJr6A)Q}M@WE7ZJF#umq#kSJ+1oVxCZ*l=-5a^z$s>m1%={z z4~0}xHdH=R0U_FcUc*H1R>+YecuMxmNAe92|9_FqrrfW! z=VrFk)6<-DGX^lpSrQ)dITQ?}bKH9Ejk^B&69T)_G~xV@3dG%#$$^Z<9ZI84TjN&= zy6QfZo&b${>8Wa?c%>D07IJM37vbk8yT`xQ(m(@bx^dd&l{U+cVRIi(H>bIuIfIE~ z(pACN1VwPWAuoAB$34iVd1QAq%o8YJTX(o%7)I3PB=X38skbG118>vG;J^}SNA>B7 zO2X*pDT(X5@vngcWCnJ{gxD$`;mrnybNvv8nh6~B9JZW@R|&Wa@-kE3P$VyN%I~Uo zr~=j2oh*yN@EFdDJXwI`dnH$kXs2eCGh=G=`SWU!9{PCCbQH9T1*|V z`WbWSanmeU=sst5IobfFc!Au7ZWK!7-oMAgd*{Lb49);n+#_5n}XU)Mn^|* zkSFn3035N)voG5>LW-4-l{z*i8B~vV-sF|yRQS2z@^5Q7Crxf}i1#Bc`52m!O176wlL`LIy4H65KvB{@1^sN^o zZbqbi|0&cC(gqudqXyvlT&2|Q*6ejD*s4{sg@*cdeWY{|+`)Zet|vc5glGvn8``invI-LS52cGcSxskbzdWNP zR$%MB5cq)Dmt&nxu-L;s0`AMrAkv*Se-^Y87*WxXQyLy7UV%!h^Ch5cPZO#F=>#5H z$&IeX0$O<_fkNS0!ekXJIn_lJwWO}y8g&0q>xM0@9fnb%Eb;)LT~8UXd_@=^qjR~`qn25c_k^90?OfA&_5kEP*n8wrJ8>}%7P%%4HN`MftHO!M2n4DbDX6Y zUC?{0d)_C4??4y;`14kpL|gar!Y$$Py~m=6DMd!nN=G|)PRe+#gQ+{lvH1x|Y?3H5c~P)Rh{c$G^3dy&&(Od>Ff!6z-FESvBbb@i?5n)k*)1Nbeo0Y;g7;^(d{JO?O^>u|KJuzQgubd~5hh-yZ7dzO1!_K2!SO2z~}|66ZFSa=w8#Op;mQrJs{hTE=vc>h$iX)rYy>+D?! zPi5!bPc#v^a=gTttrG+IH-cmyiXiels5Q;j%1DW|$7-lxMw%;%YCCKA%Toc%Z}Wrf?h!k_$Gdn_zG1;NGzEwv^57H6vghmQA(PS5@{uTo0|(Lx zVD)V7AX8;y?YfBqpV!T{SeKQD(mJsJ8cVAeqd5X@I236 z1;DSfRM3SB%tq<$a&_RiJkuc_efG!T9k0&lbQBORqm$MmkoJMSrnq{aNZIEYJ1;wr z5>N^>sAr;~YqKoeu|;n65d_OQLC4hC1uHRGRM+H&cESqK1>*eMQYC$4R=!s39697T zrK@a2`6VjA<}$)IdTt?Kfr9*We6fKz?0~q%zchn);+eh4VUl`9M)TFC5VzOwOdYd? zJS4SysRD7y32?b1Ea-dLt|MwlzcYVu`%&J8tNy=&xwFuorhh;@ELJp78ffk287hQi zW|kKPdD$p}Sdp?WSlT7e*#!}`E!vopw3-ZFE~nIzxT_R0JAS-8U>y7JG``Ev$G7Y1 z9e5`86npmF_&8zX0($L0O)~vK=jGd3S^m#Al&A-MBC&hOks?G{zZv;)f7aMND9|x4;P9CYQUsYP1I?b7#A-#rAx*GyJpr7OWW+j2 zt2FSr&AnlpC&HoxJX863dT$SP`4`Vagx6oZ!GM(lIN$5cXE~K3<6itb5ww7|WDu>= zg|gN#8AtnBgEOG-vvTA-jdT=;bFA0id2Sa$8$sN>tlj?<$pcWUZD~%)nna0b z-k!lU5F*dcIAB$T;(}{iMuTnvB2o5f|BR#1dJI`v><*;P*kN=2g)T~U%0*_nyN_Rk z*@Z1Fgn%6rFcYg7oZE>{&W^x`2V3Mb>!*j)Ii?MQe^Oq$)KFu$s1fJuQ1^x{;9Amz z)5%!+X*&CvgTBOPv)Yoq8%e_7n4&8B0M%2*X!h4@aa@PRLixKBR}x@?v&6{z^J8iN z6%Y|uZEQ2Rt?#gsg#qk4=iP{Xo%DEdq~iH6L;3cb<~row6<+Ii170&5UEU)Jkb~KUBP#w#D6s=);=soxlI0_ zP;f=*>MUrrzEop>J!N3efpfM?^tS$2!B{cZ+(UNV=7}g^GtJw$UQhzB?QBe$ZbclJ zl^ukQS_ zJQvLehBKcotGuo>vR31(&zhR<&TaM@8n*ZC8U~29kl-*(;D&9Mj{p7yj%YJ1rtxWt z@B(e-obk$w%g=Tig2`xZF{`;PG(S@H$?92FIx~xsxO?Y$Ic}xj^VfJHX&V zrQ;)QtP^NEG4zQ)&+zkW=hD8OeOGWI+?WY(fcVc7YA~@YOf&w(QLpX0-<38e(RkA z?L#wizMhHl>%R=~mi`nNHg`BmJ`F;<1!yn0O9}#Hga-ySTEMRqj2R`j+rB0*%c#L0 zw8(>?80#7URQHFf{Q2q9g4dEN)#1*6HC5W>mp4vNmJEl@EDt->AmDA}6%`JfgY@~Z zeN98~6xMwPcULkno*3CKin75=HPLu`V9Z_}qI~20({M7SW0Jt0!i^GUV)NYe)jV+~ zOf0Eb$Ps7&(LP~qur@tzf@dlLSuhmO1W{al4k`~0*tcx*Cc&oEUFuD{qXhhL|Cb~Y{9gAw)jw( zq?Gn-+x-IbHWW;572*-YZI62W{{8365u#h_-VdA3TPmj79Z4N9pfDnr`gcrHc33@m z(P~w)yX;|mS6}9J-;VD!?@R|qLVl^o6OEp>$MpYkN_>^)r2i4rWNjr-jJ?e~WCa&z zcJ4J6+8GO-bIIaC{Vy8D&E!=S3zvH;w~~2+Er>GlqWZkc`dZ&x490L@L%_Y7yuZI} z6G9u=@pwnMMOrNqRie#akIW~wSc0F5+?)TLP!aPyLr@SZznQBBgmW8kjdhrHl^VO_ zJ=xV_ey_#kEo&gr_VGpm6~$nBYH$RrUUWzqwb4yy#9r1Abu9?@Y>cn8A`~LC6Yy z@a_N>svf-_`Buq2`U`JfCM#2WVOnr7!}OWKRzlf zff64!v>$?(WPbNR*wUo2#c`^1zBDuQt#fJ0t})TO{garEY!3-+J}!xQv-#ZiU*9c! z6i!_7!RLeMe&4<4XY2)5Qh(;M3EUwG57oqra>fN~#*QGseVhtD+bS>7qI`BE9za3W zG3@d*a!H4Ht&HjK#-VPXnIjurI!oyRrKK1ZMKUfDYPNI))7~XUvGb(3=Dcp){Ob=n z-cYOopB>2&L}>4T@M~?ehpF6VeNCjmC8GlRTsDSXeVHQZR4>@_7QASU&7|@VH{Dm_ zu8IWl8{%#IpW(*q=c&f0$MFyS>gRn}jD#-y&ans4CijbmlFb)$Zvp+A_z}xzog{^m zOag1(#~l-RY&hr8mT4ZZ9JXIaVWh(NcF0_w=9zY_YVS!>`fnmrJe(m78puoF1= zyWt_NQ%*lV3jdu?TC;+$23lsA3X>etAN*oqW`&o`p7ubo95UDZ`D-$)z(bITaA|d$ zZ0FiRD=T~?|Nl1`8n?ra=(vH%8w5bkcZH3dpomT9j}@tePZ1w=o_CYI3r;~PocSB*`y=*tU_6`DvVXe z!iLvkulo)mz#FDIQPBX!y!Na`-#KpD!qC}w8en+u7#?*!Eq~_if_mK&oL4wOxKq5; zW5w=G{3@QlF!p=}x7foBCz^Gx4{nfE0N z3MGP^@+}k3qMipq<_D4zdKB{&6Nu};zLRDk){|$X56TA!U~~ZA&{v6b0#?P5gprQg zv3o@6_Qa0Q%mjogZl~eXVMFuG6f4W1;avNH4SdB_irJTWB#_@93hiKLD?qwtH*7Jh z0FAGV~CPEWmFLuP_t7noxKmey@LJE1h6U{xAfpkoFWy+G>8Yj zP=!5sUS1miRq1AQM}sI>|Lm^g*uT(}N#1FT^;-eJR{qY(#82RP|j-X`@hMVHH&!HhH%RZ@`6eUGdmj|vBV?0L5C7Ubk_r9Fx#er z!onJBbvWr$hOJ35{q?)g$$!Qv&;~}8!cRA{j=hHe<23Gc-79}a^RIKjSvOzN>CJ?s z60o3j=u4)C&NTCI9XR0**k=fD{DhFBG z4m6S#fu%06L4i(yykMnrx(OsYC$7!+kon(o8o3H&pB`9seIj@0Dj46pTX^V`YD;a& zmUtg@1_+nnO$g?eE)RM88#AIf{FyC$|6D6_uAz=+FD2$sU|e#@yCH}OSJ4b`yEz|`*rs0mc$LCFwqcxoXP(q$Qr-!xjs60Iow@^? zt{z-;-6~scbU<(fn;O0yfoBCy^P=7sXyZi+z`u>smGK_?#NvOO?ZvxSNSs>T` zjLDxnONSqi39OTFsRlk0q=knPu&j1PuvZmrpN)Pb041I(3RIBCDy&%HG0ZbH?0IcH zcJMriSnAM_bm1j#d=8gh8DUO=+A5N>2iQV9rveA+$@@X|m?lSKh>)qcWZr@n5w0!mC;_HiqEWYMTc?8{9 zimz|vH4{s60e8X>5zZ??vXqdjNkXgV&fbWvC+_-#Vd_kD@oqI|L5vUT- zyH%&OgZOR_#%Vv3j(Zq-j0p;<0lo%o&QQX8$N29W_#|z5)C|P`MzOR+<}GwOq!$hn z$g{;U0DPI4pV;go|Kcp=T)K(>Fq6C4_~q7(9l?fwyzu0|O~s^|tQ;F=n}1H;J3T^{sK0u9mBRm^ZvyJ5LV;OmHq| z)EpwTsCz^ap{m?{a5gCN=`LUk8J#~I(NMj0;0sw&XEjU?UTPq89iPp)qV&)2;ftDU zRpQ%p(Zj}=8CtB(Wx_eV%Zwl1{Z$oV7cZA>cw8+Wo>z$%g-loV;zL0AcE194Ifb9Z zVpS9wGh*j=5P%A0*%OtNV$}{G{=&zG#U#)%z({(vc99*m!uTrH_3-At;HNxa6F~`; z{fN-BW-&Lrx@mJ1d59&mZ(WQ?LV?CM3Z4?E;yM8nW9`rPKS2(5kX^@zKu0R)XngMZ zD)ed|5ZGX43K)wJ}$4pC$RU@c%BLGmTr zwCfu^ZW*O=!xCah^IgzOcd?-Hi0Qb|z2Hbe35be&fV`r+TK=q=c%oZ3^tvKov6 z!u2fY{!p5?ndh{jx5{vCq47K;15EN`;VS0fH2f$8uh7i{vBFs;=(;$1rtdwVCrutN=uHT#MP<>dt6{AoL0J16}DNxSb* z-b_}~{%(A~I&N;p%B`E2#)f}>gq!XtmLMkZy9L`R-ZjvhC4Xjco(BPq|G`;kyYuBp zC@9hhFwDtCzsAkNC`+8r>u((u>^A-b`1|VP0z1|?f1D$Gxw_gH?bcgnKzKR!_$qZ{ zV+pi#SWNGm=Omi1-GrRSAPii zIV;iZ!7!Ay5(TS}a~j)@73SBz9}sM5oe{u-_S_2Gd644>L{pmy*bg{E0yQA>+<=_9f7+R^h9OW(O&Hp$FJ!!3GNNlEj|l;>FYczgIm%Sjh|v}f z+!~HX@?NCl13x7A&QphF6)$3Z^@x9-$nd+{apzI^P;uq9C~J}-D$hZ(-cvjNK5#|o zIrZ2JeiArg}L>_1xXQgmyndSn!z2HS_SEjq@5} z7DIk=mWODI$33!~v-5c4EI&M>7`-!Bp3zAhwBnyFqQ({~rxg8aKch@>c#8d@auL#N(8}K{|E}GuVLaG!$1d za}+47*5*5)M~pFN!mF?QXG*~RV-JdV_|B8kn}#@R4Yv*rN5=U@uexy=Fj|!Odk}ri zZ!1zwLgX$*$OZ8cRk5!0yJ$i0_bO|7W6Fx7A`~>vGFoh__UITE#9Q|?{pC*&F(hE% zTV}ICu=iZH`FntAKBNq+9iOn*0*21(kRA~c8%?e+aN9)edV_Du;U*Pcuc{2u} zqisOo1>wG$u^&G#$a#w5;T@2Qf0w|oLXic!K*9nm6aLKg*y#MHO}`J*KtE5CI}Wm( zk@YeFI4(>xvw6~jES1q!9P|Bi!B)Lu1VGBpHj+do#e?bSR&JxXk`!95!n zpC6}7rom^x3T#*L3yhgJ?b|B|K_a)~rNy+wjBfn-Ec}jy=>;eo!N3+ubr67hue*@O zF{HtHu57WH@aGCRevZQF1WF=(pqj5Zv*gpWz3KCmrVj4qRPo*Pg=SfxW8QeVoU3ds+T`?z_y?0zq9 zh(MKukt%xR8r9)#A(a&l^G&%3?SQ>!qo z@B(#lKW}^_GJHAh#gtO9yNz)F0?L6kB!)Qbgk;pVGQDn}Ch-Ow%P!+g{xq4n;%Yyp zq=5RVP&$l&3iwQQ_(;40!1yHRvK|{qT(-@dF4(5JlW&`cOgdFkxw^<~$+?;FaH$cc z!=sZJMTykF55jZ}mV~%BLjHtjfE!s$huhxZF6Qz&RiFz`uRHO96VcCBxkDqA1{V-k}`Op z(osyT9Ty`Vk60*nv)3X*K1Z$(E|r3>)(sgk9VCk7xxTtkb@D zVg5|n`F~-hx#FDoaxU@q-ITQ?p*t^2m*vGb*(vX~*qe^8zg;71?`;PTCaQj>?T6Nh zmR8_{2^9Q^C-ed?Krq62Y&3+fP=mLNDilGe8Wv{FPt3e0cWeDGD#^Y?J#NZ*uDm9j z_C(@T^7q<2b(WQq z{k<(8QAH{&{`C#WqAlvmPdi`hfEV3O=byIp7wx-VnEsks0t{FG2$ogNoBr{54rLb8 zmip#v40$aLo5wnGH?%bK)R#(|so@JZB~my;M6kiMH=@za+A^<&2d3)Z*#o3^b_R!; z#$Vc>5O?;1ST*kI-aY7&ppEj|YQ+3ecdV(ffPfJ__ywbi4-lUQK}n?wD;e99N&J%l zgh%@pD9v2dNUTyX_g~sqaV45(02a|8-gSpI@8b}@d)MQeiLh&n*h`9?4}@39KqFxfaZVaH>+5jKq1g}w zJFE!kCC_`qUMQ6#nhX!V&&c9?=zNAy#U5X{Aw1>Bs@`EWs6FZOwq_Cd`hfi~V~D5j zKz`E}7@P0@Z9otn0pDF)&}GFBI*v&li9G;x3{D;c@L634^wsiTy@b0T!U=oV7`Clz zF!I5d$$ngEWj-&ASx>yAL%2rra3NJrW$jfa65olZIar1G`4DJMj;mGy%KHs39A~;K ziE#&aQE#)j0}8PL7O)PRqzynriU@!Ok{FbL-V#AQP|yHfyCT0p;fCZ&^^J|VPC4Toh;5Z@n#-j9DcFKh}S+(|n6WTZs=I)+q zi3P?za4%Q1pGPl5cR7Ej5B`E z{W5k20Cs9)saBv?zw7=;CtShL(hc3bR4i@Cx~Ksk zm0&?6ZW8!o#lLLl0}$<+WSDhN00)1T2#2_)a_c>fJOsS7SYT^FD?ge=FO>G)4f4R!Qw>Ux)1k zl2-s6(iVmcley#k{z`h5rRaeo(2vXKJcq=bP~~^_rC?qOTDxx@GO>BP{GJu?g~UT* zv>vU)UD}*;Dv%X>-6=P=G(tx6p+&p#tc!!uBeNPN)3pyLB5Z=qxi9@(BoU zb$~Yes7lJqwWM@N+%mjr1(Nosn`zrG{G4>&H?&J;KR*t`=6!w^V z5U8DTUqW2sqs`Vo?)I3D9Ym|cVlE-<55cpRM65@Y0e6V!)6Gc?_+~Ow7`n;{a$JGV zSK*;1c`AdIwE8-U;l$AdkCere3K*2N>UZPm2H=IE{}UC_%WqtkHf#PvB>QX?j0T{w zf{^Xxu@Kd7Bv@A?zU~0p!I}1QjFZK;(em<~INID~&S+SeFJM)ipFMO(=Km6NAV;2J zHm-61pbkDC9Bcny7rF>^t>=&4Ls#b1WbKrVJF~`q?ed`cTApax=i1MFtkt4`?v#`W z0?nlbH-?xR8tZD^OvJv?t1}mztfK?T6a2a=u_G!TioPb4UQmKZF5tecBe)ZS-F{m3 z_x&sd@5l^OM(^ySH5J+cwxra!EXQPL{QLK0a9=-mn*>pPCn>0ZGkHaI$-!S6Cq@Sw z7+!#sfSBMXt@x86B!)dOke%|vuzD6nbU{A(4)&s64VJxmO@#AZWu9GWU+}FPZ~+2J z&~t0D_R8;f^GF!1q3wUK<&WM$WBZ}-q|X1cG+1`|uPo$bQXv9%dogU49Pi`N)%3!G zonof<1dnxDKJbz4Kf$DblTD_8>3CC@&qS~&^**ldE6che$;cVe6et29X;_Dy?6<;< z6yL2#dDY7Og2VmeG-$sRhuXcjGsDNQ{RFg_D)LWfHZGP)oxb8kitPOsQe+8u0B_|P zC(|?U`sPf$R3$!u&l`cSk%(lR(dAaPa-5}Apd*cz{U{Z0WpLMEB6Ape#9~#>1$pnm z1W~kudEG{SZ-}E+xHx<}K1Ys#j^)}|PgvXA54_?o8~>J0&>mAo*NdoZJMzE1_EK4jCgLahemePY$H?t{2Y?1{>TfO7T?=DhEoP2G`Y@%KVQT32_eo5t{c$n9cHnH6m#)}g@3m_th7C_I-7x$ zRcOd}j>f()82dB-c?^qndj0eo2?n0vx91AmFM7aW}n^Vd!_4pl{x|VTmt-@nK1eHg@zkRdXZryZX33z7P`=MHRXWuVJ{Qh{|G=pSI$M_WzU9UAAQy$CmQKjxm9a^dxf}-(rfn1O9RTc8Fl^ znTSES&BnqU?U^5eB=#tsz;dis3GlvE;P1MD6!cS|guc`0fH8}lS1oc&=O#;O*5XM* zz=JewsTytB4;Uu?XhECY76ely>lwl`fSVtKVwV)a&XMUC z!gvBI*~Z8+2U(o~a{I;;pSwA>?Y;i1DnHIj_E+7otr z`?bbS&~<7e_fM1^BDfl1&hD@)8QZjaP*B(FUs1G@BOSmYZ*qhHoTT(ARLq&m{c$Au z5~U7+caUTrNRmJ=jSo=HsYnYTVetj$$c>6JwAo>>rfFtm-wHu;GJh8-|6Z#K zx_fBItLDaSBPtAGLdAt6(EGD?qul+8^!ri|1&9$VBi6?yE+#__o5%7EDXapH;(dHf z?=*PPgLhqdj2B#=f|sfe_{ic%NJkSt!a4$7eO`1aEi=rEKmRu&wh8QCV#zgTe=r{q z|6IB9oW^sLj1heOg1#5>B!ru`3;SoTd=cybViA_9bTSi9@<@aprT|%pw9hBk_ebN^w?Eq_A6)&wh2HQ9{;Lq07XCsk(p51!rkCy29t820xr76Jb(o~;v_6@~u)DpPo z_L*2ndA4AzWUBoX9OQjc9e~p%ffcJc0qH;);t0Sh<6WTd89_y%09KpJEN)!_Tyk6i zYog%v>GWM|H79gSwsGpM)|V{p(SW&q19u%2?6!v+0X^jA7`a*IfpZ?Ffh*K)z_rL5+A%>vhTSYPI=-at~Gp$?v(ff60+Q-%r{ z>>YQ)qQ5KL9hJOksP2mTlly&% zzF4U0Hw9!rZKg(6{6Z!Up4Z6WBHS~FIR|IF23#r$oiw?9dRDp;U>#vD7CkGGxEZE% zD1mAGFcUy7=9t6=e~EHd{`M28v2+1E%KCrTb((qYZ40}QiNc#;0O@#$_bX@ ztX%nj5|@;#%=2>H?10&=8ydcbEWdLus8&CZ!L~E#AhZ5qyASRneaqRPG}VyX7&5f) ziPqg1t!KMRTk6)#YpI7L!eyrJQNVSqdz4ec5E>5S?(Ph`|CAgE<`KWn7bEC}HA&kR zzZHBVbuBRFx66u35a}vFQ&sMqV47Zdx9=pWU5i!r|2M7hApty2DXpX}(->jNSyo=k?9Pr;2%Sy@c2kJifE|Xu(C0K(J6_IwJZ}2saqju`h zpla5=j6pMYd$}!Vg<++J4p}I_xl6GgA{N? z)^06#)B;Psj^Mw2@ju)C2>u?;ODy5d1(cp7Yhh>!_|68g3>E?GQJvGtJ3F)C-#3!U zNAs;z8FH-^@tju<_~GY7>{v)E71un5J-&m^CqSHVKbV`PEcB|KmaLnPj8G%Jy+iHx{oY>-_= z)QNXw`4h6mG5c(6MqVS;_z6R?;OZ-vy^7HUK*R0VFE4=aagE8fqD%;LpExIu*wOF* z6mW_1Nz08SNw}F+e<4TPshAj@S+B8pS0;I9YJ=c27`TU49|FsshW(@h5z(dyj&PlN zHJ%MOCZPIenG~sK} z5Qh-X(FZLG4nH1SxjvmFLR0$a3A_Q}-a8h0!c_i^#G1m3EoS59=helBAgU4WZue@1 z@jOkreT9Z}^}BW2+y$UI?N4gxM7R9M>6?(;mA3*)qsnL>w(QPwn;2_I5pf>x(&k=w z27c0L;+&^NDwxBcRUTvitTgcGW!`O;XV1LdY%3?Cf1*+YZ7m4;FE-|?2YB$!r8!AP zG|_t^oJ;D1f!<@+O-_%ioGiH2*SpWzN`Bgp?_7x=PQ@A~w=JIeSVZm62dytTvdzGw zmmatGf}=IKI)X7f=q82FvwHw`Y(PnBJbrj2LVEpm9YH2DB|qSWf|8U zuFaV}$qB#AJvq`>og12VY{@)71p|A|*7VvI}wCq!XNR!$8{n~Wku4O|%-knaKW%{n3 zvq_xbX#j`FG|pP`EMO5iNPmd-Jfl@F*om^jhhmyd<-U~1iZ7x-;>+GLJ78s&WRL&U z02V_z?~D?L4l9c}Db3wzLxSkelMNQ$tMM*;-4-@I&(^ zicUikCYHFE5t$CFAhmO z?rtVKw^DAr=0E&q{Q&KmzXN~ZFrXKc^cbROf8$)ED)Rr}MF-JxL`Bb#ndlW0SXe{?-Y*?#86B7;_p0DOl4EratPfmvmYAki4G#Xs~e&@lGvLkxb>pa602@s%x^*9!YXsux6rG?Y^|7o$UZ>#!$K zDj+M`D(`!QC~@9tkKTN+a5AQ#`0sx5O6Sb1K8UV8bw&~-NPe+-dU6-C(h+RG1U+R{ z<+~S|{2xhY8VKe0zVY+S!q~^YGZM;PDwSo%lJ-!RLXD`j2`PlkgNP_fq9l_N6-mjG zWu_u3g(PGdi70!v!OT4W^Zotbc;OAt%z5r}&V60i=l)k>bKTDAPfi`f?-6+8SONP2 z>)#MXkYIlb?JDkvK08eo8_Swi-o)D(TMO9fFua-?*wQc>i4|@KT>l+qxMSWVFV%}i zWz)kJ8Z(?;gjT2}m;%*sg3mRgWr@`%hVi&#A6!Y5)M=CDJAyN!AZ=v$)PFM0J|*OC z%#@@40>jAsFJP~$6rA`?P5ep?$CC2k{gtktf42yIiL^cweV&@c?-mT^bVko$vP;-N z#oCpUKaq_TDQH?aKy(@?eusxVV+$im09L3q92bMl0PA=+ARcWYL>S}Z*hb&6C;zCg za4`@w;D`6_70iy8G~A0IGcpAqJ9=T$v8z@QH&RAD^2;A#%X4`?Uyc)3*2%qK==1%2 z-sRp^mXA2sZc@hx)*n7itX>0JhS`4WE$+QE`ksH5!ZSI*lZT%6hRlVnW22(z2A~f* z=V+>Y5LIs$L0y`XX6S`^iWW-v{kLO}S7FIR_c=MQ+p z1=CZ)Wz>8K@87<0Ie%LP^opeA93Yz3vkXYg9Y7h6AhNd~HtK4xNK3f#xl5#kN|%nv zT+A7^K+XsQOD#%A0sV*Hn|%oSwM9=RLb4PV^}vrE*!(7P;C{7PBJ8rvR%15TJRm}7 zO4X4VdD)E}>LU@)HgY9SiOKNi#!@GjI75$GslFIB07-uJS%3%VJSJP>L3$2r=mcyl z^~^?r1HU!T+q&W9fD#}3f>1tKgFE5{zs+(@Z;^dVOzmH<3i;izX_>Ce~U3l+4!NM!W zk8YXc5?X%~921JIgV`MkCJACkgiOZGI`0ifS%yk(Y=uSV&xQ=b?!6f7i9R7^y29qc zjaveWZqkWYV&0`X!08V|hV1m^q5Wo!pI@=>U+;F>EV8V#7ZO-caH+~Iz9jKNocCUs zSc4fQ`o7WmuI`yGjIBcfw=r^(RT4|`5Wjm8u!F4Yx-k;r8cFEpGTj~LrHWDh^+0Dl zujHn@!DH)qRHqd8_fIV#kv5f#ttD(|#>(+1gn;Tjju2(TZ=pEspS2Q!)=yt?^6!lt z6Z7iD$uuGoaqsa6{aoNEg}v-KIQCkot>r{-p5p{ehl?`O zeDZGinA|GDStgD#0DP|X>HNo{fw<@e9D93-GqO=%;${EjQ*|ILii<=*5L&$8XQUs+ z$dYG$^YIZXLhrPS2HMdY<+BQe&aFkBk02;>jG@6=7rd(I6#{ZQBC5>1PgZBTl?yI9 zc=b;FrDyC@a*e)`=O1w|iA#TS>Q+Gb(MiR*^(Un+cxv0%Mk#g>++PXP7Qw@1>S#BX z->}-_3iT*G)+x+3Qt}HtdpmC^6nTfo`^x;7jUGwRg5n> zbsRt60g|bWE4rx^x1mL}69?8&m|B8veYb3VS_C-mF35r4>X&B#vtsTV`e)jf!owGB#~+UNWB~PCdny3WEy<{TiLmlS>92$JC?CsiV zFA>8?{;mu^RsxSFMR?XEUl%ylhxM(TTiyhN zlwuJ02XEIK$?Q|nTd&qKcFD=S^AXR(bZ!p9l9x#qYxjY?omvOBSMK;aw*H|9S%7&O zNBgF$Qxnpc$HHRB!)nEK$^+|WRC2BQ0UPO?I4oe~6wkG1EM0Pu_jWoudQ~5M=B9DY zK#LK>RQ}$`4$vR-6eMWs4!VG7w#VPAv-ti~>RNl`0E8!bglv3{d;ZOVS3@>+KLY^W z@3D^?aqdMm+3D$^^_S$~(F8~Gaxv-WRSg30u=Y_5>YQ-_{imc77#&5il)yh_qE2y% z#aaRf<`~`Rx2ivAP*V~I`;JByosGtf(*-*p99Cc~y;OHyqUSUT_ehQi!?l8#-{c01 zF-PW+RnzX$7f$5-u{vwKmXmKn;pxK9CN&DiT-WlZ6=>(B$#}HuSM$0BL1>sIc_{K- z&(@r(;y%5f9b_7gx;+ZrxQG;lw=};k{sq$G{2RoT0uiUA8w`l0dt^#uK+J zXLzYjOVD>{Q&W1pQDn~t0?iXN)<5KGf6_BSh0p^g|Mk|@6R}=AZEhY=ptyKXLlIN7 z+PlPR8&QH1`fnBlPIv}bpdo;;1Z2FHa+udyN)CdS1$x%Um)w0#iu>**MXQi$8GOT` z+_2B;(b&mt#z#NJ)vxtgqZr-a5a|h$xE9@g|EQO7=wAD=TPr%M@}6I`3Ll%X``r#n zQ!QZX3w-0Ms4Y+l!=5RENQ!quG#cL@uoI8F``ZVYzSHH0Kt#AY_}jdjXEBes z-0YIl5x9~23klz4k)aiS+~`A~eZeGmGAB`dDza9A@egFab&R1ltGZ~Dm!wNFRQGoA z$oj=HFvietpsAd(9uFRdaUhv2y_n$-t zm%GFp0dp6ly0_VjpA|GI-~`2e5!m@WCFvWAZ&z~avbsR}^3l#xnnnpcq-<-@Zy^-j z-h{>gb5QtBIM5U9XX>5YatsT$8N!5d=!eUA;2H8VKLhEXDHj1pGrDtfjR=7{6zwPS zmC{aU#Ae3`%rVUw!ORC_jr0X=x3zWy9bFH(rdcvo`%WCns7*e7QC6f~_t3tM^Mel? zPfdp+v}Xt|>u+<}bg0`d;Is@z=FY#e)ZYMbs(BR!89s&r2|%KE`|*8`VZZPuoD`{q zq<^sa2}L=|e_XxQy2x?ERDDqT-*5?X#=jbQRNGgPCJR!ZYBHK11G0F_AV`kw=H8Lh zgi*cx7}S3rA1O>_pGp>)|G)=Apk<3qFOJ3(W~zdb3XJm-c$)`xwS5AC{LeUAJrs2j ze~9Y$K>mjIMd*PF!~-N}e>Bf8`JSryQK6RX$Qn)%jEM^ggm3K{>Wa8?`4CxwFOnd<8I z8KJnA5sh)s0yEv6--MV!Ec|-pdD&Gq=v)2;(*|E9IB8R_u6lqpWD-t$5cxf}m*%(L-`cgfwe8G={bryEyYJ~eON zAKmP@FZPdAS}cxCft@WgN`Dg(fg98!a$K6cTS%?7o(8vkwVME0H92{|iE)YfCt5wh zB7-=JR&6jn_Vt5k|8F8~twMFm z&+Qhf)x;^7zb53EOid9!PxM`!cMLj_!=Im}!1Ts<;Iw)%iKU23=40jv(C0fcC#mU5 zWSs;?jp^Il)I`iAZEwrt)2zex>M8!J5R{*T|8s$;Q%+vY`7VvS0>>r zW>|Agp^heY7E z`*n=sbpxjfJ+@=5zqm8_^@4K$x@!%c<;4n^6K~_10U8YF+OT)crtjZ5w}}kLn}eir zH|YKWR?(ljyr)?vcxDcFv_NhG4&yRD6A>;AYGLbk>Vx3v;GoG7*CS!oj@P(DgA)w> z=}>Xx$z<^@{*eb-B^x)aii)1XY}Q;htd{Y6LpQ_wu!N{=6)$WM>HJP+uaIi}B2{&X z?uWgwCI9N!*)htT3BNrg%2sJ&c<@47X3UX(rp$FwBUFu@gV+lGASLc{!SAbI@ zGoON^0rK8V1ZS5k_SVBOFtN@}n10-EAxo=x#JXHOWBVA21_LfOmHJ{Ok%~IryyLUZSAcz zem88$)@PHGVECY{6Kq_8pDZ4F=acQl4_qjAci`7+PAkX)iD|$8y24xQw4gW8sUhw$#m|&PPkKkHF(ba)cwCDGf=ZRqESKKv((q{_h_Y(V~;C7V9mw29kxsJ;B z`p7e!ZJb}^LpR-k1^{xhVG<;GuS=x<<{S7rADT^bDBLqt-{o}+0S*=k7do>SwMXe& zq!@5&Vacr&7;Ml(P%pu$S--edb}{s+1WgZQCS4p)+s%1$C8hlHS9t6BItL}fE@;8} zmDuRF>^a3A?&!Ln!TOTyz3xbYR{vHBA^S+PITq>B}K!c1S7d^x%2C^o)?g<2y{#-#-G zFHFPq3WPe_>_FgRA)s9rL1t^agxJfjOX^Yt)CDgSrbmN`E~lkt-TJW2x-X^=U(ekc zr7PwW@yu}k_YQK`s*JhNnxl-5aF^R3*I4{`JKT^wrrzbq1}M9T^oIaTd=?u0>a1-c zWrj0*OOVc;{PPvQ&V(13h!BJWcutXq%e$uhTyTU}IO|=1+|WyCD2fKhqc*}gC9va| z0W=DT@De>prNtpQwcrY*KeZhDPzAU$TAAR(x@|$El7(2*w@q%N@-Y({@%ODCBNRLK z0KBt2XpFZO3QC8eLfRL}5C=f2{$ zEk6xwLI4|B)ux{c0~=f;m_{J=aH`#z<+R%jSeLWMa&};{&Ou&R4{5qmsAVGS7s=?Y zV`^wDWDb2Xc=tloG>g3aYrC^JOL}NqPNuTDjM!~cJ2tGvj9m-3Kvm|pbf~I~%YYwy zz|9%=eR(`$?gKpwJVLv_gG>W01f(1Xx`W|a2=xEeh+Fj7t<;iUtJTa}-fJb8-cx%y z1Qz8B$FNT)drb14@wRX@S>0b&qMTLCO0Cp7&5Mk`3u)Z80XSvz%2ki+Nc8)EWt7<$ z%^WgA{pl)xF^%WGGiYMG&G$Sm-Zs;0`PByS+*woxD`5P|@$ou_kr_dj%CEXGZ95vN z2p!E)r?ON3;mHK1jFa-98@p;ub=vp=%!2;j__94 zJ$$&}u_#RzH{4!Pk@?|DFd;pIgwt=ehH2+xr2S6hmK{7k47Y-ogvvsTQB zsYT+Nt{11m<#0_CIFBhRlCjX+9Rqi@Xb37A4}4dk$FRp3V`V)i`apx8`iHUUV_Yqb zC#|J^bqAoJH1=K6#=+Y#(N*%+M;XVk-1HuHm2af1B_uc4isDvC5)4CD39@l8K-;U5 z>G)caF4u@;ogxCd^!;u@B{10UrMow7|&TCcNiU1Y_Kkgr!|^i1(?ox-9P98ZczZC^Ta<3>C-x z##%>NF;k2KM!H_eH0Dvn91j_J=8bI>28*cv|Lx$CoX}UE1rFU zL(|m?g7n8}P$RtZ(^>@a)rQaA0~H_7XLlyv7$*xMAQ8)+FH^!z5I_a*_|Aa&S*Y&! z1yxedZRK=)Iujxr_!|yrNUemENzmxHL*ocL2f|y+?_!~KRC(85Q69X8O}n7(&UAfq zT&M8Z5SQcdy=F>yT7Hrm2 z<<+O@mqH)^+1w9@(_BtktWX6Hx%6NBjQIn{n;)pB{z*z7YwTDb<@4GGJ;P5M;K11> z%MYyOZP*ZkcuvK4ZK91cAh^ileV)bcKn*BMJE>%1_MAgStqcPqfWf2GCvN&|+cJ)TsAX6tP$-O)-(WFXl`KTDE z2u#K7`oD5Bd$JEE9_(AdOcE}RNJ1gt8mDt6b3pTN+&zXc$M3c4B+Cdm5ospEwl1+R zpcQ!J6`Md_HI&Bn^EvySQBTHPoA~B*(QgSS>TLIXSqxZL2d8sK&%?O07xYT(54ptA z1gkcD+Af5enGljClaww03|o~WeM(@~$UPxlP24DQz3%7MYY9cX=bcaMwsBWKla^ZX zgv@g^p4J-q&oa1qo{|XNjf|j!1b)Ve%U%HR~>>7+& zJtFN`Ol~aLLM=TX@X4Lm3!Fab?B}|>U<{+-QZT0U+^Xh$NN_A>qU;LCOQji2MV7cH3 zoIW<@(MHBi1{4FR)VcwdyV7;Q^ube}+xeQLcYeCeV+c)W971=LJbmgQ0iNGRtx`ll zc@5+a#zwJ*2K2J)g*WkZ#E)F0F{gv{09zhUbJ%2|@6LNWYi{m*WSYt@#H89p`UcHY z40CRz#r;#NvwC}qQ7-Y5{`rGx2KLP#a`#VT~T{N1CM_!8J&fvf~ zvs=deNjxD$+8Rvtn|s*sPF)l3KL%)rhtG~f_1BS_{tqHrE59Z6Z^9zqN%hCXo@={? zx&f!WF>03F(5|oUyz-Mo=VtS|90V;}mVXdwRV;-1U;OF#T>^}Tw+DoD_Oboo|H9U@ zS}MOE38KJrIRJi(ddHw?8_1>{A}IXH=@y`j#lz`m{;2nK zenGb%(<%i3*kPJg;+PY79~FVI?-iF<)~OT?e@@xRD0^JTP;ZakO~bj1oaGIdE%`YH+>L$P(((RZ4x&L6hrtv8 zOnrsPfyvfgt=)I<2$4o>$0JaHf6qD%38+iqQScKKXd}Rmme;9-7z$6yhq~jvAe~&O zba)NuFEa<$S5O-^i|dAXD1*ZS-!$g*ekM1i9$(K;xTgbQw~y?jtPJ#Skbv6`6eMr6 zNC)yR>dXz%dZ*`_?=@J#Aeh)|-LJ0@Q|j>^q(vO;^atpR{G%&gQ7?(o_BA(59#hB1maI{pjM=c^d38cK*$=N|6NXYxV9xChXW_ zv6Uo(w7lFi=B*;HcH$??QmnkvU0JOPDY}Wj*k7H#%hdxgAFm zoy_zOR)wNa;=CmV^n8%`&#S-k@5%SnK#|l#DlUziArlD8RKN<0x$q8uoBwx@ytjR! zZ~{D$^slLs?1A6c*4wovUh~k;8dP@zuU0|v2EGP%aGKYkG>>@&>a`e@jNI96N*a8a`zvPiWDlW%_TpB3rU<0nhR`4y$ z2WZrPEgBgG%t}=evyz3x+-r$P6R>mK^suZj?G5j3eay^I`ict}%FzHa7lnZ+#Xo@> z7ZsW0iP%XkeHt{x{gW5$%_^ZOg3jxd2n`u9@mGkwgim4=bpK;9l6U-9x*oiPtGYc# z1{1O4?Hk+au|AHIIaxfKN6iGxPU!3=-Zx*yp0R)RJz*c)bf~;}TZG1tn%~k_2@q%p z{Cj_WjxZ%?w0Oe&wh*0s1V?L91l68c!PP0d3w1qE^9@cFL6^=Pt$MfZyI1-Sy&Y1*m@YczTX%Ts<3=SwMKA$d}0`i zL3c3GAjVG^>;xp(hdYO6<6aE6!gZuo?TW_spzH9PrH=+c^50dFXMe}h#wNv`j~_L0 z0>({_&PNClc&gJDG;fJqsw64%T8+Za$5Fz#4X$Is;?ojwYr7YOk(2oT8F*?4RVF7= zZv*}@L3fexnLgqF6(FrT>bI4G&1ZxVsrhPk6UApdI1Xb0H+Zq0N>w>J-K_i882fXsU$J3gaGAY{3g$Te$AGQsS*0SsjUN4$!U{(*%cYd39Tckt5!bn2|ji=j%7_%RZz=hlhLHKTdky6foKmmn!_kWu zY2LM&2uR)zD5;GfYXq!sD&)X~d78W{z#(_YotI3!oAF4*COmBBxA@MkFE?0I;x zQKk0;#=g!zL*{X`yw6eqc|I?-fS2vG_klhLZ3D1;O|QxWMKQ-c^B(E!2=lLjX|~e2`@#Y+>BU zhtGUQ^0?9OBdXr&F}qpfl7MMN1J6mc&VKq6n|Zv`7*l2H{w*@=bADZMlJfMK%^%37 zHnO;BYiD7ov{Sr2BM|t+N?HhT)j7VFX2b5Nibqf8f$5B3>=E+(=V^m1P6D%bxiu?)@J9UwSJYZ?_OTYmBU+kRByuz zs>SkOe^OU`7j<4}vK}8d-?8wc-jU(Jd@g>U{xbsUk;NH=_3%{AYaq!gw;4?a0=fSl zxHI_H_;NB?m!LMJ^*2w5u<;Rl0~wBre$8`a?aJyxB+vfKaz)E8D0!cKckNApa7ohq z+dVqQ#=qANWaQx*9L1FHkuz}w!@9nN&o?gt5s9_GiigGbQLK(B5jzl^gHHeVakPCe zj~E`4qwB0u_;!EY0Y*JQSkkn~1{H$-7Al_ECZtpjkE|0AaS#AjI(M}0;B~7KC*^Qw zRtQ|8du(sPv^iZxoxHC#Y7G%oD5YGXrgJf;oi9vJP3KEnPjIpTI82{8&1qK+xy%{ggdIZEhl}{b_Bbg$!6d@rS7%tl8hh=#wD+RhE zL%}x&JwRh`rg-aFQSQHiYvGsw%k}8Tm>2-w zOkLf1nvAU*I4RVg^uebL0dhk=W`KK;@NH=W&Amm(;ECh%SL>bQmG7EZUzV{NTTKGN zMk6}JI(qND4RgJsK8rs%yw3bHm*@e1J)5`Gg@^Aq)D?2tJ|_EeI+c5Lt+7DlC#D`0 zEvntylV6|nB23|oK9-SpysbOk_FH<_sbOkjc_E+__)m~3uLuTI77<>3X1f9^Au@}JEDVn;FLs8yNgiZOox`&{Z?}&k*8aZir-|}$Ev-S zc&@!WLXCi*r2WP2sLEqv_rMixCCPkm@xuD0x)n{)2LEk{lmOk8tsG~gXsccoQ;N6x zO;-Hcp3Sbrvv%$YVv&con(X0vyt?W$C{|qTzjyR*w%fpTHutuk@-J7VZmILM%3O=@vw9@y@N7+pm3m;rmm0Mq`k?}`p z-x0w~m_+W=hR1`fR5dbVYZ(K6I>G~mDj6N~n+G(H{-nFb7$cNFB+F%-OJdFw#PbP} zhGTg#TaP(nInawp2#24}%6hseqe(|#vDSFp?EAgT2>N#$|De{&FP=2C5GQV44JwgJ zd05o>5OleYF!pcc(62QyF%A)w!rx0r#}u}zP5T8iV`5}8@sDv+lgc8<@Da&NiGFGn z)1>e0e@Z_jU+X~l;OU&ciSQq|bFgAr7b0ke$n@~A5JruROe`ptUvZ*(cd_5)lYdK^ z6HXnlNl4nh%Y5TNvCj>+#BhYt@<$T@c};JUaq*$Y(?;DI&+}z}7He5sSn=JI`RWMT zQ_orXW#wQ->6_xKBP^F8!PBx&7n4&?WMDpSqSG=v1aRt+^LEK3+vG!<@ta0F1{iBJ ze(4Wg5mgA$>D*hIW+;IE3Z%p@g|=zp$oy=;Gj1k0x<%_tvAQ8oU*$Hnj@7eL;8QO1 zs+%@`Av1W5vf!MvhTP;oevCfiCrOyaMaMms7V!vB_B3n&5xBhnp3KjexJ3v$3_&@$foKH?OO#psme$CJ8g!%6?JI= zp58UI0qWZ<;)hpNS=vsip2YQXOskf#oE4`8T?v^V>LEwb4JjZxIkf>BV+;g)C#`>z zeVYE#6^V%a$TQLUl96D*iMnXs6=?ycE*uhqJh#lt@42-U>#qx&ZaeZl4~_-5KYq$l z#=B71p_gbQ99y1W%cRKz4*lAL-5amVF}~#O=l+qr;VaWOMv6#R0ynvz^l9Q|*!CmX z8}Qs?2V|OeKoig1Aoj*bh>rs_FRsP>8Pkdp>6z=1?itfz{yFa7+|4Wc6yay{MV&N~ zK}I*mOliK*a*Fd185@H$Kmx^>5`;{^wgxwcLO+%`6nteh{T4n*4g}JIpcVR2L* zME|Q(3P`voQ^dln^GW_RAie(c=-)CnyrdXoZAZW|o@9EYaQNJ|`}7A9#9bs)nzxps zzYus$^;*E%S9#C8=GZQzA-(>-ZLZN7^UD0)VvK{YuBd27L^W%(^l2@@Me=}9D)9pW zViiBWbo^PXspk|Fd{z7Fq-d0|`C4wThN?_(BKeT)l9P^jqS@>+!~Fm@xeC*4895YU zFLZTsXeH_NiobG=ov)?>4P5rHug-DUC7!sjg+{Znp;@ooBZjB^J{)xEg~jKg zeLAQDLH&d#TCy54D7}FbxgU?)DFm7nZ&cH(M1YgDX&guVpQxJaTS(h)o=Gru$zL7t$FS+?Cj(dUlw`|LlznW!3{FLP)!MS>3sWLaNH2r$B zoF}}JhTp9_^for)BvaL9@>o7=0p=&4agOy5G$iEQo1_y-Jmd{_Sq96|lbs^Osox*2 zpI!)H; zBh_*6nDt1y#Sm+=Mk~*%un2o+L$#`92r7Jr{-5IMnr<1<#yQ7R(T@kFW_ICmUvyU} zO`u0)}jHP&GMy;%MsFB`uFQE+ZPAO-_8EnUGqm!4{DNk!pQ|nG8Dn#{8nt$RO=)>y8denfq zh5xabbAbHN}(8Pd3)j5BD%Oel%)p!3Q;y-b`ZB# z$UM(*KV~V4$0=kPi@Y`Qv;Nxn@6{R;gKYlnrX>|`<3GQq!`(z9)N#CD=c-#}Q0KrC zyUGZ<06+6!oeo-Tw{p+zi`E1BbCb$swqiskh1O-eXNdrBZ;O^!1wglDPp3o6aMJ@7 z+S`Emc<_3A;h%Zd4%lHg8txl=p7$pk zpRgZ=%xi!7DaP$RE;7c{fs^aWq^6Zi^}0-N1MOn@?SlLhM zetlph@kZ>^qbKB4mbbErg3W)ycH|LzM@gOtgpqo4#-D}s>OQ|0-KWUEgDW3pM@8yU zMiv$}-<~sH8k=Ykd!zh%k>4^?^+%;|{+ht;E%o;9D;I9^+@1dA#E&RmpKE?F=qPqE zDrO41!j+ieTcF(VFLd6CV{|e8i`SLSa*VpIb$+D_&$jowKh^=|P&f|P`T3Hk_tWBc z#|~a-&21{2D!n(6DwCDwk$Z-LAFEuL`Q-CY%!VzD#~H^^m}{FJ(PB{TSNfe^ies5L z2|#hTE(dGz9ZC(0;?N@W@be_{R)Cv!h5d(Lcbe;aK&rbxuFm)<=VSlYB=p);z#XIL znGXd8I_(ACg^q=XcX*ra9w)38RH^Ol4=wGF@X^z&XS_Qu<y|(-_q8NVV2<$9;r7O`J+#dM$jiy{8F1J}xg9Db6-Hlun+!F6|VuhioceUqC^< zW%uE`PX?u~9veMblP`kv+~jLZyC|7~gKhNTZ!xBb_ic$70ibnU36VhLH?7zfdavs3 zB(ZXNeNoXsl8n}{ zLH_LA6@mG(^J9i&`&*rx-{I17)Ed>yFJzocHD5gO6O-0q+z}iw6OZ~`$ZFz21JT2n zT@1OfF@xOkV2$*~jN@Y8$ePm@Sh_wT>)d$p-{s&*j<%rh9`!bVUbkm}v2N%HY-SGX zEq5Poj?L!W+M5G#XGrL#sf$;WqYsft57g0uRlo@$al@U|7D;rN4fYK`*jR7ufuL|p zO8fAs;HfqLLzq2|eOfdiZGfzqriVU7f(}S9>up>l_RrAJiJ?uPUkP{g^o-G=HN(Lj zXRn5hp1?y2&u?4G>-{8?J84D9PTAGooa!y!;ld=6v433u9y8t-A}f7$vs@@EYk4#I z-?f(*`n7f^H}+mc5kPDA(6YQ0SlCYGSDkvR{g%bfC27{p1sLc|mk-5oB?d4JW9({m z&y4ee5|0O9R>{M+4-?a1T{jo9!W0wMUBYChk9zJFJDu+PCZ3wXZAjUo4#ahKc)$gL{kG+n*Q81`;y5ItT_sySu z?G7-eHWL4)dcb&`&AYYvHNIa) z9Ng@^nRK<51z}3h2=9ldR%!Xsq7LkvzYniiN0}0FWjk6nR~4@gTN;?1uKF<6&9@Br z+A$f@no{20?>q6}fZ!H{Cbi5-L>ZKR(q!crSoSH(=#Vf%-#uH?O{4V21diL4%sI!` z4ohVou=gmF9J^953PszQ(IhQ_Rsu%6gqaO^h+z{Io;<1D(_6)4-(qv8L%s?2bj=(m zJK+hD{0q`~>*LXicyu(h?RqnciqY6LelcXhPAceJ{|MYw$L{T5U&G-cZ~QR%8+2wN zzY2tI0$f^wkl`btu)Q=l{E;opi7>oFMR|ksjS|~{KYI}EnGP7Y4ETvmc@peR9;%31 zW&qb#Gk+r3^Zk69)%b#}il}kBQ832^+sikHBQA3zN+utLLfyJr`++(_d-LIK)}@}} zoXO#_;m?s5+;yAB^<8Ty-s3L34?ABb7UtYq8aYHwq$)3#wodAuzb2BiOYoG#Dv4HTeOustgn>%nPg(S9?-)gR3-sgr$OLN3WxJqHe(F;z^JC;i9;?;Bm@WJdqk2s|vF z#4r&wjvRU zdfVDAY5fq3v^(@T)y;X|@{Gxam-l#mwSC)lIs7B>So3#-kB4Z-7pkY8jm??$T@Nfb zO%=L5$E(rbB%*ADeJM!Zwbzg`nzM&JxHW?JxPu;O!CO6=Klk%yeR0I4+C%Jfl2P>4 zJes%ND?%A0;4tS{^YR@qKr05UZ-eLc$^xcrsZ5w%*6i_`fT&CLQ7LY++9APO31<_^ zh3?E?=YCP#@xL9iPlBT}UJ|sX`zp_`l(BVp#XJ>PJbaV)pw^1vPg$2Q1goB+P=UTv zT;{0heF_{B)5pl~y0==Uh6u`|Y3hNU%1xgKG44rK9z6bAeWzuw6$}ZsUA=a9{^6Tnr+-AcenXSjy*|D^c z^bL{n;H@56vwY=LiWU4b^j&Zc@}iCVnD?Mxm5jS@xZRjUXY;&;4cYq&OHzfijI^Uv zNI|sY#c#aLtUg~DgDWP(6t8vxISLk%zO5e*ZT{8Y_ELP^Yh|_<3XNhdQB|t ze%hMXS-1Xtu-}R+h}pU>;B`$&TAYcx{ASjgH7i=6y7X?=RyPw=bqB+D1? zf0O#Dd3h?ME$I4Ahr0de_77?a0uJ+*436KXG9(4!=hJb>9ta9A*-}{jL?$@x_9x7)oHRP$E z*Ht5mZJF}%{j!9c()*@F44FpHca@9(y+V6-L72Vnpv*Tduq}-a9dZlAK~ELLv-WuC zX8rG1_xH_ls+tRH(xhKAPuiMe>P2U24311nf*(eZxUkWN-Qts7JmaN5WD(W(>KQJ1 z^N?WeM}Ayufl|`dgx1E?0$t&}4*}Lj*50Klb(nZC|689~@3Lyt#f#cW0eZai!b&%I z-+5~PTWnTDAT0v)e{+jE#9;Ws=%mQtkx6<(84jrTon+toGV-D2@{YGehqH~UQ)UM> zMadgC{J_bV5>HMEw-`qcmA0xl0ZIojcSHzwv?rBB<|n=i?#e+N`t$XfO5zts!`DV} z!WYLIaOk6YGxJsBSu47=2&KKj&GOO4fSHwE-J6NWNA0osZTZZnRQ|m7$SA>0&LxMM zMy_|+_3+nW1;M#pdzyKd^m{JH6>@_fuk33+S{JhVhvn=1+=(^(l0*~Aa&d$i!$@#* zvC7)76NkcBzY3t#;e-K2Tg&@RyJ%oD7Vu_{^?3qAs;OW=^BjU+dtGAS{TT&UdZ#ai zu~LUmU!tHsZXD5e`-sTX{-E)f; z)_|?e$a}ak^GkxJ;@N@W#Wj5!=c7tieV@kJnthNhAbq<@?f3VHVM8*5ECNzRM;1q4 zFn;V)RtmmK2oVEclEU(H`}{8~mMln~`!`wsdUIDm>4Z>v^0gPfEK!s*wA-dVea}bC zyt1=la@bx&<_mA9byCdx9~OLd5U9#4XwKRDmU>-9I={{L-|Iu|qr+zz&f_B{r9$ZL zoK_a3BEM5!oVmhypICaa{Z+%gdk8w!Q672zxe!n$AWv^g3js-gp(8*kWNG>7H>&-H zH!)w1)!c`!pm*egIu`kt_$B=0KF|JAm0$Y6Prq^Rm*E*7Wtt7y^nP+oFo;e(x69>a z5D_=A0~8~_&qkq|G-bXTWtC!b9If*A29S~6u=$io>1rgD01Q`M_##0kH}P1 z(U~Wzy|_yYZ&jB9zCWrxgRrIEdKd+q9C98hBeqP;1o)_kpAJoa{6x0a*t;yIVaLeK zuokZi;zt~8bK^^DWoH*Lv>&sp4|uhb-uIwGroMLW;CKfW zJ*@5;dmefaVoO&36OIh?v|ms8M4Qr1jyWQ3=+O6kD@9RLVaUZknif-Re3Z1W+e*c;buHzlEAL%|@pN+EB$BWkbsF!B3hqpj$S3TB`4A|x7mT|{# zYF~Sq!&}dK*URv@b-QXISA+VKE@28b;~XC_7PpOxf;nvG*}E0@rmtY+I3j?RG(9*~ zBoR;I)k;Uyl3NOJryq}ouim%+Q3OkRypFNk6_ou^SaXC_Dzv|$?4!au@v%3WQ@$|q zLtDm2xjMW#fYS8WCImgQA>y1-S`UIK_bvmTkL~T3VL}!;wFg9A%eHxm1*0UcP|;$NANn;UNWonGb-;K_bJ9Jj*_nAy(Eg?E@U@xS z=@KE_3>&FA4H#iBn3wQ{VWP_!ZH*$r*#yL{II{}-`IkM+N) zHJYiTOERrzbfeMg?@Z5KQ7mILtyE8=)Sq3iw}UJYA|+SQn0Lm7AoLz}_L(FBk5`C? z3-aTRxCM;6{lN7exnI7TE(|F^PcHUre~red26<&hXc<=2hiQffL{+&~>3$1JKUTQd zyq^53TiJVJK7Tl^nN_5R+qJRPO0YJZx46<_X)4lWi*Vhp6)X3QEZ}1thXhhnt*P7` zm&fvo^UzX6q+R^-NNe7IdHG~fK(MNJh4k&ibCp4rE4Llvd;I@$c60?--VHEK4V4Hu zeFW|y*YHT}wbf2p{IZ|;0`~#6?zrcjkBGKmo+b_45~G|kj_U86Nkn1K`v(K>lB=^% zM?7o#i9_t&qS+g1-Yc|im_HNZ&Tl+ct5*?>kJS$vG_~!>G9&w$goD!Clnx0=F)-iR2B}Gj33h-j{RF&Lfnk--8Cz7Cuzw_- zE4=ls&eY~#<{i9ePg6#9SN7Krl8T~>x^Cc+?qGS8)% ze=Z&B_yLSDk`-(_J;EDNbd9b2RWys`!d1^b)s|KzQL!Kx#&JVeb}qu=itt4H+x~~- zc5c9AJb(lY_LL>v0_~^BG2^J;Lk9YD?U=pGZw2a+;UoM-+~c0~^u7?@{zC2hvD7ye ziJyU>4VG$CEiWJxVmxu0seVsH=s6fvrL;x@QU*BuBk&n+3dMg}{U}AV{k=CtwSD1G zUBP&X#bkGLikTW}<-Ag`Qu)HA?u*OapSKr*Pu>d1n?hG+wK)LqnQ^sFUmicN??bo6 zd1`m1B&x^Do_;t06*;R#jx)ce2NgVft$)|}4aY+sPyWHOK3?)q#ro$Pc9#aRbu9cb zR?K->sxZE*qi_zvj5_CHd%;K%;kh)t{l=zM6!&&a<4>bEk$se~Qv%m@6g5nSvP_}X zHpO<)!WIW`QT{e>JLGZ`jsrWjvuowX$Uf@9&vB{CfGH{oH3vmm9?oJG3UHKYhmV^H z;$P8lM!CrcJM{1z~>#JoXVW6+bz_$nZiCvV!6+C&tSnOpNUiI6TyP?FS zQIz4SFEX=KhdFw+EJny%3ugvbbJq#0i@vo*>8s!VzO;Qj;V<6o14#hN{ys$d?_GY(EIKtyK@$}I&>?<3Hzsmcs;RB3TF0L?^A zPp`OJ1~gj?OkTd>2P-hsnuv=edOkCB#>Z6vzqRU*ld)lwh>4n7Pwx4m)x+~Wc4{M# zT9b+JAkps?%gTLSajYDHMi?M96RrP!)L*PfGcu%Bioexz90K85a%83B6)i2TCuyVg zt$1JHDUfw=iW*jSV|2mQad$T)cUP;XV76lOe;bt>sTpD)c8Vmf_Ai*5_WNHh@d=RJNiNaN_fjA2}jHt2QyR#(uNNoCJ+D?YpI5)qQ4f0B6TR? zNjM?rmHr>e@I8bFN0#HtVueCs3u60|4K z!SDiaFeGxq{jV#2vJ`0wH{b{BYFOUSxZbudgF?-eY~&75<>si__EN0$Esw+PD^mGR zLUxPQh?9rs@AzP17e}aq%T5{P?7xWV2tG#YPfj*G>3c@p8cpU;>At+JO1=7%_r&R& zUq&rO;K<9FdNxI-UJM59AnGkFM-8yQ&js)MdeTx3NeF4WGwFxXXrbk`Fylg!zV&?| zcyBOt$D*)&^<7bd?2~+q1^s}2e-aZ&31vnnS0;ep0bDTo0LaGQ$)*>jERI#xF1*rE zU&~mL(Tk+y7y;#?ovk_U5wLzs$r6h`^RwtRG{lb(*)KB7`}Meh>c_{64!J1p9y(C` zG12^1)YbsP+#KFUn4uu-(BC6I1d>}MjOk~P`mk&Tc+}RjEe7gwtIrF+?39;QY~VH7 z%BiPR9H?8mEljWZX!tMYk*=d3uo#BYi`4zH@p~jBId9eTZ|23FjnypbAge^ol2EA1NrWr-juX0EodIYB7>cvz53 z_};j}kcv^5%9_Q~MC$9LX(kc?!G_w}-SG5z`=04!JS4c2P6Mn7P=5o!?YIh4a>S_y z1?$4wfT0{4c9wCXU8RX?-)gk<7n;$|2npV}VHaT9eZ z{t546gtyEDU(=JcrkO%93S;wPRWT27VF`Zis(0^(gwrOTacm_+a5F>F6oYcTqWFq=ut#)+G&KIckoh3oBPcz|m z6?FQ^N@>c7cDURe%X|b(IXbl9$ z{TH-b&h~9m!xK6H>5rJ6_(e}9&Xq3@Xtyuk2RE=~s;~Xt1BD)aI`Fc416U9=&SwqA zONRJBqMoCtQF=+{SFt_PrAhDome~{FQikj~f5GaOczF};o6HlS9#YP|hrg&K2sGqv zp-t1%!=*cOrbrgAz2~CfKFcVCD{uH4lFNJ=LaFe91fas`holZ|E)&BmN@~o*6sjPy z#FAC|?8WA88F!GKnd17#I{>vIh-L3{(Rp1R-{D)uOvixz!gezMkI=F|*It?RkI}jd zE%yEX>Eq66Nc*hL+sbC6`?tGhNBSp6UI{Pqv`#)Zm+L6&Q%jo^jbBfn1{GkS{Fkr! zLWy%P=jsT~z+gbZWxEL$3kB@nJws0Y!j~hhy!mu0%Pfv9Nsyt`Mp*^S(r9#92M5i3w;*i&gzxQZ}^nwK5lJ(${SkM z9C2SS%j(dF3<>a*b4aku`ex)1DBBXip;tilBBpID*``WFnl(Cdr~0Hi05+(2A^ti6 zJFkXDgn+-||DA!EYnj)@QgMGs79JYnp$oP);aYnuMO0M}aK^Li;c$(`q1xCn1?+Jl{kZB4&7eXfl%%?59*;+ zGK~p{H3GoVTQ7%CCQwa&jcrnKe((e8r|FXm#?OjX5v}<7$Y%k$c{wkwLK1*g+wVjr zjkDF^H{S?FLmvZ%zMYKVl~!f|BSy1-kcR@p3EIwgwxmi{&>b~W>t~d57Bh75N1ZLn ziM_Sj^W^NgjSm>UO3nJPcq|=iY6SLykY1K}5yOM4HCCw8(np90gPR~*mA7n>p<}2y zJ{_szCgob1p4a4yL$PTk?-aH!6fciRGWa?24OV8W$0Q0(w>d z{(2mc6yEs1LGN1myv|k0CVkST5_MQApN7p3COd^SL_f850`@pO0Ef#Dm8%>f1`wUj zf}j;|M1TARety~$%VHxgzyjPtfCStL9A>W-Fx^L7#jBJ|Z~;zOxg2i9P-OiPw7h^F zDJot-cnLn-R%XYtB1!}NMRi3$Eb062=p8R|O?V=mp*yqOz?QG0EPY7TcBKg)hp7c+ z0hotC=rO6twLX5Og_7#d*8^CIn_pCM=BB%waeF1o&8hB@^N8a;dDu%uKVuWGjwmJb zBCK2{v|^26Hi+I`{8wrnQuYWclCliosSmJPT^N)7`ayhcN%k;;Dzvh)Xj~-mQK+s+ zZ*P&6v0$X%$@BrwuJ@dJKMX#eS-dnlP@((#{lzcAmDXFqCZk>S+C*Vx;fl7T9{6bl z`hLsZD&Dmb=S@L|$U-991En!jn3vBU|0!auWLtu)$fOD;%4zqRpXQ`2Ke+3j_{uEQ zBk3Q|L4u9AOQv47h=K!jnS<|)r|i|=KPX-P(_*_{5fXJ4ctECl6$>AOa-w^;#amRcd zoah6Mw&-}>+(8O1xuZl70xmH!6(W2i_n!lM?ccG5z-|7*8&CScfznwS> z8b(Ug#utWBLDhg0Q_Q!A`!}kEtnWi>qWfXbD7%+<)zzrAeD|~q*f)!4#AP#(WMApG zHPOekodbj0in|K-fsDz7(JIz)Nh-M^CtDK2<tM z4RDG_AWrpBM~@2XY!NA~sY)*yScD?!hpiLA;ZWDq5BNLoWyPE~&3A!9z@c8DhI&?a zB?(3!GQ@C!o)`~7n_ac}lV+dvZyrCHHtTsB@TxGv4S0-Ne0WFj)ki!274#Q^i+g+@ zX{V-&10A$E|N4hyLf*Gt_~h%tD*8Gr7QXYw_qh^cYt@t<%IQ)(jOGK_;FE?@1Dv*-*5IW9aoD(Y7nRX7sCX`mHb&grPi{L3A^y3OW+Ms%^gqGC+6A*SI|I*5ZE3PkmGZJ3R0I|JV z|GQ#opT4Qiv+hhtXBNbfX}kh_B3643Y$>c?{MK2JYifr7`1m&`?&MMoW{uWMM-sxFW;%e;g zKJC|PLbQ!$=qpQX4hP09|E7CH%~9^ev_Rz%Izco|MTea~k}LAWOGOy7y08ec_r>+F z*WC&gpSl$Ws#X5rt1aM|-bI{XWn9(_sJ?bLmJy7I>&Xj64u&WdVir(DU?mNnm!ssz zNjQ~~$stFZ;7|PD_Vv&9B|k3oMgsLdO#S?3Y0e0?&&nRThB_&F8q`waSwr!h{&Jb- z{37`0%^kG4R$?L59Hu!x}nOE5T(f?!aa5SNBUPkg!*2 z?IE!D`={COdV`Cv+ARuis;%3uj1a-N=s-9dpUI4F$XI5LU;%yv1x|OLDyN4SHDgRj zDM2r_&EbYOWd44a<`sOn4O}AzGrZSynzpHoEB${&q5CdkCHi2@Nl$?Ks&GaBLa2H8 zvJLqc_dC23(o*4ow2>C|!BaxMfMt|KZSQD5 z!Ql(1JwqbC|);Vs$qIn6m^F# zF1q4&$ej#=sAo zBeEw3%$#=`0^D!!n>-5U$EJLkl1;ETe?fR{v_7+{-G;pxrAvTWQ)3HxB7w+mw9osZ zbzza16C**AfH%7;e+Dpf<|P!AX&74+-X_dh6K)UDOagw8F8&OtxK#V6c~#}rCGetZ zq-L`5emFZB%j0Rn=j283wqoY*vD5$H}-&33CX zVevyIz+-buic1MGStK!&jdABY(#@9v52jCL$98PTjh_a{olg(rPJsm!naR~V zmLjB-i7KgLrzPrTy&|X_|Hg_F!Cowf*}1CD5JIu5!7F3wv&x^=0?i>ar=lmn8BN-A z@=kwnV4HTn8Np=_v6I<6i~gwLh*dAMiT=!CzFF@+>xK!t=ivzO&D z8cMj59#y-m=hM>XpPiQ#zEX@^oz1*v`fH~A=IX>|63NPBM$3b(9OG#WV0K8j3QVJ{ zeMxpyf{EzzlVC`OJxCc&qHi?Q`JuxJrNJW#p&|*wpDzFs`mwsJfBScql-f8jZXQs+Eq3e1QKfr-YO7om>Z?rP@nCNb1+qEwIwemkDs8V{jD5-!s zoh}Pn|1)1vLQ{pMiG3lpH58(F73@s(CGk30pij_VWK)w7aZ%6tJ2}_ZxOeU+(4JFP zDZNv>?@b!7;6@Pb4V(P7a`g+Qr{8^r2Gp_wCs0g4b&v*X`ZmE9u}AhzKiaD%tO zoV1NsTacB1XdUFM0!*15v%#S5s-`yegQnYb|5a%BU9LO*v&9(%0^eo($;7Q07GZ8o z#X9Sv+)ryS&p`U)ITR2WE8&3E^B+jGAF+RmC)l#?e(|Z?g5YF{LQa2*Fcg8V@d5`g!C)t%%=b zG;A79>e4X~qVjWN-~ETW`t1HsBQ@z;x_Y56%c8y=-9*pEuaZOgx{Nsru!Rq&kxe+D z`@<6z1j}1uO@EKm9&NRk)(*}mTKJ-Mp1KY)c0H+Y`28PU7#@( zcC^J62aFh>Q+VVdENR5S!d=lB-|bAM(hcx4g!V3$EFb-R+LQm0CnaSz%PWGB73fbAmBZf>q2_p~D$b+w{h};U6$_)xmFnrfLGxmEuHn*DnMkL?eMd zy%yvIF((4YpFu!l6D|%tNPr-VCGgw*o*cG6=HG_ggRm$WN-=e0A@1C!d!^u8$M za->#(W4OoR(eV88=cI$1w)vGssNx0fu39m5v#@y*qqqi?luE*+yv-(G$R+zom+CA)1L;D%H3o>$@ zI5Y+@AZ09pbEt>TB&A)e)Oaa>peK~T$0#-SSe;K^RD@zt$N1nstHbbMkJ6@=10vwA z23&%{C8J^aG?PHos`MIiK8ty8#c%5trg0nrDd^HVMMRxP3{L88bHVP7NT2)5Jp#OM zZ2MfdKaGjX7A!XFkoq_2p@rJY>s9(b)b#~7KR>F;9{2uR)~vQLruP+QfO!azauU?< zetqWeF4}BWBf61rPwAWG{VLf}3$p+G?9)co{y?9R3P>K)$h}(y zFJE+CM1THn8a`F-g#@EMA-Jo4uq9W1k6V>yWj3U&<(S(V9t~0;nIM8B7zT}5oSwW~ z5Z>ROpz=ECJ*c5vJr3LBxJ}}?ZaMcU{Qj8h-`BHYM zaBR3~(x2e@-cN+oe$OItT2wLCgE*72RK%4WI0xiCevt71^?4b%QmHg20`tD}4yp}C z6wfl{@u~}d{ry7Yb*6q)ofVnAyJu=7;moD_P za)9eC`?zp_oW}-RqzjYES*Qb5!3E#>B(cMvUU7qPEv=h}`Dw<|52p!M5RT@JYlFII zS`D7RN~;y zhjb@h=~im!Q%M|x2UD8s;FeRBtMGBfNDw2jJMCddt@45GiOnf(Os~TOrVbmUa{I4? z14&J-$3#Fy;o4^;Kwg3rAvI1YNn8PAZEVjgo2eUE^>IKP<0yw}0mb z&BMIusiJ;-d+GSVj6cAGUe&j@H0L3w_r7#<*w^guE}-J)zw1N+uIL&{z=5RUwg!F= z6dNlFPsVb|C2I%af|r`_bU;yIYK>G`%C1MZ+!{0F5aRw8U;B0rv>38-Kd{V|+ul&g zCMnTlqwD$1U1}d9LgOa*j@$j7*wvd}ToQ!VZjKts8%GIJlXP&# zFWmjy<0~Kx&0MDe;&SF3e1#?C(Vdrk6nm~@&Gm0ZA&cbCGF60;9%6Wo`mkACi2CgU{`&ZY4_grc&i|dXW^WzW*1-j}2Mu_jp8QMJ za&f~gtUBnREK$r}6o+(WeR-oZ7aof;&FRpZa%k~I*>e~C2a?5okSOkMT#x#*`)5fu zi}>bc7`m1p6fJ2((>jIztGUR8v_p~lXbiz>GU%H2if8Td+@_bJr1#B?MpDQ@v7X}} zSDV7|T)w6KV$c*}Bqm5Cwirj-?%k!U3m_%$mj!NxI)`x;DSZ$(Q>cA>_3>`?QkoQr z{abLDK84!5mZ)me!Nzi-%<{Pm_0I5+Ejn1U^e&sh+i)e_^uN{kF}=ZN!R?*lA%-3g z-IjM;9_vW1Ipb56eD2wGdbVchW?JrxP<$7o=)e^K0k&{JGE!|pu5UAMX8Y-Bs2W8c zj-9s9U&=ui%WrMRLN#VzEv&S7F#-o|)Ajiy&stMn!bzO~p3#V=Z>Is?O0$kdJ)b?k zr7(?|KHXW}Y66`avA-x>8H$Xk+Rrg_d-Ga)pxWW@c8>ofWadn zh(b_UZa#>9jIQ;^0Kt7gWXr9(t6Qpn2}P;&Gi%!oau6JiH~Oz{6LC!mD&Bx7QmZSn z5NxuuCy0)lQW{*A7^9r`zShvDVQGdi@$$Ru^xMv%SWCBF?VA_ZmcHhyW^`yoMAEta zMDDZx>QP?eL%@INNWo5Czl^%YG0MfcZ`z?C#SuNyV9O{t!vt@%nmLC60aAqnTzR;U z=~bLKpwh+Hgd0J?WgitNlLgXTl`3pE8)XHM z5^S8T`*RmzW`>edxJ>W zUq^cGUZ13QW**0wbRW%;4#EC@mU!iiK-~#OYTO77cpKhkob#R_2IQT81l>m-*j+}~ z(BMAF1gT%mqby6(M+Z7WHQ1SRwBZtfMuG%iaSnma-f4)U+H0zNh~*nt5A4cx9;Ser z>%k>#r;i^v-OEBeH^4xNG%@KP$jXu89-(i=LgBZrqE#||d z_4KF8RdpTionLsc%$l2L>ZX?+0iEM%vWh-Ow+YI|L5V{>6EC%u*v+kRB%kn?G3VWa zQ%2;R5&ny)^sb$0Y>ygw`qAl=XdJrhp(nqDV;2RJ#9u<9%uT|h8Lk*O3Bq|nZ9ZO= zkvF@LO_gcpDHqq4Fjg&S$};ONFG;>-tCLR+Th3-n87ZTfbONyZhZ^)kr``5|) zi2H3I2l~iSV+HwCQ$<7})E=y*O(Gq}kxIK7dAX11&J%>6dxw{I6X>y#Ik?f06cYxBk(h&MG(q)SiPtS%WnEv>y-)=b8&s?cVn?U zSf=-Hk$Ikq2Cr=yw_+Qh#(xahiP97ULWiJFeWkyuy;RC;omV%AbO{XPz=-taYUxtX@wtf2~U)KR@Eg2R%;&PSw`813MqX3aqH)hL?2W4rG5+A z|BNC`k`o`WGHmdgPV)N4_vo|-poRo8Z|~zEu4wg}yWHkY=>#1@@>k+R-o