about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-01-15 19:44:33 +0000
committerbors <bors@rust-lang.org>2016-01-15 19:44:33 +0000
commitf4befa6712487141610b2a3618384a238b8a4409 (patch)
treecd7f9b7328b367f55cc928225bd83d54e66797fc
parentdd51c3a7c1f69a2160da914d1f308916263b7044 (diff)
parent7d6da8e4216544aa5dc33f7079789a69484e730c (diff)
downloadrust-f4befa6712487141610b2a3618384a238b8a4409.tar.gz
rust-f4befa6712487141610b2a3618384a238b8a4409.zip
Auto merge of #30890 - nagisa:mir-tuple-adts, r=nikomatsakis
This PR changes translation of tuple-like ADTs from being calls to being proper aggregates. This change is done in hope to make code generation better. Namely, now we can avoid:

1. Call overhead;
2. Generating landingpads in presence of cleanups (we know for sure constructing ADTs can’t panic);
3. And probably much more, gaining better MIR introspectablilty.

Along with that a few serious deficiencies with translation of ADTs and switches have been fixed as well (commits 2 and 3).

r? @nikomatsakis
cc @tsion
-rw-r--r--src/librustc_mir/hair/cx/expr.rs50
-rw-r--r--src/librustc_trans/trans/mir/block.rs9
-rw-r--r--src/librustc_trans/trans/mir/rvalue.rs14
-rw-r--r--src/test/run-pass/mir_adt_construction.rs77
-rw-r--r--src/test/run-pass/mir_build_match_comparisons.rs24
5 files changed, 152 insertions, 22 deletions
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 012bd9691be..8090fca66bb 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -53,7 +53,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                     // Find the actual method implementation being called and
                     // build the appropriate UFCS call expression with the
                     // callee-object as self parameter.
-
                     let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
                     let mut argrefs = vec![fun.to_ref()];
                     argrefs.extend(args.iter().map(|a| a.to_ref()));
@@ -64,12 +63,40 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
                         args: argrefs,
                     }
                 } else {
-                    ExprKind::Call {
-                        ty: &cx.tcx.node_id_to_type(fun.id),
-                        fun: fun.to_ref(),
-                        args: args.to_ref(),
+                    let adt_data = if let hir::ExprPath(..) = fun.node {
+                        // Tuple-like ADTs are represented as ExprCall. We convert them here.
+                        expr_ty.ty_adt_def().and_then(|adt_def|{
+                            match cx.tcx.def_map.borrow()[&fun.id].full_def() {
+                                def::DefVariant(_, variant_id, false) => {
+                                    Some((adt_def, adt_def.variant_index_with_id(variant_id)))
+                                },
+                                def::DefStruct(_) => {
+                                    Some((adt_def, 0))
+                                },
+                                _ => None
+                            }
+                        })
+                    } else { None };
+                    if let Some((adt_def, index)) = adt_data {
+                        let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(fun.id).substs);
+                        let field_refs = args.iter().enumerate().map(|(idx, e)| FieldExprRef {
+                            name: Field::new(idx),
+                            expr: e.to_ref()
+                        }).collect();
+                        ExprKind::Adt {
+                            adt_def: adt_def,
+                            substs: substs,
+                            variant_index: index,
+                            fields: field_refs,
+                            base: None
+                        }
+                    } else {
+                        ExprKind::Call {
+                            ty: cx.tcx.node_id_to_type(fun.id),
+                            fun: fun.to_ref(),
+                            args: args.to_ref(),
+                        }
                     }
-
                 }
             }
 
@@ -549,10 +576,11 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
         def::DefFn(def_id, _) => (def_id, ItemKind::Function),
         def::DefMethod(def_id) => (def_id, ItemKind::Method),
         def::DefStruct(def_id) => match cx.tcx.node_id_to_type(expr.id).sty {
-            // A tuple-struct constructor.
+            // A tuple-struct constructor. Should only be reached if not called in the same
+            // expression.
             ty::TyBareFn(..) => (def_id, ItemKind::Function),
-            // This is a special case: a unit struct which is used as a value. We return a
-            // completely different ExprKind here to account for this special case.
+            // A unit struct which is used as a value. We return a completely different ExprKind
+            // here to account for this special case.
             ty::TyStruct(adt_def, substs) => return ExprKind::Adt {
                 adt_def: adt_def,
                 variant_index: 0,
@@ -563,7 +591,8 @@ fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr)
             ref sty => panic!("unexpected sty: {:?}", sty)
         },
         def::DefVariant(enum_id, variant_id, false) => match cx.tcx.node_id_to_type(expr.id).sty {
-            // A variant constructor.
+            // A variant constructor. Should only be reached if not called in the same
+            // expression.
             ty::TyBareFn(..) => (variant_id, ItemKind::Function),
             // A unit variant, similar special case to the struct case above.
             ty::TyEnum(adt_def, substs) => {
@@ -900,6 +929,7 @@ fn loop_label<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Cod
     }
 }
 
+/// Converts a list of named fields (i.e. for struct-like struct/enum ADTs) into FieldExprRef.
 fn field_refs<'tcx>(variant: VariantDef<'tcx>,
                     fields: &'tcx [hir::Field])
                     -> Vec<FieldExprRef<'tcx>>
diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs
index 18a9aad0e91..ad5b0069a48 100644
--- a/src/librustc_trans/trans/mir/block.rs
+++ b/src/librustc_trans/trans/mir/block.rs
@@ -48,11 +48,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
 
             mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => {
-                let adt_ty = bcx.tcx().lookup_item_type(adt_def.did).ty;
-                let represented_ty = adt::represent_type(bcx.ccx(), adt_ty);
-
                 let discr_lvalue = self.trans_lvalue(bcx, discr);
-                let discr = adt::trans_get_discr(bcx, &represented_ty, discr_lvalue.llval, None);
+                let ty = discr_lvalue.ty.to_ty(bcx.tcx());
+                let repr = adt::represent_type(bcx.ccx(), ty);
+                let discr = adt::trans_get_discr(bcx, &repr, discr_lvalue.llval, None);
 
                 // The else branch of the Switch can't be hit, so branch to an unreachable
                 // instruction so LLVM knows that
@@ -61,7 +60,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len());
                 assert_eq!(adt_def.variants.len(), targets.len());
                 for (adt_variant, target) in adt_def.variants.iter().zip(targets) {
-                    let llval = adt::trans_case(bcx, &*represented_ty, adt_variant.disr_val);
+                    let llval = adt::trans_case(bcx, &*repr, adt_variant.disr_val);
                     let llbb = self.llblock(*target);
 
                     build::AddCase(switch, llval, llbb)
diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs
index f0842554277..7b9eac4317d 100644
--- a/src/librustc_trans/trans/mir/rvalue.rs
+++ b/src/librustc_trans/trans/mir/rvalue.rs
@@ -98,13 +98,19 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
             mir::Rvalue::Aggregate(ref kind, ref operands) => {
                 match *kind {
-                    // Unit struct or variant; both are translated very differently compared to any
-                    // other aggregate
-                    mir::AggregateKind::Adt(adt_def, index, _)
-                    if adt_def.variants[index].kind() == ty::VariantKind::Unit => {
+                    mir::AggregateKind::Adt(adt_def, index, _) => {
                         let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx()));
                         let disr = adt_def.variants[index].disr_val;
                         adt::trans_set_discr(bcx, &*repr, dest.llval, disr);
+                        for (i, operand) in operands.iter().enumerate() {
+                            let op = self.trans_operand(bcx, operand);
+                            // Do not generate stores and GEPis for zero-sized fields.
+                            if !common::type_is_zero_size(bcx.ccx(), op.ty) {
+                                let val = adt::MaybeSizedValue::sized(dest.llval);
+                                let lldest_i = adt::trans_field_ptr(bcx, &*repr, val, disr, i);
+                                self.store_operand(bcx, lldest_i, op);
+                            }
+                        }
                     },
                     _ => {
                         for (i, operand) in operands.iter().enumerate() {
diff --git a/src/test/run-pass/mir_adt_construction.rs b/src/test/run-pass/mir_adt_construction.rs
new file mode 100644
index 00000000000..4526c40af84
--- /dev/null
+++ b/src/test/run-pass/mir_adt_construction.rs
@@ -0,0 +1,77 @@
+// 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.
+
+#![feature(rustc_attrs)]
+
+#[repr(C, u32)]
+enum CEnum {
+    Hello = 30,
+    World = 60
+}
+
+#[rustc_mir]
+fn test1(c: CEnum) -> i32 {
+  let c2 = CEnum::Hello;
+  match (c, c2) {
+    (CEnum::Hello, CEnum::Hello) => 42,
+    (CEnum::World, CEnum::Hello) => 0,
+    _ => 1
+  }
+}
+
+#[repr(packed)]
+#[derive(PartialEq, Debug)]
+struct Pakd {
+    a: u64,
+    b: u32,
+    c: u16,
+    d: u8,
+    e: ()
+}
+
+impl Drop for Pakd {
+    fn drop(&mut self) {}
+}
+
+#[rustc_mir]
+fn test2() -> Pakd {
+    Pakd { a: 42, b: 42, c: 42, d: 42, e: () }
+}
+
+#[derive(PartialEq, Debug)]
+struct TupleLike(u64, u32);
+
+#[rustc_mir]
+fn test3() -> TupleLike {
+    TupleLike(42, 42)
+}
+
+#[rustc_mir]
+fn test4(x: fn(u64, u32) -> TupleLike) -> (TupleLike, TupleLike) {
+    let y = TupleLike;
+    (x(42, 84), y(42, 84))
+}
+
+#[rustc_mir]
+fn test5(x: fn(u32) -> Option<u32>) -> (Option<u32>, Option<u32>) {
+    let y = Some;
+    (x(42), y(42))
+}
+
+fn main() {
+  assert_eq!(test1(CEnum::Hello), 42);
+  assert_eq!(test1(CEnum::World), 0);
+  assert_eq!(test2(), Pakd { a: 42, b: 42, c: 42, d: 42, e: () });
+  assert_eq!(test3(), TupleLike(42, 42));
+  let t4 = test4(TupleLike);
+  assert_eq!(t4.0, t4.1);
+  let t5 = test5(Some);
+  assert_eq!(t5.0, t5.1);
+}
diff --git a/src/test/run-pass/mir_build_match_comparisons.rs b/src/test/run-pass/mir_build_match_comparisons.rs
index 19266191d44..ad24e39d4f9 100644
--- a/src/test/run-pass/mir_build_match_comparisons.rs
+++ b/src/test/run-pass/mir_build_match_comparisons.rs
@@ -11,7 +11,7 @@
 #![feature(rustc_attrs)]
 
 #[rustc_mir]
-pub fn test1(x: i8) -> i32 {
+fn test1(x: i8) -> i32 {
   match x {
     1...10 => 0,
     _ => 1,
@@ -22,7 +22,7 @@ const U: Option<i8> = Some(10);
 const S: &'static str = "hello";
 
 #[rustc_mir]
-pub fn test2(x: i8) -> i32 {
+fn test2(x: i8) -> i32 {
   match Some(x) {
     U => 0,
     _ => 1,
@@ -30,13 +30,28 @@ pub fn test2(x: i8) -> i32 {
 }
 
 #[rustc_mir]
-pub fn test3(x: &'static str) -> i32 {
+fn test3(x: &'static str) -> i32 {
   match x {
     S => 0,
     _ => 1,
   }
 }
 
+enum Opt<T> {
+    Some { v: T },
+    None
+}
+
+#[rustc_mir]
+fn test4(x: u64) -> i32 {
+  let opt = Opt::Some{ v: x };
+  match opt {
+    Opt::Some { v: 10 } => 0,
+    _ => 1,
+  }
+}
+
+
 fn main() {
   assert_eq!(test1(0), 1);
   assert_eq!(test1(1), 0);
@@ -52,4 +67,7 @@ fn main() {
   assert_eq!(test3("hello"), 0);
   assert_eq!(test3(""), 1);
   assert_eq!(test3("world"), 1);
+  assert_eq!(test4(10), 0);
+  assert_eq!(test4(0), 1);
+  assert_eq!(test4(20), 1);
 }