rust-lang / miri Goto Github PK
View Code? Open in Web Editor NEWAn interpreter for Rust's mid-level intermediate representation
License: Apache License 2.0
An interpreter for Rust's mid-level intermediate representation
License: Apache License 2.0
I'd like to use miri to step through code like in a debugger. At first I thought it would make sense to restructure miri so the interpreter ends up looping over an iterator that does the actual processing. But now I wonder if it would make sense to insert "hooks" into random places inside the interpreter, which a caller to the interpreter could attach to. So basically the interpreter runs in a thread and whenever it encounters a hook-place, it checks for a hook and if one is there, starts interacting with it.
The hook has a few options available:
Possible hook-places:
What do you think?
repro:
#![feature(associated_consts)]
trait Foo {
const ID: i32 = 2;
}
impl Foo for i32 {
const ID: i32 = 1;
}
fn main() {
assert_eq!(1, <i32 as Foo>::ID);
}
Source
From this rustc test.
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);
}
Output and error
TRACE:miri::step: _0 = <std::ops::FnMut(isize, isize) -> isize as std::ops::FnMut<(isize, isize)>>::call_mut(_3, _4) -> bb1
TRACE:miri::eval_context: _1 (1 frames up): Undef
TRACE:miri::eval_context: _3: (Ptr(Pointer { alloc_id: AllocId(0), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(3), offset: 0 }))
TRACE:miri::memory: Alloc 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (40 bytes) (immutable)
TRACE:miri::memory: └─────────(2)─────────┘ └─────────(2)─────────┘
TRACE:miri::memory: Alloc 2: function pointer: main::{{closure}}: extern "rust-call" fn((isize, isize)) -> isize
TRACE:miri::eval_context: _4:
TRACE:miri::memory: Alloc 4: 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 (16 bytes)
DEBUG:miri::memory: reading fn ptr: 2
TRACE:miri::eval_context: load mir DefId { krate: CrateNum(0), node: DefIndex(7) => overloaded_calls_object_two_args/4089d7c8b778d88cec885baf7b69e6df-exe::main[0]::{{closure}}[0] }
TRACE:miri::eval_context: _1: Undef
TRACE:miri::eval_context: _2: Undef
TRACE:miri::step: // bb0
TRACE:miri::step: StorageLive(_4)
TRACE:miri::step: _4 = _2
TRACE:miri::eval_context: _4: Undef
TRACE:miri::eval_context: _2:
TRACE:miri::memory: Alloc 5: 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context: _4: Bytes(1)
TRACE:miri::step: StorageLive(_5)
TRACE:miri::step: _5 = _3
TRACE:miri::eval_context: _5: Undef
TRACE:miri::eval_context: _3: Undef
TRACE:miri::eval_context: _5: Undef
TRACE:miri::step: StorageLive(_6)
TRACE:miri::step: StorageLive(_7)
TRACE:miri::step: _7 = _4
TRACE:miri::eval_context: _7: Undef
TRACE:miri::eval_context: _4: Bytes(1)
TRACE:miri::eval_context: _7: Bytes(1)
TRACE:miri::step: _8 = CheckedMul(_7, const 10isize)
TRACE:miri::eval_context: _8: Undef
TRACE:miri::eval_context: _7: Bytes(1)
TRACE:miri::eval_context: _8: (Bytes(10), Bytes(0))
TRACE:miri::step: assert(!(_8.1: bool), "attempt to multiply with overflow") -> bb1
TRACE:miri::step: // bb1
TRACE:miri::step: _6 = (_8.0: isize)
TRACE:miri::eval_context: _6: Undef
TRACE:miri::eval_context: _6: Bytes(10)
TRACE:miri::step: StorageLive(_9)
TRACE:miri::step: _9 = _5
TRACE:miri::eval_context: _9: Undef
TRACE:miri::eval_context: _5: Undef
TRACE:miri::eval_context: _9: Undef
TRACE:miri::step: _10 = CheckedAdd(_6, _9)
TRACE:miri::eval_context: _10: Undef
TRACE:miri::eval_context: _6: Bytes(10)
TRACE:miri::eval_context: _9: Undef
error: attempted to read undefined bytes
--> ../rust/master/src/test/run-pass/overloaded-calls-object-two-args.rs:20:29
|
20 | let z = foo(&mut |x, y| x * 10 + y);
| ^^^^^^^^^^
and are really slow, since they use write_value
for every single value.
I have some code where I write_value
the first value and then just copy those bytes N-1 times over the remaining array fields. Better ideas are very welcome.
old speed:
test repeat ... bench: 509,773 ns/iter (+/- 96,298)
test repeat_manual ... bench: 313,843,922 ns/iter (+/- 5,732,707)
new speed:
test repeat ... bench: 366,520 ns/iter (+/- 69,298)
test repeat_manual ... bench: 300,833,908 ns/iter (+/- 8,937,121)
not really worth the improvements. I'll just slap on an execution limit thingy.
examples of unsafe transmutes:
&T
-> &mut T
NonZero
typeexamples of transmutes that should be something else (warn?)
*const T
(cast)&T
-> *const T
(cast/coercion)*const T
-> &T
(should be a deref)I tried the example code on the rust homepage.
Panicking line: https://github.com/solson/miri/blob/master/src/interpreter/terminator/mod.rs#L64
a non-mutable static might contain a raw pointer into a static mut and this code would then freeze the static mut. Don't worry about it for now.
this code: #85 (comment)
Code example reproducing this issue:
#![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);
}
}
probably just requires making read_nonnull_discriminant_value
also take in the discriminant type instead of assuming it's usize
sized.
The following code reproduces the issue:
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
use std::mem::size_of;
enum E {
A = 1,
B = 2,
C = 3,
}
struct S {
a: u16,
b: u8,
e: E,
}
fn main() {
assert_eq!(size_of::<E>(), 1);
assert_eq!(size_of::<Option<E>>(), 1);
assert_eq!(size_of::<Result<E, ()>>(), 1);
assert_eq!(size_of::<S>(), 4);
assert_eq!(size_of::<Option<S>>(), 4);
let enone = None::<E>;
let esome = Some(E::A);
if let Some(..) = enone {
panic!();
}
if let None = esome {
panic!();
}
}
minimal reproduction:
#![allow(dead_code)]
enum E {
A = 1,
B = 2,
C = 3,
}
fn main() {
let enone = None::<E>;
if let Some(..) = enone {
panic!();
}
}
Support floating point types. Probably have to add both f32
, and f64
to PrimVal
, and handle those everywhere.
AFAIK Rust floating point arithmetic follows IEEE754. Still, I don't know what happens on architectures that do not fully conform to the spec. Does Rust or LLVM emulate it or not? Probably not. This might turn out to not be a problem in practice (e.g. miri result would still be deterministic "enough of the time"). Once we are the we might want to reconsider that though.
I haven't read up on them, but the way they are sprayed around in MIR looks very much like that is how they would work.
(low priority)
It would be nice to use libffi to allow for external calls.
Source
fn foo() {}
fn main() {
let f: &Fn() = &foo;
f();
}
Output and error
TRACE:miri::step 0 _4 = <std::ops::Fn() as std::ops::Fn<()>>::call(_5, _6) -> bb1
TRACE:miri::eval_context 0 frame[0] _4: Undef
TRACE:miri::eval_context 0 frame[0] _5: (Ptr(Pointer { alloc_id: AllocId(0), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(3), offset: 0 }))
TRACE:miri::memory 0 Alloc 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (48 bytes) (immutable)
TRACE:miri::memory 0 └─────────(2)─────────┘ └─────────(2)─────────┘ └─────────(2)─────────┘
TRACE:miri::memory 0 Alloc 2: function pointer
TRACE:miri::eval_context 0 frame[0] _6:
DEBUG:miri::memory: reading fn ptr: 2
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', src/terminator/mod.rs:632
I'm not sure, but I think simply reserving a single allocation to be used for all zst should work (pointers to different zst values will compare equal, but comparing pointers to different types is probably forbidden anyway).
right now miri can't do
pub struct Foo {
inner: i32,
}
fn main() {
unsafe {
let foo = Foo {
inner: std::mem::uninitialized(),
};
let bar = foo;
}
}
but this is something the standard library does: https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/mutex.rs#L105
Not sure what the rules are behind that yet, but to the best of my knowledge UB is when you actually make a choice depending on undefined memory and not just copying undefined bytes around.
I investigated the only "tried to modify constant memory" in #55 and it's due to the fact that Miri can't properly initialize statics that recursively refer to each other. This would be low priority since it only affects the unstable #![feature(static_recursion)]
, but the feature looks like it will be stable soon.
E.g.
struct S(&'static S);
static S1: S = S(&S2);
static S2: S = S(&S1);
When you start initializing S1:
ConstantExtractor
)Note how the drop impl swaps the generics: impl<V, U> Drop for S<U, V>
.
use std::mem;
struct S<U,V> {
_u: U,
size_of_u: usize,
_v: V,
size_of_v: usize
}
impl<U, V> S<U, V> {
fn new(u: U, v: V) -> Self {
S {
_u: u,
size_of_u: mem::size_of::<U>(),
_v: v,
size_of_v: mem::size_of::<V>()
}
}
}
impl<V, U> Drop for S<U, V> {
fn drop(&mut self) {
assert_eq!(mem::size_of::<U>(), self.size_of_u); // mem::size_of::<U>() is 2 instead of 1
assert_eq!(mem::size_of::<V>(), self.size_of_v);
}
}
fn main() {
S::new(0u8, 1u16);
}
rust-lang/rust#39230 changed some things around with boxed, especially that there is a "Drop
impl" now: https://github.com/rust-lang/rust/blame/master/src/liballoc/boxed.rs#L297 (it's empty, doesn't drop its contents)
We should just wait for rustc to fix it.
repro:
#![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<DroppableStruct> {}
struct Whatever { w: Box<MyTrait+'static> }
impl Whatever {
fn new(w: Box<MyTrait+'static>) -> Whatever {
Whatever { w: w }
}
}
fn main() {
{
let f: Box<_> = box DroppableStruct;
let _a = Whatever::new(box f as Box<MyTrait>);
}
assert!(unsafe { DROPPED });
}
struct Foo(Box<u8>);
impl Drop for Foo {
fn drop(&mut self) {
*self.0 = 255; // error: dangling pointer dereferenced
}
}
fn main() {
drop(Foo(Box::new(0)));
}
Boxes are immediately deallocated by the drop terminator, where as drop implementations are pushed to the stack, https://github.com/solson/miri/blob/master/src/interpreter/terminator/mod.rs#L531
I'm not sure how to solve this, other than maybe adding a separate type of frame just to deallocate the box.
Right now it's possible to create a pointer to an immutable static or a constant and modify that memory. It's not a problem for constants yet, because we don't cache constants so far.
The following code works fine if drop(x)
is used instead of let _ = x
. The closure function generated contains just a _0 = ()
and a return
. Trans probably does some magic, but I haven't been able to locate it.
struct Foo<'a>(&'a mut bool);
impl<'a> Drop for Foo<'a> {
fn drop(&mut self) {
*self.0 = true;
}
}
fn f<T: FnOnce()>(t: T) {
t()
}
fn main() {
let mut ran_drop = false;
{
let x = Foo(&mut ran_drop);
let x = move || { let _ = x; };
f(x);
}
assert!(ran_drop);
}
Cast kinds:
¹ This goes along with implementing vtables and virtual method calls.
² Currently casts with same-sized source and destination are implemented by a blind copy and casts from fat pointers copy just the pointer part. This works well enough to pass most of our existing tests but it's extremely hacky. Search for the Misc
case.
This will likely require multiple PRs.
repro example:
fn main() {
vec![()].into_iter();
}
Leaving things to do here so they don't get lost
@eddyb in IRC:
replace the LLVM-generating stuff in trans with lowering miri allocations to LLVM
it should be more efficient in cases without relocations because you can give LLVM a byte blob
not sure if its own load-from-constant folding can deal with it
Miri should use rustc's compiler error macros to ease the eventual merge into rustc itself.
So, instead of panic!
or unimplemented!
, use span_bug!
, bug!
, etc. I believe these come from #[macro_use] extern crate rustc;
.
Prompted by #28 (comment).
Tried to run a test with cargo run tests/run-pass/vecs.rs
, but it results on several error messages stating that the following libraries cannot be found (note: I have removed the trailing hashes in the names for clarity):
The same happens when running miri
directly.
When looking at miri.rs
I found out that it relies on RUSTUP_HOME
and RUSTUP_TOOLCHAIN
. After adding those environment variables everything worked correctly. I think this should be documented on the readme.
The f.b
access goes to offset 2, while it should be offset 8 due to the alignment of T. The issue is that in eval_lvalue_projection
the base_ty
is Foo<Bar>
, so the layout computed by rustc says "hey we can put T
directly after a
without padding". I'm completely baffled as to how rustc does these things. Is there some info in the vtable we can use?
struct Foo<T: ?Sized> {
a: u16,
b: T
}
trait Bar {
fn get(&self) -> usize;
}
impl Bar for usize {
fn get(&self) -> usize { *self }
}
fn main() {
let f : Foo<usize> = Foo { a: 0, b: 11 };
let f : &Foo<Bar> = &f;
assert_eq!(f.b.get(), 11);
}
TRACE:miri::eval_context: load mir DefId { krate: CrateNum(0), node: DefIndex(13) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::main[0] }
TRACE:miri::step: StorageLive(_1)
TRACE:miri::step: _1 = Foo<usize> { a: const 0u16, b: const 11usize }
TRACE:miri::eval_context: _1: Undef
TRACE:miri::memory: allocate: 16, 8 at AllocId(2)
TRACE:miri::eval_context: _1:
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step: StorageLive(_2)
TRACE:miri::step: StorageLive(_3)
TRACE:miri::step: StorageLive(_4)
TRACE:miri::step: _4 = &_1
TRACE:miri::eval_context: _4: Undef
TRACE:miri::eval_context: _1:
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context: _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step: _3 = &(*_4)
TRACE:miri::eval_context: _3: Undef
TRACE:miri::eval_context: _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context: _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::lvalue: deref to Foo<usize> on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
TRACE:miri::eval_context: _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step: _2 = _3 as &Foo<Bar> (Unsize)
TRACE:miri::eval_context: _2: Undef
TRACE:miri::eval_context: _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
DEBUG:miri::vtable: get_vtable(trait_ref=Binder(<usize as Bar>))
DEBUG:miri::vtable: get_vtable_methods(impl_id=DefId { krate: CrateNum(0), node: DefIndex(10) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0] }, substs=Slice([])
DEBUG:miri::vtable: get_vtable_methods: trait_method_type=AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(8) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0]::get[0] }, name: get(88), kind: Method, vis: Restricted(DefId { krate: CrateNum(0), node: DefIndex(0) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe }), defaultness: Default { has_value: false }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(7) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0] }), method_has_self_argument: true }
DEBUG:miri::vtable: get_vtable_methods: trait_method_type=AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(8) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0]::get[0] }, name: get(88), kind: Method, vis: Restricted(DefId { krate: CrateNum(0), node: DefIndex(0) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe }), defaultness: Default { has_value: false }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(7) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0] }), method_has_self_argument: true }
DEBUG:miri::vtable: get_vtable_methods: mth=ImplMethod { method: AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(11) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0]::get[0] }, name: get(88), kind: Method, vis: Public, defaultness: Final, container: ImplContainer(DefId { krate: CrateNum(0), node: DefIndex(10) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0] }), method_has_self_argument: true }, substs: Slice([]), is_provided: false }
DEBUG:miri::memory: creating fn ptr: 3
TRACE:miri::memory: allocate: 32, 8 at AllocId(4)
TRACE:miri::memory: mark_static_initalized
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::memory: sub-reloc: AllocId(3)
TRACE:miri::memory: mark_static_initalized
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::eval_context: _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::step: StorageDead(_3)
TRACE:miri::step: StorageDead(_4)
TRACE:miri::step: StorageLive(_6)
TRACE:miri::step: StorageLive(_7)
TRACE:miri::step: StorageLive(_8)
TRACE:miri::step: StorageLive(_9)
This is where the fun begins. We don't have the real type info available anymore, since we did an unsize cast above.
TRACE:miri::step: _9 = &((*_2).1: Bar)
TRACE:miri::eval_context: _9: Undef
TRACE:miri::eval_context: _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::eval_context: _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::lvalue: deref to Foo<Bar> on ByValPair(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::lvalue: field base layout for ty Foo<Bar>: Univariant {
variant: Struct {
align: Align {
raw: 49
},
packed: false,
sized: false,
offsets: [
Size {
raw: 0
},
Size {
raw: 2
}
],
memory_index: [
0,
1
],
min_size: Size {
raw: 2
}
},
non_zero: false
}
TRACE:miri::lvalue: field: 1 at offset 2
see how _9
points to offset 2 in alloc 2.
TRACE:miri::eval_context: _9: (Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::step: _8 = <Bar as Bar>::get(_9) -> bb1
TRACE:miri::eval_context: _8: Undef
TRACE:miri::eval_context: _9: (Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory: Alloc 4: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory: └─────────(3)─────────┘
TRACE:miri::memory: Alloc 3: function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::terminator: args: [
(
ByValPair(
Ptr(
Pointer {
alloc_id: AllocId(
2
),
offset: 2
}
),
Ptr(
Pointer {
alloc_id: AllocId(
4
),
offset: 0
}
)
),
&Bar
)
]
DEBUG:miri::memory: reading fn ptr: 3
TRACE:miri::terminator: sig: ([&usize]; variadic: false)->usize
TRACE:miri::eval_context: load mir DefId { krate: CrateNum(0), node: DefIndex(11) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0]::get[0] }
TRACE:miri::eval_context: _1: Undef
TRACE:miri::step: // bb0
TRACE:miri::step: StorageLive(_2)
TRACE:miri::step: _2 = _1
TRACE:miri::eval_context: _2: Undef
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context: _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step: StorageLive(_3)
TRACE:miri::step: _3 = (*_2)
TRACE:miri::eval_context: _3: Undef
TRACE:miri::eval_context: _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context: _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory: Alloc 2: 00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::lvalue: deref to usize on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }))
TRACE:miri::memory: allocate: 8, 8 at AllocId(5)
TRACE:miri::memory: alignment check 2 failed for alloc AllocId(2)
error: tried to access memory with alignment 2, but alignment 8 is required
--> tests/run-pass/dst-field-align.rs:21:30
|
21 | fn get(&self) -> usize { *self }
| ^^^^^
|
note: inside call to <usize as Bar>::get
--> tests/run-pass/dst-field-align.rs:27:16
|
27 | assert_eq!(f.b.get(), 11);
| ^^^^^^^^^
note: inside call to main
When I run the following code I get a panic.
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();
}
Finished debug [unoptimized + debuginfo] target(s) in 0.1 secs
Running `target/debug/miri example_code.rs`
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `1`, right: `0`)', src/interpreter/mod.rs:535
stack backtrace:
1: 0xb672ec11 - std::sys::backtrace::tracing::imp::write::h4b09e6e8c01db097
2: 0xb673edaa - std::panicking::default_hook::{{closure}}::h1d3243f546573ff4
3: 0xb673b949 - std::panicking::default_hook::h96c288d728df3ebf
4: 0xb673c053 - std::panicking::rust_panic_with_hook::hb1322e5f2588b4db
5: 0xb673bedd - std::panicking::begin_panic::hfbeda5aad583dc32
6: 0xb673bdfe - std::panicking::begin_panic_fmt::h4fe9fb9d5109c4bf
7: 0xb75c6794 - miri::interpreter::EvalContext::eval_rvalue_into_lvalue::h1e311a0af7ed05c7
at /home/bjorn/Documenten/miri/src/interpreter/mod.rs:535
8: 0xb759e79d - miri::interpreter::step::<impl miri::interpreter::EvalContext<'a, 'tcx>>::statement::he6295ec356cfef01
at /home/bjorn/Documenten/miri/src/interpreter/step.rs:80
9: 0xb759db8b - miri::interpreter::step::<impl miri::interpreter::EvalContext<'a, 'tcx>>::step::ha1e6c1f76ff81898
at /home/bjorn/Documenten/miri/src/interpreter/step.rs:47
10: 0xb75d8cfd - miri::interpreter::eval_main::h2893a72c73e2909c
at /home/bjorn/Documenten/miri/src/interpreter/mod.rs:1171
11: 0xb73d2369 - <miri::MiriCompilerCalls as rustc_driver::CompilerCalls<'a>>::build_controller::{{closure}}::h168a492b582a289e
at /home/bjorn/Documenten/miri/src/bin/miri.rs:78
12: 0xb7292b0f - rustc_driver::driver::compile_input::{{closure}}::hdf708031c67974d4
13: 0xb728287b - rustc_driver::driver::phase_3_run_analysis_passes::{{closure}}::hcac3a2328cd9259a
14: 0xb71c5f3c - rustc::ty::context::TyCtxt::create_and_enter::h391a9cd3e27a0125
15: 0xb7243411 - rustc_driver::driver::compile_input::hc0edbed7edb3eb18
16: 0xb72693ff - rustc_driver::run_compiler::h22d678d32fb7c300
17: 0xb73d09ce - miri::main::he47b4050ae2aa0ba
at /home/bjorn/Documenten/miri/src/bin/miri.rs:141
18: 0xb673bca5 - std::panicking::try::do_call::h8a9d45772be711b8
19: 0xb67468e7 - __rust_maybe_catch_panic
20: 0xb673aee6 - std::rt::lang_start::haaae1186de9de8cb
21: 0xb73d253a - main
22: 0xb6495af2 - __libc_start_main
23: 0xb736c140 - <unknown>
error: Process didn't exit successfully: `target/debug/miri example_code.rs` (exit code: 101)
Source
#![feature(i128_type)]
fn main() {
let bad = unsafe {
std::mem::transmute::<&[u8], u128>(&[1u8]);
// On 32-bit: transmute::<&[u8], u64>(&[1u8]);
};
bad + 1;
}
Output and error
TRACE:miri::step 0 _1 = std::intrinsics::transmute::<&[u8], u128>(_2) -> bb1
TRACE:miri::eval_context 0 frame[0] _1: Undef
TRACE:miri::eval_context 0 frame[0] _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0 Alloc 2: 01 (1 bytes) (immutable)
TRACE:miri::step 0 // bb1
TRACE:miri::step 0 StorageDead(_2)
TRACE:miri::step 0 StorageDead(_3)
TRACE:miri::step 0 StorageDead(_4)
TRACE:miri::step 0 StorageLive(_6)
TRACE:miri::step 0 _6 = _1
TRACE:miri::eval_context 0 frame[0] _6: Undef
TRACE:miri::eval_context 0 frame[0] _1: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0 Alloc 2: 01 (1 bytes) (immutable)
TRACE:miri::eval_context 0 frame[0] _6: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0 Alloc 2: 01 (1 bytes) (immutable)
TRACE:miri::step 0 _7 = CheckedAdd(_6, const 1u128)
TRACE:miri::eval_context 0 frame[0] _7: Undef
TRACE:miri::eval_context 0 frame[0] _6: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0 Alloc 2: 01 (1 bytes) (immutable)
error: internal compiler error: src/eval_context.rs:903: value_to_primval can't work with fat pointers
Source
#![feature(i128_type)]
fn main() {
let bad = unsafe {
std::mem::transmute::<u128, &[u8]>(42);
// On 32-bit: transmute::<u64, &[u8]>(42);
};
bad[0];
}
Output and error
TRACE:miri::step 0 _1 = std::intrinsics::transmute::<u128, &[u8]>(const 42u128) -> bb1
TRACE:miri::eval_context 0 frame[0] _1: Undef
TRACE:miri::step 0 // bb1
TRACE:miri::step 0 StorageLive(_3)
TRACE:miri::step 0 _4 = Len((*_1))
TRACE:miri::eval_context 0 frame[0] _4: Undef
TRACE:miri::eval_context 0 frame[0] _1: Bytes(42)
TRACE:miri::eval_context 0 frame[0] _1: Bytes(42)
TRACE:miri::lvalue 0 deref to [u8] on ByVal(Bytes(42))
thread 'main' panicked at 'not yet implemented', src/value.rs:116
Travis-CI is failing and I don't have a machine to build miri on right now (in the middle of moving).
time MIRI_RUSTC_TEST=../rust/src/test/run-fail cargo test --release 2> cargo_test_output_fail.txt
Note that "success" is bad 🤣 . Since it means that a run-fail tests passed. Search for "... ok" in the full output to figure out which run-fail tests need a fix in miri.
10 success, 15 no mir, 0 crate not found, 95 failed, 0 C fn, 0 ABI, 4 unsupported, 0 intrinsic
0 success, 15 no mir, 0 crate not found, 105 failed, 0 C fn, 0 ABI, 4 unsupported, 0 intrinsic
(sorted, deduplicated)
74 the evaluated program panicked
6 main function not found
1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_3.rs:16:5: 16:10
1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_2.rs:16:5: 16:10
1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_1.rs:16:5: 16:10
1 index out of bounds: the len is 3 but the index is 3 at ../rust/src/test/run-fail/dst-raw-slice.rs:17:18: 17:25
1 index out of bounds: the len is 3 but the index is 2305843009213693952 at /checkout/src/libcore/slice.rs:658:10: 658:24
1 index out of bounds: the len is 1 but the index is 2 at /checkout/src/libcollections/vec.rs:1422:10: 1422:25
1 a raw memory access tried to access part of a pointer value as raw bytes
1 RemainderByZero at ../rust/src/test/run-fail/mod-zero.rs:15:14: 15:19
1 Overflow(Sub) at ../rust/src/test/run-fail/overflowing-sub.rs:15:14: 15:31
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-6.rs:18:14: 18:29
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-5.rs:17:14: 17:29
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-4.rs:21:13: 21:23
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-3.rs:17:14: 17:26
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-2.rs:17:14: 17:26
1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-1.rs:17:14: 17:26
1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-4.rs:21:13: 21:23
1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-3.rs:17:14: 17:25
1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-2.rs:17:14: 17:21
1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-1.rs:17:14: 17:25
1 Overflow(Neg) at ../rust/src/test/run-fail/overflowing-neg.rs:15:14: 15:27
1 Overflow(Mul) at /checkout/src/libcore/num/mod.rs:2220:24: 2220:35
1 Overflow(Mul) at /checkout/src/libcore/num/mod.rs:1168:24: 1168:35
1 Overflow(Mul) at ../rust/src/test/run-fail/overflowing-mul.rs:15:13: 15:22
1 Overflow(Add) at ../rust/src/test/run-fail/overflowing-add.rs:15:14: 15:27
1 DivisionByZero at ../rust/src/test/run-fail/divide-by-zero.rs:15:14: 15:19
3 threading
1 program arguments
6 <std::string::String as std::convert::From<&'a str>>::from
4 std::io::stderr
2 std::panic::set_hook
1 std::panic::take_hook
1 std::fmt::format
1 std::collections::hash_map::RandomState::new::KEYS
Original post: add tail-call optimization
<scott> a call that branches to the return block on success could be TCO'd
Things to add in super fast mode:
Things to add in fast mode:
Things to add in trace mode:
repro:
#[derive(PartialEq,PartialOrd,Eq,Ord)]
struct ShortCircuit {
x: isize,
}
pub fn main() {
let a = ShortCircuit { x: 1 };
let b = ShortCircuit { x: 2 };
a.cmp(&b);
}
It would be best to take advantage of the ElaborateDrops
MIR pass, which is currently run after the after_analysis
callback that Miri uses.
I'm currently looking into adding a callback between after_analysis
and after_trans
for this purpose.
Source
fn main() {
let f: fn(i32) -> Option<i32> = Some::<i32>;
f(42);
}
Output and error
TRACE:miri::step: _1 = std::option::Option<i32>::Some as fn(i32) -> std::option::Option<i32> (ReifyFnPointer)
TRACE:miri::eval_context: _1: Undef
DEBUG:miri::memory: creating fn ptr: 2
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::step: StorageLive(_3)
TRACE:miri::step: _3 = _1
TRACE:miri::eval_context: _3: Undef
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::eval_context: _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::step: _2 = _3(const 42i32) -> bb1
TRACE:miri::eval_context: _2: Undef
TRACE:miri::eval_context: _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
DEBUG:miri::memory: reading fn ptr: 2
error: internal compiler error: /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/librustc/ty/subst.rs:476: Type parameter `T/#0` (T/0) out of range when substituting (root type=Some(std::option::Option<T>)) substs=[]
Eventually, we want rustc to be able to limit the amount of time spent evaluating a constant value, since Miri const eval could have infinite loops or simply very long calculations.
Thanks to the EvalContext::step
interface, this should be pretty easy. Likely, we can just have a step count limit which will be configurable like rustc's recursion_depth
.
miri creates two allocations for the *b"hi"
in A
.
see MIR in https://is.gd/bCUD7p
This makes sense, since there's _1 = A; _2 = &_1
instead of _2 = &A
.
Should this
a) be fixed in rustc, or
b) should we cache all literals (we have FIXMEs for that), or
c) should we add some Cow
-like structure to lvalues, so they can refer to other lvalues "ByVal"?
d) is the test ( https://github.com/rust-lang/rust/blob/master/src/test/run-pass/const-str-ptr.rs ) wrong?
@eddyb: any preferences?
Found more evidence for b and against d:
#![feature(const_fn)]
const fn foo() -> *const i8 {
b"foo" as *const _ as *const i8
}
const fn bar() -> i32 {
*&{(1, 2, 3).1}
}
fn main() {
assert_eq!(foo(), b"foo" as *const _ as *const i8);
assert_eq!(bar(), 2);
}
Memory
currently has a pointer_size
field. Instead, it should take tcx.data_layout
to get not only pointer size, but also endianness and alignment for all primitive types. Then, Miri should diagnose unaligned memory accesses and read/write all multi-byte values with the given target endianness rather than host endianness.
tcx.data_layout
in Memory
.The following piece of code triggers this assertion
#[miri_run]
fn slice() -> u8 {
let arr: &[_] = &[101, 102, 103, 104, 105, 106];
arr[5]
}
so I think the following piece of code works, while it most definitely shouldn't, haven't tested yet, but am on it.
let x = &1; // the `&1` is promoted to a constant, but only the pointer is frozen, not the pointee
let y = unsafe { &mut *(x as *const i32 as *mut i32) };
let *y = 42;
assert_eq!(*x, 42);
Compiling miri v0.1.0 (file:///duh/miri)
src/interpreter.rs:1008:23: 1008:30 error: bitshift exceeds the type's number of bits, #[deny(exceeding_bitshifts)] on by default
src/interpreter.rs:1008 n if n <= 1 << 32 => 4,
^~~~~~~
error: aborting due to previous error
I have a feeling that some of these might expose the host-bitwidth to an interpreted program
The following code demonstrates the problem:
fn cast_fn_ptr() {
fn f() {}
let g = unsafe {
transmute::<fn(), fn(i32)>(f)
};
// Here Miri blindly lets us call `f` as if it took an argument.
// This causes Miri to go out bounds on the arguments `Vec`
// (see backtrace below).
g(42)
}
Logs and backtrace:
DEBUG:miri 0 Interpreting: cast_fn_ptr
TRACE:miri::interpreter::stepper 0 tmp0 = cast_fn_ptr::f as fn() (ReifyFnPointer)
DEBUG:miri::memory 0 creating fn ptr: 4
TRACE:miri::interpreter::stepper 0 var0 = std::mem::transmute::<fn(), fn(i32)>(tmp0) -> bb1
TRACE:miri::interpreter::stepper 0 // bb1
TRACE:miri::interpreter::stepper 0 tmp1 = var0
TRACE:miri::interpreter::stepper 0 return = tmp1(const 42i32) -> bb2
DEBUG:miri::memory 0 reading fn ptr: 4
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', ../src/libcollections/vec.rs:1167
stack backtrace:
1: 0x7f9c6bc0561f - std::sys::backtrace::tracing::imp::write::h6528da8103c51ab9
2: 0x7f9c6bc1324b - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::hbe741a5cc3c49508
3: 0x7f9c6bc12e7f - std::panicking::default_hook::he0146e6a74621cb4
4: 0x7f9c6bbd916e - std::panicking::rust_panic_with_hook::h983af77c1a2e581b
5: 0x7f9c6bc13491 - std::panicking::begin_panic::he426e15a3766089a
6: 0x7f9c6bbdb1ca - std::panicking::begin_panic_fmt::hdddb415186c241e7
7: 0x7f9c6bc1342e - rust_begin_unwind
8: 0x7f9c6bc5c4cf - core::panicking::panic_fmt::hf4e16cb7f0d41a25
9: 0x7f9c6bc5c6b2 - core::panicking::panic_bounds_check::h14f942e6ac026712
10: 0x56106d71c38f - _<collections..vec..Vec<T> as core..ops..Index<usize>>::index::h21bed189d0a022e0
at ../src/libcollections/vec.rs:1167
11: 0x56106d711465 - miri::interpreter::EvalContext::eval_fn_call::h0ffe2222a9193951
at src/interpreter/mod.rs:551
12: 0x56106d70a974 - miri::interpreter::EvalContext::eval_terminator::h906ffc9b7c429822
at src/interpreter/mod.rs:438
13: 0x56106d73bbf6 - miri::interpreter::stepper::Stepper::terminator::he5b0f785cd245fcf
at src/interpreter/stepper.rs:38
14: 0x56106d6a7354 - miri::interpreter::stepper::Stepper::step::hc3a72975df25ed8c
at src/interpreter/stepper.rs:85
15: 0x56106d6a68ed - miri::interpreter::step::h5d73db301ea80161
at src/interpreter/mod.rs:29
16: 0x56106d65973e - miri::interpret_start_points::hdb489d38e2bc843c
at src/bin/miri.rs:68
17: 0x56106d658ff4 - _<miri..MiriCompilerCalls as rustc_driver..CompilerCalls<'a>>::build_controller::_$u7b$$u7b$closure$u7d$$u7d$::h44743d57da4e97a6
at src/bin/miri.rs:37
18: 0x7f9c6ddf974d - rustc_driver::driver::compile_input::_$u7b$$u7b$closure$u7d$$u7d$::hf187cb470aad2bf2
19: 0x7f9c6ddf616d - rustc_driver::driver::phase_3_run_analysis_passes::_$u7b$$u7b$closure$u7d$$u7d$::hd565ad56c5876a16
20: 0x7f9c6ddef939 - rustc::ty::context::TyCtxt::create_and_enter::habef58c7230d34f9
21: 0x7f9c6ddb9dff - rustc_driver::driver::compile_input::hfd60b020f6d0208d
22: 0x7f9c6dda5d24 - rustc_driver::run_compiler::h884d01d12eb76bbb
23: 0x56106d6704b1 - miri::main::h496a9d1b73279651
at src/bin/miri.rs:119
24: 0x7f9c6bc127d8 - std::panicking::try::call::h852b0d5f2eec25e4
25: 0x7f9c6bc2170b - __rust_try
26: 0x7f9c6bc216ae - __rust_maybe_catch_panic
27: 0x7f9c6bc1227e - std::rt::lang_start::hfe4efe1fc39e4a30
28: 0x56106d68d9e9 - main
29: 0x7f9c6b332abf - __libc_start_main
30: 0x56106d658c58 - _start
31: 0x0 - <unknown>
Source
fn foo() {}
fn main() {
let f: &Fn() = &(foo as fn());
f();
}
Output and error
TRACE:miri::step 0 _1 = _2 as &std::ops::Fn() (Unsize)
TRACE:miri::eval_context 0 frame[0] _1: Undef
TRACE:miri::eval_context 0 frame[0] _2: Ptr(Pointer { alloc_id: AllocId(3), offset: 0 })
TRACE:miri::memory 0 Alloc 3: 00 00 00 00 00 00 00 00 (8 bytes) (immutable)
TRACE:miri::memory 0 └─────────(2)─────────┘
TRACE:miri::memory 0 Alloc 2: function pointer
DEBUG:miri::vtable: get_vtable(trait_ref=Binder(<fn() as std::ops::Fn<()>>))
error: internal compiler error: src/vtable.rs:63: bad VtableFnPointer fn_ty: fn()
I think it would make sense to store vtables not as allocations but as a
struct Vtable {
drop: Option<Pointer>,
size: u64,
align: u64,
methods: Vec<Pointer>,
}
output of
time MIRI_SYSROOT=~/.xargo/HOST MIRI_RUSTC_TEST=../rust/src/test/run-pass cargo run --release --manifest-path="rustc_tests/Cargo.toml" 2> cargo_test_output.txt > /dev/null
1617 success, 558 no mir, 205 crate not found, 158 failed
1630 success, 560 no mir, 205 crate not found, 143 failed
1632 success, 561 no mir, 205 crate not found, 140 failed
1632 success, 562 no mir, 205 crate not found, 139 failed
1633 success, 563 no mir, 205 crate not found, 137 failed
1638 success, 554 no mir, 205 crate not found, 141 failed
1864 success, 211 no mir, 205 crate not found, 258 failed
1867 success, 202 no mir, 205 crate not found, 264 failed
1868 success, 202 no mir, 205 crate not found, 94 failed, 43 C fn, 9 ABI, 102 unsupported, 5 intrinsic
1878 success, 201 no mir, 205 crate not found, 58 failed, 42 C fn, 9 ABI, 121 unsupported, 5 intrinsic
1883 success, 201 no mir, 205 crate not found, 55 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1885 success, 201 no mir, 205 crate not found, 53 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1886 success, 201 no mir, 205 crate not found, 52 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1902 success, 202 no mir, 206 crate not found, 60 failed, 44 C fn, 0 ABI, 122 unsupported, 6 intrinsic
1901 success, 202 no mir, 206 crate not found, 54 failed, 44 C fn, 9 ABI, 122 unsupported, 6 intrinsic
1904 success, 202 no mir, 206 crate not found, 49 failed, 44 C fn, 9 ABI, 122 unsupported, 6 intrinsic
1905 success, 202 no mir, 206 crate not found, 52 failed, 44 C fn, 9 ABI, 122 unsupported, 2 intrinsic
1907 success, 202 no mir, 206 crate not found, 52 failed, 44 C fn, 9 ABI, 122 unsupported, 0 intrinsic
1913 success, 202 no mir, 206 crate not found, 50 failed, 44 C fn, 9 ABI, 122 unsupported, 0 intrinsic
1933 success, 201 no mir, 209 crate not found, 49 failed, 38 C fn, 0 ABI, 122 unsupported, 6 intrinsic
1985 success, 206 no mir, 219 crate not found, 62 failed, 40 C fn, 0 ABI, 122 unsupported, 6 intrinsic
2016 success, 208 no mir, 225 crate not found, 57 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2022 success, 208 no mir, 225 crate not found, 51 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2025 success, 208 no mir, 225 crate not found, 48 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2179 success, 2 no mir, 235 crate not found, 68 failed, 145 C fn, 0 ABI, 15 unsupported, 6 intrinsic
2179 success, 3 no mir, 235 crate not found, 73 failed, 42 C fn, 0 ABI, 112 unsupported, 6 intrinsic
2188 success, 14 no mir, 236 crate not found, 79 failed, 44 C fn, 0 ABI, 113 unsupported, 6 intrinsic
2457 success, 5 no mir, 252 crate not found, 100 failed, 44 C fn, 0 ABI, 118 unsupported, 14 intrinsic
Tracking the state in gist from now on, so we can see diffs
https://gist.github.com/oli-obk/5a0832eef3124ad9088748fc9e759318
currently we can't handle packed structs with fields whose type has an alignment bigger than 1 (because packed structs have an alignment of 1). So accessing a packed struct's field will do the alignment check and fail.
This has three components:
char
literal constants.char
variant to PrimVal
.char
values in Memory
.Source
static mut FOO: &'static i32 = &42i32;
pub fn main() {
unsafe { *FOO; }
}
Output and error
TRACE:miri::step: pushing stack frame for global: FOO
TRACE:miri::step: _2 = const 42i32
TRACE:miri::eval_context: _2: Undef
TRACE:miri::eval_context: _2: Bytes(42)
TRACE:miri::step: _1 = &_2
TRACE:miri::eval_context: _1: Undef
TRACE:miri::eval_context: _2: Bytes(42)
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 2a 00 00 00 (4 bytes)
TRACE:miri::step: _0 = &(*_1)
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 2a 00 00 00 (4 bytes)
TRACE:miri::eval_context: _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory: Alloc 2: 2a 00 00 00 (4 bytes)
TRACE:miri::lvalue: deref to i32 on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
TRACE:miri::step: return
TRACE:miri::eval_context: deallocating local
TRACE:miri::memory: Alloc 2: 2a 00 00 00 (4 bytes)
DEBUG:miri::memory: deallocated : 2
TRACE:miri::step: // bb0
TRACE:miri::step: _2 = (*FOO)
TRACE:miri::eval_context: _2: Undef
TRACE:miri::lvalue: deref to i32 on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
error: dangling pointer was dereferenced
--> const-vec-of-fns.rs:4:14
|
4 | unsafe { *FOO; }
| ^^^^
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.