diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 6399fa5eedf..049e2162fe8 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -1701,7 +1701,7 @@ pub fn trans_match_inner(scope_cx: block, None } }; - let lldiscr = discr_datum.to_ref_llval(bcx); + let lldiscr = discr_datum.to_zeroable_ref_llval(bcx); compile_submatch(bcx, matches, [lldiscr], chk); let mut arm_cxs = ~[]; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index 0f325c432f4..c0403083ce1 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -471,6 +471,31 @@ fn to_ref_llval(&self, bcx: block) -> ValueRef { } } + fn to_zeroable_ref_llval(&self, bcx: block) -> ValueRef { + /*! + * Returns a by-ref llvalue that can be zeroed in order to + * cancel cleanup. This is a kind of hokey bridge used + * to adapt to the match code. Please don't use it for new code. + */ + + match self.mode { + // All by-ref datums are zeroable, even if we *could* just + // cancel the cleanup. + ByRef(_) => self.val, + + // By value datums can't be zeroed (where would you store + // the zero?) so we have to spill them. Add a temp cleanup + // for this spilled value and cancel the cleanup on this + // current value. + ByValue => { + let slot = self.to_ref_llval(bcx); + self.cancel_clean(bcx); + add_clean_temp_mem(bcx, slot, self.ty); + slot + } + } + } + fn appropriate_mode(&self) -> DatumMode { /*! See the `appropriate_mode()` function */ diff --git a/src/test/run-pass/match-vec-rvalue.rs b/src/test/run-pass/match-vec-rvalue.rs new file mode 100644 index 00000000000..5f68b0e9a69 --- /dev/null +++ b/src/test/run-pass/match-vec-rvalue.rs @@ -0,0 +1,12 @@ +// Tests that matching rvalues with drops does not crash. + +fn main() { + match ~[1, 2, 3] { + x => { + assert_eq!(x.len(), 3); + assert_eq!(x[0], 1); + assert_eq!(x[1], 2); + assert_eq!(x[2], 3); + } + } +}