diff options
| author | Oliver Schneider <git-spam-no-reply9815368754983@oli-obk.de> | 2017-07-28 16:48:43 +0200 |
|---|---|---|
| committer | Oliver Schneider <git-spam-no-reply9815368754983@oli-obk.de> | 2017-08-01 09:56:21 +0200 |
| commit | adfea61665385428b9c9aced9442dba65464c3c8 (patch) | |
| tree | d0b1bf5d67af2175cf65737c60be83c57ef8fdb3 | |
| parent | 7ed706d09c68469a7540f01efbfa5c4f6e3603b9 (diff) | |
| download | rust-adfea61665385428b9c9aced9442dba65464c3c8.tar.gz rust-adfea61665385428b9c9aced9442dba65464c3c8.zip | |
Allow machines to create new memory kinds
| -rw-r--r-- | miri/fn_call.rs | 20 | ||||
| -rw-r--r-- | miri/lib.rs | 26 | ||||
| -rw-r--r-- | miri/memory.rs | 16 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/const_eval.rs | 12 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/error.rs | 14 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/eval_context.rs | 13 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/machine.rs | 14 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/memory.rs | 75 | ||||
| -rw-r--r-- | src/librustc_mir/lib.rs | 1 | ||||
| -rw-r--r-- | tests/compile-fail/stack_free.rs | 2 |
10 files changed, 131 insertions, 62 deletions
diff --git a/miri/fn_call.rs b/miri/fn_call.rs index d8e92e7291f..cf3464ce3af 100644 --- a/miri/fn_call.rs +++ b/miri/fn_call.rs @@ -15,6 +15,8 @@ use super::{ MemoryExt, }; +use super::memory::Kind; + pub trait EvalContextExt<'tcx> { fn call_c_abi( &mut self, @@ -110,7 +112,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> self.write_null(dest, dest_ty)?; } else { let align = self.memory.pointer_size(); - let ptr = self.memory.allocate(size, align, Kind::C)?; + let ptr = self.memory.allocate(size, align, Kind::C.into())?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } } @@ -118,7 +120,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> "free" => { let ptr = args[0].into_ptr(&mut self.memory)?; if !ptr.is_null()? { - self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?; + self.memory.deallocate(ptr.to_ptr()?, None, Kind::C.into())?; } } @@ -242,7 +244,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> } if let Some(old) = success { if let Some(var) = old { - self.memory.deallocate(var, None, Kind::Env)?; + self.memory.deallocate(var, None, Kind::Env.into())?; } self.write_null(dest, dest_ty)?; } else { @@ -265,12 +267,12 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> } if let Some((name, value)) = new { // +1 for the null terminator - let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?; + let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env.into())?; self.memory.write_bytes(value_copy.into(), &value)?; let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into(); self.memory.write_bytes(trailing_zero_ptr, &[0])?; if let Some(var) = self.machine_data.env_vars.insert(name.to_owned(), value_copy) { - self.memory.deallocate(var, None, Kind::Env)?; + self.memory.deallocate(var, None, Kind::Env.into())?; } self.write_null(dest, dest_ty)?; } else { @@ -491,7 +493,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - let ptr = self.memory.allocate(size, align, Kind::Rust)?; + let ptr = self.memory.allocate(size, align, Kind::Rust.into())?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_alloc_zeroed" => { @@ -503,7 +505,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - let ptr = self.memory.allocate(size, align, Kind::Rust)?; + let ptr = self.memory.allocate(size, align, Kind::Rust.into())?; self.memory.write_repeat(ptr.into(), 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } @@ -517,7 +519,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> if !align.is_power_of_two() { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } - self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?; + self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust.into())?; } "alloc::heap::::__rust_realloc" => { let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; @@ -534,7 +536,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> 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, Kind::Rust)?; + let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust.into())?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } diff --git a/miri/lib.rs b/miri/lib.rs index c32c8105e33..89985593bc7 100644 --- a/miri/lib.rs +++ b/miri/lib.rs @@ -32,6 +32,7 @@ mod fn_call; mod operator; mod intrinsic; mod helpers; +mod memory; use fn_call::EvalContextExt as MissingFnsEvalContextExt; use operator::EvalContextExt as OperatorEvalContextExt; @@ -278,6 +279,7 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> { impl<'tcx> Machine<'tcx> for Evaluator { type Data = EvaluatorData; type MemoryData = MemoryData<'tcx>; + type MemoryKinds = memory::Kind; /// Returns Ok() when the function was handled, fail otherwise fn eval_fn_call<'a>( @@ -313,4 +315,28 @@ impl<'tcx> Machine<'tcx> for Evaluator { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { ecx.ptr_op(bin_op, left, left_ty, right, right_ty) } + + fn mark_static_initialized(m: memory::Kind) -> EvalResult<'tcx> { + use memory::Kind::*; + match m { + // FIXME: This could be allowed, but not for env vars set during miri execution + Env => Err(EvalError::Unimplemented("statics can't refer to env vars".to_owned())), + _ => Ok(()), + } + } + + fn box_alloc<'a>( + ecx: &mut EvalContext<'a, 'tcx, Self>, + ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + let size = ecx.type_size(ty)?.expect("box only works with sized types"); + let align = ecx.type_align(ty)?; + if size == 0 { + Ok(PrimVal::Bytes(align.into())) + } else { + ecx.memory + .allocate(size, align, Kind::Machine(memory::Kind::Rust)) + .map(PrimVal::Ptr) + } + } } diff --git a/miri/memory.rs b/miri/memory.rs new file mode 100644 index 00000000000..55e6026280c --- /dev/null +++ b/miri/memory.rs @@ -0,0 +1,16 @@ + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum Kind { + /// Error if deallocated any other way than `rust_deallocate` + Rust, + /// Error if deallocated any other way than `free` + C, + /// Part of env var emulation + Env, +} + +impl Into<::rustc_miri::interpret::Kind<Kind>> for Kind { + fn into(self) -> ::rustc_miri::interpret::Kind<Kind> { + ::rustc_miri::interpret::Kind::Machine(self) + } +} diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 604ef15e904..8a0b6d5b692 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -128,6 +128,7 @@ impl Error for ConstEvalError { impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { type Data = (); type MemoryData = (); + type MemoryKinds = !; fn eval_fn_call<'a>( ecx: &mut EvalContext<'a, 'tcx, Self>, instance: ty::Instance<'tcx>, @@ -185,4 +186,15 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator { ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into()) } + + fn mark_static_initialized(m: !) -> EvalResult<'tcx> { + m + } + + fn box_alloc<'a>( + _ecx: &mut EvalContext<'a, 'tcx, Self>, + _ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + Err(ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into()) + } } diff --git a/src/librustc_mir/interpret/error.rs b/src/librustc_mir/interpret/error.rs index dd718c737af..7d62d59fcd7 100644 --- a/src/librustc_mir/interpret/error.rs +++ b/src/librustc_mir/interpret/error.rs @@ -4,7 +4,7 @@ use rustc::mir; use rustc::ty::{FnSig, Ty, layout}; use super::{ - MemoryPointer, Kind, LockInfo, AccessKind + MemoryPointer, LockInfo, AccessKind }; use rustc_const_math::ConstMathErr; @@ -88,8 +88,8 @@ pub enum EvalError<'tcx> { AssumptionNotHeld, InlineAsm, TypeNotPrimitive(Ty<'tcx>), - ReallocatedWrongMemoryKind(Kind, Kind), - DeallocatedWrongMemoryKind(Kind, Kind), + ReallocatedWrongMemoryKind(String, String), + DeallocatedWrongMemoryKind(String, String), ReallocateNonBasePtr, DeallocateNonBasePtr, IncorrectAllocationInformation, @@ -262,10 +262,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), + ReallocatedWrongMemoryKind(ref old, ref new) => + write!(f, "tried to reallocate memory from {} to {}", old, new), + DeallocatedWrongMemoryKind(ref old, ref 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/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index f5082a4d2d8..277e6e99e75 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -782,17 +782,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } NullaryOp(mir::NullOp::Box, ty) => { - // FIXME(CTFE): don't allow heap allocations in const eval - // FIXME: call the `exchange_malloc` lang item if available - 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 align = self.type_align(ty)?; - let ptr = self.memory.allocate(size, align, MemoryKind::Rust)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } + let ptr = M::box_alloc(self, ty)?; + self.write_primval(dest, ptr, dest_ty)?; } NullaryOp(mir::NullOp::SizeOf, ty) => { diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index adb1054af1d..140bd946c78 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -21,6 +21,9 @@ pub trait Machine<'tcx>: Sized { /// Additional data that can be accessed via the Memory type MemoryData; + /// Additional memory kinds a machine wishes to distinguish from the builtin ones + type MemoryKinds: ::std::fmt::Debug + PartialEq + Copy + Clone; + /// Entry point to all function calls. /// /// Returns Ok(true) when the function was handled completely @@ -61,5 +64,16 @@ pub trait Machine<'tcx>: Sized { right: PrimVal, right_ty: ty::Ty<'tcx>, ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; + + /// Called when trying to mark machine defined `MemoryKinds` as static + fn mark_static_initialized(m: Self::MemoryKinds) -> EvalResult<'tcx>; + + /// Heap allocations via the `box` keyword + /// + /// Returns a pointer to the allocated memory + fn box_alloc<'a>( + ecx: &mut EvalContext<'a, 'tcx, Self>, + ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal>; } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index e8701d1e64c..31e47e706ad 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -116,7 +116,7 @@ impl fmt::Display for AllocId { } #[derive(Debug)] -pub struct Allocation { +pub struct Allocation<M> { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer pub bytes: Vec<u8>, @@ -132,12 +132,12 @@ pub struct Allocation { /// 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` - pub kind: Kind, + pub kind: Kind<M>, /// Memory regions that are locked by some function locks: BTreeMap<MemoryRange, LockInfo>, } -impl Allocation { +impl<M> Allocation<M> { fn iter_locks<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item=(&'a MemoryRange, &'a LockInfo)> + 'a { self.locks.range(MemoryRange::range(offset, len)) .filter(move |&(range, _)| range.overlaps(offset, len)) @@ -165,11 +165,7 @@ impl Allocation { } #[derive(Debug, PartialEq, Copy, Clone)] -pub enum Kind { - /// Error if deallocated any other way than `rust_deallocate` - Rust, - /// Error if deallocated any other way than `free` - C, +pub enum Kind<T> { /// Error if deallocated except during a stack pop Stack, /// Static in the process of being initialized. @@ -179,8 +175,8 @@ pub enum Kind { UninitializedStatic, /// May never be deallocated Static, - /// Part of env var emulation - Env, + /// Additional memory kinds a machine wishes to distinguish from the builtin ones + Machine(T), } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -226,7 +222,7 @@ pub struct Memory<'a, 'tcx, M: Machine<'tcx>> { pub data: M::MemoryData, /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations). - alloc_map: HashMap<AllocId, Allocation>, + alloc_map: HashMap<AllocId, Allocation<M::MemoryKinds>>, /// The AllocId to assign to the next new allocation. Always incremented, never gets smaller. next_id: AllocId, @@ -285,7 +281,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } - pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> { + pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation<M::MemoryKinds>> { self.alloc_map.iter() } @@ -313,7 +309,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(ptr) } - pub fn allocate(&mut self, size: u64, align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> { + pub fn allocate( + &mut self, + size: u64, + align: u64, + kind: Kind<M::MemoryKinds>, + ) -> EvalResult<'tcx, MemoryPointer> { assert_ne!(align, 0); assert!(align.is_power_of_two()); @@ -341,7 +342,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(MemoryPointer::new(id, 0)) } - pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> { + pub fn reallocate( + &mut self, + ptr: MemoryPointer, + old_size: u64, + old_align: u64, + new_size: u64, + new_align: u64, + kind: Kind<M::MemoryKinds>, + ) -> EvalResult<'tcx, MemoryPointer> { use std::cmp::min; if ptr.offset != 0 { @@ -349,7 +358,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } if let Ok(alloc) = self.get(ptr.alloc_id) { if alloc.kind != kind { - return Err(EvalError::ReallocatedWrongMemoryKind(alloc.kind, kind)); + return Err(EvalError::ReallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind))); } } @@ -361,7 +370,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { Ok(new_ptr) } - pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>, kind: Kind) -> EvalResult<'tcx> { + pub fn deallocate( + &mut self, + ptr: MemoryPointer, + size_and_align: Option<(u64, u64)>, + kind: Kind<M::MemoryKinds>, + ) -> EvalResult<'tcx> { if ptr.offset != 0 { return Err(EvalError::DeallocateNonBasePtr); } @@ -380,7 +394,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { .map_err(|lock| EvalError::DeallocatedLockedMemory { ptr, lock })?; if alloc.kind != kind { - return Err(EvalError::DeallocatedWrongMemoryKind(alloc.kind, kind)); + return Err(EvalError::DeallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind))); } if let Some((size, align)) = size_and_align { if size != alloc.bytes.len() as u64 || align != alloc.align { @@ -573,7 +587,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { /// Allocation accessors impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { - pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { + pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::MemoryKinds>> { match self.alloc_map.get(&id) { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { @@ -583,7 +597,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } - fn get_mut_unchecked(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { + fn get_mut_unchecked(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation<M::MemoryKinds>> { match self.alloc_map.get_mut(&id) { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { @@ -593,7 +607,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } } - pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> { + pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation<M::MemoryKinds>> { let alloc = self.get_mut_unchecked(id)?; if alloc.mutable == Mutability::Mutable { Ok(alloc) @@ -663,13 +677,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { } let immutable = match (alloc.kind, alloc.mutable) { - (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)", - (Kind::Stack, _) => " (stack)", + (Kind::UninitializedStatic, _) => " (static in the process of initialization)".to_owned(), + (Kind::Static, Mutability::Mutable) => " (static mut)".to_owned(), + (Kind::Static, Mutability::Immutable) => " (immutable)".to_owned(), + (Kind::Machine(m), _) => format!(" ({:?})", m), + (Kind::Stack, _) => " (stack)".to_owned(), }; trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable); @@ -793,17 +805,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> { // 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::UninitializedStatic => {}, + Kind::Machine(m) => M::mark_static_initialized(m)?, 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; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 46a570349f0..960b73ee6b2 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -2,6 +2,7 @@ i128_type, rustc_private, conservative_impl_trait, + never_type, )] // From rustc. diff --git a/tests/compile-fail/stack_free.rs b/tests/compile-fail/stack_free.rs index 08ff7457b76..96006c884e5 100644 --- a/tests/compile-fail/stack_free.rs +++ b/tests/compile-fail/stack_free.rs @@ -1,4 +1,4 @@ -// error-pattern: tried to deallocate Stack memory but gave Rust as the kind +// error-pattern: tried to deallocate Stack memory but gave Machine(Rust) as the kind fn main() { let x = 42; |
