about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-05-03 19:08:06 +0000
committerbors <bors@rust-lang.org>2015-05-03 19:08:06 +0000
commit6b3d66b04f9ade6b3a46db4eb188e7397b44117a (patch)
treed6a14c0c0226e23cf147e50011a545d0e86c9367
parent1a60dc4fc4b66760b71f1700cdb8b151cb8a67d9 (diff)
parent715605faf9d32988bc7b2135a711c08e42a1871e (diff)
downloadrust-6b3d66b04f9ade6b3a46db4eb188e7397b44117a.tar.gz
rust-6b3d66b04f9ade6b3a46db4eb188e7397b44117a.zip
Auto merge of #25060 - luqmana:matching-dst-struct, r=huonw
Fixes #23261.

cc @blaenk
-rw-r--r--src/librustc_trans/trans/_match.rs35
-rw-r--r--src/test/run-pass/issue-23261.rs70
2 files changed, 103 insertions, 2 deletions
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::consts;
 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<ValueRef> = (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<ValueRef> = (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 <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.
+
+// Matching on a DST struct should not trigger an LLVM assertion.
+
+struct Foo<T: ?Sized> {
+    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<Get>) {
+    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<Get> = &Foo { a: 32, inner: 32 };
+    check_trait_obj(foo);
+}