Change:
Rust compiler (rustc 1.1.0-nightly ( 2015-05-14) (built 2015-05-14) fails to compile this source: With the error: I can reproduce this ICE also on
Reduced it a bit:
The broken IR:
Looking through it quickly, it seems like the order of the cleanups is messed up? If is then from the switch we go to the label which just jumps to where we memcpy the value for into the alloca and then continue to the label. This is where the problem is since it tries to call on the value returned from . But that's the thing, we wouldn't have called in that arm of the branch and hence shouldn't need to call free either.
Adding a block around suppresses the error. I feel like we had a similar problem not too long ago.
This is "caused" by the micro optimization for dereferencing owned pointer rvalues in . Since is the "trailing" expression for the arm its cleanup will be scheduled in the same scope as the match expression. cc
Either way, this is a dupe of I believe had an idea for a fix.
it's simple, we remove the micro-optimization and always return an datum from . The possibility of returning datum also caused issues with field accesses - see for that fix. We have a static type system and the / marker types are there for a reason. If there is no answer to "how is an rvalue dereference or field meaningful?", then the return type for both of those should be .

Discussion:
//! `&foo()` or `match foo() { ref x => ... }`, where the user is //! implicitly requesting a temporary. //! <del> //! Somewhat surprisingly, not all lvalue expressions yield lvalue datums //! when trans'd. Ultimately the reason for this is to micro-optimize //! the resulting LLVM. For example, consider the following code: //! //!     fn foo() -> Box<int> { ... } //!     let x = *foo(); //! //! The expression `*foo()` is an lvalue, but if you invoke `expr::trans`, //! it will return an rvalue datum. See `deref_once` in expr.rs for //! more details. //! </del> //! ### Rvalues in detail //! //! Rvalues datums are values with no cleanup scheduled. One must be

let r = match datum.ty.sty { ty::ty_uniq(content_ty) => { <ins> // Make sure we have an lvalue datum here to get the // proper cleanups scheduled let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </ins> if type_is_sized(bcx.tcx(), content_ty) { <del> deref_owned_pointer(bcx, expr, datum, content_ty) </del> <ins> let ptr = load_ty(bcx, datum.val, datum.ty); DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) </ins> } else { // A fat pointer and a DST lvalue have the same representation // just different types. Since there is no temporary for `*e` // here (because it is unsized), we cannot emulate the sized // object code path for running drop glue and free. Instead, // we schedule cleanup for `e`, turning it into an lvalue. <del> let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </del> let datum = Datum::new(datum.val, content_ty, LvalueExpr); DatumBlock::new(bcx, datum)

expr.id, method_call, r.datum.to_string(ccx)); return r; <del> /// We microoptimize derefs of owned pointers a bit here. Basically, the idea is to make the /// deref of an rvalue result in an rvalue. This helps to avoid intermediate stack slots in the /// resulting LLVM. The idea here is that, if the `Box<T>` pointer is an rvalue, then we can /// schedule a *shallow* free of the `Box<T>` pointer, and then return a ByRef rvalue into the /// pointer. Because the free is shallow, it is legit to return an rvalue, because we know that /// the contents are not yet scheduled to be freed. The language rules ensure that the contents /// will be used (or moved) before the free occurs. fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr, datum: Datum<'tcx, Expr>, content_ty: Ty<'tcx>) -> DatumBlock<'blk, 'tcx, Expr> { match datum.kind { RvalueExpr(Rvalue { mode: ByRef }) => { let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); let ptr = Load(bcx, datum.val); if !type_is_zero_size(bcx.ccx(), content_ty) { bcx.fcx.schedule_free_value(scope, ptr, cleanup::HeapExchange, content_ty); } } RvalueExpr(Rvalue { mode: ByValue }) => { let scope = cleanup::temporary_scope(bcx.tcx(), expr.id); if !type_is_zero_size(bcx.ccx(), content_ty) { bcx.fcx.schedule_free_value(scope, datum.val, cleanup::HeapExchange, content_ty); } } LvalueExpr => { } } // If we had an rvalue in, we produce an rvalue out. let (llptr, kind) = match datum.kind { LvalueExpr => { (Load(bcx, datum.val), LvalueExpr) } RvalueExpr(Rvalue { mode: ByRef }) => { (Load(bcx, datum.val), RvalueExpr(Rvalue::new(ByRef))) } RvalueExpr(Rvalue { mode: ByValue }) => { (datum.val, RvalueExpr(Rvalue::new(ByRef))) } }; let datum = Datum { ty: content_ty, val: llptr, kind: kind }; DatumBlock { bcx: bcx, datum: datum } } </del> } #[derive(Debug)]

<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. // This used to generate invalid IR in that even if we took the // `false` branch we'd still try to free the Box from the other // arm. This was due to treating `*Box::new(9)` as an rvalue datum // instead of as an lvalue. fn test(foo: bool) -> u8 { match foo { true => *Box::new(9), false => 0 } } fn main() { assert_eq!(9, test(true)); } </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. #[derive(Clone, Debug, PartialEq)] enum Expression { Dummy, Add(Box<Expression>), } use Expression::*; fn simplify(exp: Expression) -> Expression { match exp { Add(n) => *n.clone(), _ => Dummy } } fn main() { assert_eq!(simplify(Add(Box::new(Dummy))), Dummy); } </ins>