Change:
Currently on Running on OSX cc'ing and since I saw both of them also had this issue in #rust-internals on IRC
I should also note that the test code s only when being compiled using an optimization flag. Without the flag, it runs well.
is also having this issue. He says that tests stopped passing for him sometime after the night of December 16th
Yeah both of my open PR's were exhibiting this on Mac OS X Yosemite. You can find them at and for reference.
Here's the backtrace from a crash log: LLDB gives the following stack trace: frame points at a macro to generate red-black tree functions, though as you can see is , so perhaps that's the cause.
I'm going to attempt a bisect, though it'll take a while.
According to the bisect, the bad commit is .
Which was introduced in /cc
As it turns out, is what introduced the test in the first place.
can you check whether affects this issue in any way?
Sadly, still fails on after applying .
I was running and hit an error on this unit test as well, looks to be related. I'm on . Everything passes fine when not running under Valgrind. This is running with the merge of ., commit .
i also see this on my mac
When this is fixed, can we make sure moves to run-pass-valgrind
I am now also getting a second failure:
yeah I that second test when I was investigating the first one (when I was working on stuff related to syntax); the test is intended to make it clear that the issue here is not related to the syntax.
thanks for the update. They are just a minor annoyance when running the test suite.
Here is a reduced test case for input: I have compared the generated llvm-ir with and without the flag to . Here's something funny: Without , we have just a single call to the jemalloc free routine , followed immediately by a : with the flag to , however, we get output that is quite different, with two distinct calls to , in series (tail calls no less), and the second seems to be being fed an value? I am not an expert in LLVM IR, but this struck me as strange:
Here is a result of further investigation: I do not know how valid it is to try to plug in different llvm-passes in an attempt to expose a code-generation bug, but it seemed to produce interesting information (in terms of certain combinations seeming to fall victim to the same bug). Both of these example commands are being fed the reduced test case from the previous comment (named ).
(One can emulate the same effect on a generated .bc file via the llvm utility. My main question at this point is if I can extract a standalone test that is independent of rust, including the libraries like jemalloc that I am linking in...)
Ah, an interesting new detail: in the below revision to the running example, passing yields a program that works, even when passing . This means there may be some way to isolate this to something within , rather than focusing our attention on LLVM bugs. Example with workaround:
I looked into this some more. In particular, I made a test case that uses only the and crates -- it would be straightforward to make it fully standalone, but I did not see a benefit in that. The crucial thing is that this standalone test has its own "built-in" allocator (from a static array of bytes in the code), and it instruments the allocate/free calls with calls to C . Its possible I made a mistake along the way, but there are some pretty funky things being illustrated in this test. (Update: there was indeed a mistake in my port: I forgot to null terminate my strings, and so the first printf call was printing both strings, which presumably just happened to be laid out next to each other in the generated data segment. Nonetheless, the above gist is still useful, in that it continues to illustrate the bug injected by using , and it does so as a standalone program that tells you how the allocate/deallocate calls went wrong.)
Another gist I have developed seems to show a case where optimizations are disabled and yet we still have a spurious invocation. This seems like a promising line of inquiry. (Note that main difference between this gist and the one from the previous comment is that I introduced an extra block around the first match arm:
Yay, I found a different example, a generalization of the original one, where one can observe the erroneous frees occurring even when optimizations are disabled. (This is very similar to my previous gists; the main difference is that I have four separate match arms, each building a differently sized and then implicitly coercing it to a .) The errors you get with and without are different, but they are real. This leads me to suspect that this is a real code gen bug in Rust, not an optimization bug in LLVM. (Although it could be that we are getting some attributes wrong on the structure we feed into LLVM.)
And here's a variant of that same example; again, this causes an error both with and without optimizations on Mac OS X: (The above gist has the advantage that it uses and uses an embedded allocator to track where the error arises. But sometimes you want something that works in the playpen.)
(The program from the previous comment also causes a core-dump in a Linux VM; Ubuntu 64-bit 14.10...)
Awesome work :sparkles:

Discussion:
let tcx = bcx.tcx(); let datum_ty = datum.ty; <del> // Arrange cleanup let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "unsize_unique_vec", expr.id)); </del> <ins> debug!("unsize_unique_vec expr.id={} datum_ty={} len={}", expr.id, datum_ty.repr(tcx), len); // We do not arrange cleanup ourselves; if we already are an // L-value, then cleanup will have already been scheduled (and // the `datum.store_to` call below will emit code to zero the // drop flag when moving out of the L-value). If we are an R-value, // then we do not need to schedule cleanup. </ins> let ll_len = C_uint(bcx.ccx(), len); let unit_ty = ty::sequence_element_type(tcx, ty::type_content(datum_ty));

let base = PointerCast(bcx, base, type_of::type_of(bcx.ccx(), datum_ty).ptr_to()); <del> bcx = lval.store_to(bcx, base); </del> <ins> bcx = datum.store_to(bcx, base); </ins> Store(bcx, ll_len, get_len(bcx, scratch.val)); DatumBlock::new(bcx, scratch.to_expr_datum())

}; let result_ty = ty::mk_uniq(tcx, ty::unsize_ty(tcx, unboxed_ty, k, expr.span)); <del> let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "unsize_unique_expr", expr.id)); </del> <ins> // We do not arrange cleanup ourselves; if we already are an // L-value, then cleanup will have already been scheduled (and // the `datum.store_to` call below will emit code to zero the // drop flag when moving out of the L-value). If we are an R-value, // then we do not need to schedule cleanup. </ins> let scratch = rvalue_scratch_datum(bcx, result_ty, "__uniq_fat_ptr"); let llbox_ty = type_of::type_of(bcx.ccx(), datum_ty); let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to()); <del> bcx = lval.store_to(bcx, base); </del> <ins> bcx = datum.store_to(bcx, base); </ins> let info = unsized_info(bcx, k, expr.id, unboxed_ty, |t| ty::mk_uniq(tcx, t)); Store(bcx, info, get_len(bcx, scratch.val)); <del> let scratch = unpack_datum!(bcx, scratch.to_expr_datum().to_lvalue_datum(bcx, "fresh_uniq_fat_ptr", expr.id)); </del> DatumBlock::new(bcx, scratch.to_expr_datum()) } }

<ins> // 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 <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. // See Issues #20055 and #21695. // We are checking here that the temporaries `Box<[i8, k]>`, for `k` // in 1, 2, 3, 4, that are induced by the match expression are // properly handled, in that only *one* will be initialized by // whichever arm is run, and subsequently dropped at the end of the // statement surrounding the `match`. trait Boo { } impl Boo for [i8; 1] { } impl Boo for [i8; 2] { } impl Boo for [i8; 3] { } impl Boo for [i8; 4] { } pub fn foo(box_1: fn () -> Box<[i8; 1]>, box_2: fn () -> Box<[i8; 2]>, box_3: fn () -> Box<[i8; 3]>, box_4: fn () -> Box<[i8; 4]>, ) { println!("Hello World 1"); let _: Box<Boo> = match 3 { 1 => box_1(), 2 => box_2(), 3 => box_3(), _ => box_4(), }; println!("Hello World 2"); } pub fn main() { fn box_1() -> Box<[i8; 1]> { Box::new( [1i8; 1] ) } fn box_2() -> Box<[i8; 2]> { Box::new( [1i8; 2] ) } fn box_3() -> Box<[i8; 3]> { Box::new( [1i8; 3] ) } fn box_4() -> Box<[i8; 4]> { Box::new( [1i8; 4] ) } foo(box_1, box_2, box_3, box_4); } </ins>

<ins> // 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 <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. // Issue #2005: Check that boxed fixed-size arrays are properly // accounted for (namely, only deallocated if they were actually // created) when they appear as temporaries in unused arms of a match // expression. pub fn foo(box_1: fn () -> Box<[i8; 1]>, box_2: fn () -> Box<[i8; 20]>, box_3: fn () -> Box<[i8; 300]>, box_4: fn () -> Box<[i8; 4000]>, ) { println!("Hello World 1"); let _: Box<[i8]> = match 3 { 1 => box_1(), 2 => box_2(), 3 => box_3(), _ => box_4(), }; println!("Hello World 2"); } pub fn main() { fn box_1() -> Box<[i8; 1]> { Box::new( [1i8] ) } fn box_2() -> Box<[i8; 20]> { Box::new( [1i8; 20] ) } fn box_3() -> Box<[i8; 300]> { Box::new( [1i8; 300] ) } fn box_4() -> Box<[i8; 4000]> { Box::new( [1i8; 4000] ) } foo(box_1, box_2, box_3, box_4); } </ins>