Change:
Original testcase by (reduced from ): The debug log reveals that a (shallow free) cleanup of the rvalue is scheduled in the scope of AST node , while the dereference expression itself has id : reveals that id refers to the entire function's block: This means the cleanup of a value valid only within a match arm is scheduled outside the arm. cc
Dupe of Leaving open for now but we can close either.
This is caused by a micro-optimization specific to dereferences of rvalues. Is this used oftenly enough to justify its existence in the first place? Even if it is, I guess we can disable it in problematic cases like this, where the temporary scope of the match arm's RHS is outside the arm.
I think we can probably remove it, in general removing special treatment of seems like a Good Thing
Triage: this still ICEs.
can you point me/us at the micro-optimization that you mentioned above, so that I (we) can try removing it ourselves and measure the impact?
I only vaguely recall this issue, but I'm pretty sure I was talking about .
triage: still ICEs.
Indeed, it doesn't even need : Does this at least continue to ICE even if LLVM is compiled without assertions? If not, we may want to up the priority on this ticket.

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>