Change:
The current translation for struct update in the case of ignore looks incorrect. It should only move those fields that are not specified manually. This can mean the difference between moving the base as a whole and not. Probably the easiest thing to do is to create an alloca, use the same code as the normal path, and just drop the alloca afterwards.
See FIXME.
visiting for triage. nominating for production ready.
Accepted for production-ready
visting for triage, still relevant. FYI, the module in question is

Discussion:
}; if !type_is_zero_size(ccx, result_ty) { <del> let repr = adt::represent_type(ccx, result_ty); </del> match args { callee::ArgExprs(exprs) => { let fields = exprs.iter().map(|x| *x).enumerate().collect::<Vec<_>>(); <del> bcx = expr::trans_adt(bcx, &*repr, disr, fields.as_slice(), </del> <ins> bcx = expr::trans_adt(bcx, result_ty, disr, fields.as_slice(), </ins> None, expr::SaveIn(llresult)); } _ => ccx.sess().bug("expected expr as arguments for variant/struct tuple constructor")

dest) } ast::ExprTup(ref args) => { <del> let repr = adt::represent_type(bcx.ccx(), expr_ty(bcx, expr)); </del> let numbered_fields: Vec<(uint, Gc<ast::Expr>)> = args.iter().enumerate().map(|(i, arg)| (i, *arg)).collect(); <del> trans_adt(bcx, &*repr, 0, numbered_fields.as_slice(), None, dest) </del> <ins> trans_adt(bcx, expr_ty(bcx, expr), 0, numbered_fields.as_slice(), None, dest) </ins> } ast::ExprLit(lit) => { match lit.node {

} }; <del> let repr = adt::represent_type(bcx.ccx(), ty); trans_adt(bcx, &*repr, discr, numbered_fields.as_slice(), optbase, dest) </del> <ins> trans_adt(bcx, ty, discr, numbered_fields.as_slice(), optbase, dest) </ins> }) }

* which remaining fields are copied; see comments on `StructBaseInfo`. */ pub fn trans_adt<'a>(mut bcx: &'a Block<'a>, <del> repr: &adt::Repr, </del> <ins> ty: ty::t, </ins> discr: ty::Disr, fields: &[(uint, Gc<ast::Expr>)], optbase: Option<StructBaseInfo>, dest: Dest) -> &'a Block<'a> { let _icx = push_ctxt("trans_adt"); let fcx = bcx.fcx; <ins> let repr = adt::represent_type(bcx.ccx(), ty); // If we don't care about the result, just make a // temporary stack slot </ins> let addr = match dest { <del> Ignore => { for &(_i, ref e) in fields.iter() { bcx = trans_into(bcx, &**e, Ignore); } for sbi in optbase.iter() { // FIXME #7261: this moves entire base, not just certain fields bcx = trans_into(bcx, &*sbi.expr, Ignore); } return bcx; } SaveIn(pos) => pos </del> <ins> SaveIn(pos) => pos, Ignore => alloc_ty(bcx, ty, "temp"), </ins> }; // This scope holds intermediates that must be cleaned should

let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &*base.expr, "base")); for &(i, t) in base.fields.iter() { let datum = base_datum.get_element( <del> t, |srcval| adt::trans_field_ptr(bcx, repr, srcval, discr, i)); let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); </del> <ins> t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); </ins> bcx = datum.store_to(bcx, dest); } },

// Now, we just overwrite the fields we've explicity specified for &(i, ref e) in fields.iter() { <del> let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i); </del> <ins> let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); </ins> let e_ty = expr_ty_adjusted(bcx, &**e); bcx = trans_into(bcx, &**e, SaveIn(dest)); let scope = cleanup::CustomScope(custom_cleanup_scope);

fcx.schedule_drop_mem(scope, dest, e_ty); } <del> adt::trans_set_discr(bcx, repr, addr, discr); </del> <ins> adt::trans_set_discr(bcx, &*repr, addr, discr); </ins> fcx.pop_custom_cleanup_scope(custom_cleanup_scope); <del> return bcx; </del> <ins> // If we don't care about the result drop the temporary we made match dest { SaveIn(_) => bcx, Ignore => { bcx = glue::drop_ty(bcx, addr, ty); base::call_lifetime_end(bcx, addr); bcx } } </ins> }