diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 060dde02c2d..84d464e8f07 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -210,6 +210,7 @@ use trans::datum::*; use trans::debuginfo::{self, DebugLoc, ToDebugLoc}; use trans::expr::{self, Dest}; +use trans::monomorphize; use trans::tvec; use trans::type_of; use middle::ty::{self, Ty}; @@ -1076,9 +1077,39 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) { let repr = adt::represent_type(bcx.ccx(), left_ty); let arg_count = adt::num_args(&*repr, 0); - let field_vals: Vec = (0..arg_count).map(|ix| - adt::trans_field_ptr(bcx, &*repr, val, 0, ix) + let (arg_count, struct_val) = if type_is_sized(bcx.tcx(), left_ty) { + (arg_count, val) + } else { + // For an unsized ADT (i.e. DST struct), we need to treat + // the last field specially: instead of simply passing a + // ValueRef pointing to that field, as with all the others, + // we skip it and instead construct a 'fat ptr' below. + (arg_count - 1, Load(bcx, expr::get_dataptr(bcx, val))) + }; + let mut field_vals: Vec = (0..arg_count).map(|ix| + adt::trans_field_ptr(bcx, &*repr, struct_val, 0, ix) ).collect(); + + match left_ty.sty { + ty::ty_struct(def_id, substs) if !type_is_sized(bcx.tcx(), left_ty) => { + // The last field is technically unsized but + // since we can only ever match that field behind + // a reference we construct a fat ptr here. + let fields = ty::lookup_struct_fields(bcx.tcx(), def_id); + let unsized_ty = fields.iter().last().map(|field| { + let fty = ty::lookup_field_type(bcx.tcx(), def_id, field.id, substs); + monomorphize::normalize_associated_type(bcx.tcx(), &fty) + }).unwrap(); + let llty = type_of::type_of(bcx.ccx(), unsized_ty); + let scratch = alloca_no_lifetime(bcx, llty, "__struct_field_fat_ptr"); + let data = adt::trans_field_ptr(bcx, &*repr, struct_val, 0, arg_count); + let len = Load(bcx, expr::get_len(bcx, val)); + Store(bcx, data, expr::get_dataptr(bcx, scratch)); + Store(bcx, len, expr::get_len(bcx, scratch)); + field_vals.push(scratch); + } + _ => {} + } Some(field_vals) } else if any_uniq_pat(m, col) || any_region_pat(m, col) { Some(vec!(Load(bcx, val))) diff --git a/src/test/run-pass/issue-23261.rs b/src/test/run-pass/issue-23261.rs new file mode 100644 index 00000000000..fc806f5429a --- /dev/null +++ b/src/test/run-pass/issue-23261.rs @@ -0,0 +1,70 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Matching on a DST struct should not trigger an LLVM assertion. + +struct Foo { + a: i32, + inner: T +} + +trait Get { + fn get(&self) -> i32; +} + +impl Get for i32 { + fn get(&self) -> i32 { + *self + } +} + +fn check_val(val: &Foo<[u8]>) { + match *val { + Foo { a, .. } => { + assert_eq!(a, 32); + } + } +} + +fn check_dst_val(val: &Foo<[u8]>) { + match *val { + Foo { ref inner, .. } => { + assert_eq!(inner, [1, 2, 3]); + } + } +} + +fn check_both(val: &Foo<[u8]>) { + match *val { + Foo { a, ref inner } => { + assert_eq!(a, 32); + assert_eq!(inner, [1, 2, 3]); + } + } +} + +fn check_trait_obj(val: &Foo) { + match *val { + Foo { a, ref inner } => { + assert_eq!(a, 32); + assert_eq!(inner.get(), 32); + } + } +} + +fn main() { + let foo: &Foo<[u8]> = &Foo { a: 32, inner: [1, 2, 3] }; + check_val(foo); + check_dst_val(foo); + check_both(foo); + + let foo: &Foo = &Foo { a: 32, inner: 32 }; + check_trait_obj(foo); +}