about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-10-23 15:56:40 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-10-23 19:23:46 -0700
commit61bb3571a59f4659a0a46565c71fa7ecfa352811 (patch)
tree10be016bc2768311197959946e5756b3aeff65f6
parent759e1c165f474314bb113d0b72e8ef85fc3864d7 (diff)
downloadrust-61bb3571a59f4659a0a46565c71fa7ecfa352811.tar.gz
rust-61bb3571a59f4659a0a46565c71fa7ecfa352811.zip
rustc: Implement construction of monomorphic struct-like variants. r=nmatsakis
-rw-r--r--src/rustc/middle/privacy.rs32
-rw-r--r--src/rustc/middle/trans/alt.rs4
-rw-r--r--src/rustc/middle/trans/base.rs2
-rw-r--r--src/rustc/middle/trans/consts.rs6
-rw-r--r--src/rustc/middle/trans/expr.rs71
-rw-r--r--src/rustc/middle/ty.rs11
-rw-r--r--src/rustc/middle/typeck/check.rs224
-rw-r--r--src/rustc/middle/typeck/collect.rs11
-rw-r--r--src/test/run-pass/struct-like-variant-construct.rs15
9 files changed, 303 insertions, 73 deletions
diff --git a/src/rustc/middle/privacy.rs b/src/rustc/middle/privacy.rs
index 98260a0d081..acf56ad81e9 100644
--- a/src/rustc/middle/privacy.rs
+++ b/src/rustc/middle/privacy.rs
@@ -3,11 +3,11 @@
 
 use /*mod*/ syntax::ast;
 use /*mod*/ syntax::visit;
-use syntax::ast::{expr_field, expr_struct, ident, item_class, item_impl};
-use syntax::ast::{item_trait, local_crate, node_id, pat_struct, private};
-use syntax::ast::{provided, required};
+use syntax::ast::{def_variant, expr_field, expr_struct, ident, item_class};
+use syntax::ast::{item_impl, item_trait, local_crate, node_id, pat_struct};
+use syntax::ast::{private, provided, required};
 use syntax::ast_map::{node_item, node_method};
-use ty::ty_class;
+use ty::{ty_class, ty_enum};
 use typeck::{method_map, method_origin, method_param, method_self};
 use typeck::{method_static, method_trait};
 
@@ -188,6 +188,30 @@ fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
                                 }
                             }
                         }
+                        ty_enum(id, _) => {
+                            if id.crate != local_crate ||
+                                    !privileged_items.contains(&(id.node)) {
+                                match tcx.def_map.get(expr.id) {
+                                    def_variant(_, variant_id) => {
+                                        for fields.each |field| {
+                                                debug!("(privacy checking) \
+                                                        checking field in \
+                                                        struct variant \
+                                                        literal");
+                                            check_field(expr.span, variant_id,
+                                                        field.node.ident);
+                                        }
+                                    }
+                                    _ => {
+                                        tcx.sess.span_bug(expr.span,
+                                                          ~"resolve didn't \
+                                                            map enum struct \
+                                                            constructor to a \
+                                                            variant def");
+                                    }
+                                }
+                            }
+                        }
                         _ => {
                             tcx.sess.span_bug(expr.span, ~"struct expr \
                                                            didn't have \
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index 6de9ad977c9..4d2ced35338 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -909,7 +909,7 @@ fn compile_submatch(bcx: block,
     let rec_fields = collect_record_or_struct_fields(m, col);
     if rec_fields.len() > 0 {
         let pat_ty = node_id_type(bcx, pat_id);
-        do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| {
+        do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| {
             let rec_vals = rec_fields.map(|field_name| {
                 let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
                 GEPi(bcx, val, struct_field(ix))
@@ -1257,7 +1257,7 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
         ast::pat_rec(fields, _) | ast::pat_struct(_, fields, _) => {
             let tcx = bcx.tcx();
             let pat_ty = node_id_type(bcx, pat.id);
-            do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| {
+            do expr::with_field_tys(tcx, pat_ty, None) |_hd, field_tys| {
                 for vec::each(fields) |f| {
                     let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
                     let fldptr = GEPi(bcx, val, struct_field(ix));
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index ae69904e219..3896fdf0e7a 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -547,7 +547,7 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
     let mut cx = cx;
     match ty::get(t).sty {
       ty::ty_rec(*) | ty::ty_class(*) => {
-          do expr::with_field_tys(cx.tcx(), t) |_has_dtor, field_tys| {
+          do expr::with_field_tys(cx.tcx(), t, None) |_has_dtor, field_tys| {
               for vec::eachi(field_tys) |i, field_ty| {
                   let llfld_a = GEPi(cx, av, struct_field(i));
                   cx = f(cx, llfld_a, field_ty.mt.ty);
diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs
index a3803ffb91a..98ff7329b0f 100644
--- a/src/rustc/middle/trans/consts.rs
+++ b/src/rustc/middle/trans/consts.rs
@@ -162,7 +162,7 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
           let bt = ty::expr_ty(cx.tcx, base);
           let bv = const_expr(cx, base);
           let (bt, bv) = const_autoderef(cx, bt, bv);
-          do expr::with_field_tys(cx.tcx, bt) |_has_dtor, field_tys| {
+          do expr::with_field_tys(cx.tcx, bt, None) |_has_dtor, field_tys| {
               let ix = ty::field_idx_strict(cx.tcx, field, field_tys);
 
               // Note: ideally, we'd use `struct_field()` here instead
@@ -294,7 +294,9 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
       }
       ast::expr_struct(_, ref fs, _) => {
           let ety = ty::expr_ty(cx.tcx, e);
-          let cs = do expr::with_field_tys(cx.tcx, ety) |_hd, field_tys| {
+          let cs = do expr::with_field_tys(cx.tcx,
+                                           ety,
+                                           None) |_hd, field_tys| {
               field_tys.map(|field_ty| {
                   match fs.find(|f| field_ty.ident == f.node.ident) {
                       Some(f) => const_expr(cx, f.node.expr),
diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs
index a64aac3426a..dee55353604 100644
--- a/src/rustc/middle/trans/expr.rs
+++ b/src/rustc/middle/trans/expr.rs
@@ -850,7 +850,12 @@ fn fn_data_to_datum(bcx: block,
     return bcx;
 }
 
-fn with_field_tys<R>(tcx: ty::ctxt, ty: ty::t,
+// The optional node ID here is the node ID of the path identifying the enum
+// variant in use. If none, this cannot possibly an enum variant (so, if it
+// is and `node_id_opt` is none, this function fails).
+fn with_field_tys<R>(tcx: ty::ctxt,
+                     ty: ty::t,
+                     node_id_opt: Option<ast::node_id>,
                      op: fn(bool, (&[ty::field])) -> R) -> R {
     match ty::get(ty).sty {
         ty::ty_rec(ref fields) => {
@@ -862,6 +867,30 @@ fn with_field_tys<R>(tcx: ty::ctxt, ty: ty::t,
             op(has_dtor, class_items_as_mutable_fields(tcx, did, substs))
         }
 
+        ty::ty_enum(_, ref substs) => {
+            // We want the *variant* ID here, not the enum ID.
+            match node_id_opt {
+                None => {
+                    tcx.sess.bug(fmt!(
+                        "cannot get field types from the enum type %s \
+                         without a node ID",
+                        ty_to_str(tcx, ty)));
+                }
+                Some(node_id) => {
+                    match tcx.def_map.get(node_id) {
+                        ast::def_variant(_, variant_id) => {
+                            op(false, class_items_as_mutable_fields(
+                                tcx, variant_id, substs))
+                        }
+                        _ => {
+                            tcx.sess.bug(~"resolve didn't map this expr to a \
+                                           variant ID")
+                        }
+                    }
+                }
+            }
+        }
+
         _ => {
             tcx.sess.bug(fmt!(
                 "cannot get field types from the type %s",
@@ -877,7 +906,7 @@ fn trans_rec_field(bcx: block,
     let _icx = bcx.insn_ctxt("trans_rec_field");
 
     let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-    do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| {
+    do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
         let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
         DatumBlock {
             datum: base_datum.GEPi(bcx, [0u, 0u, ix], field_tys[ix].mt.ty),
@@ -969,9 +998,45 @@ fn trans_rec_or_struct(bcx: block,
         }
     }
 
+    // If this is a struct-like variant, write in the discriminant if
+    // necessary, position the address at the right location, and cast the
+    // address.
     let ty = node_id_type(bcx, id);
     let tcx = bcx.tcx();
-    do with_field_tys(tcx, ty) |has_dtor, field_tys| {
+    let addr = match ty::get(ty).sty {
+        ty::ty_enum(_, ref substs) => {
+            match tcx.def_map.get(id) {
+                ast::def_variant(enum_id, variant_id) => {
+                    let variant_info = ty::enum_variant_with_id(
+                        tcx, enum_id, variant_id);
+                    let addr = if ty::enum_is_univariant(tcx, enum_id) {
+                        addr
+                    } else {
+                        Store(bcx,
+                              C_int(bcx.ccx(), variant_info.disr_val),
+                              GEPi(bcx, addr, [0, 0]));
+                        GEPi(bcx, addr, [0, 1])
+                    };
+                    let fields = ty::class_items_as_mutable_fields(
+                        tcx, variant_id, substs);
+                    let field_lltys = do fields.map |field| {
+                        type_of(bcx.ccx(),
+                                ty::subst_tps(
+                                    tcx, substs.tps, None, field.mt.ty))
+                    };
+                    PointerCast(bcx, addr,
+                                T_ptr(T_struct(~[T_struct(field_lltys)])))
+                }
+                _ => {
+                    tcx.sess.bug(~"resolve didn't write the right def in for \
+                                   this struct-like variant")
+                }
+            }
+        }
+        _ => addr
+    };
+
+    do with_field_tys(tcx, ty, Some(id)) |has_dtor, field_tys| {
         // evaluate each of the fields and store them into their
         // correct locations
         let mut temp_cleanups = ~[];
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 68d82e4b74b..e065fdfe0bc 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -3721,6 +3721,17 @@ fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> ~[field_ty] {
             _ => cx.sess.bug(~"class ID bound to non-class")
          }
        }
+       Some(ast_map::node_variant(variant, _, _)) => {
+          match variant.node.kind {
+            ast::struct_variant_kind(struct_def) => {
+              class_field_tys(struct_def.fields)
+            }
+            _ => {
+              cx.sess.bug(~"struct ID bound to enum variant that isn't \
+                            struct-like")
+            }
+          }
+       }
        _ => {
            cx.sess.bug(
                fmt!("class ID not bound to an item: %s",
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index e95d92efc93..339a035b35c 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -1443,6 +1443,78 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         return bot;
     }
 
+    fn check_struct_or_variant_fields(fcx: @fn_ctxt,
+                                      span: span,
+                                      class_id: ast::def_id,
+                                      substitutions: &ty::substs,
+                                      field_types: ~[ty::field_ty],
+                                      ast_fields: ~[ast::field],
+                                      check_completeness: bool) -> bool {
+        let tcx = fcx.ccx.tcx;
+        let mut bot = false;
+
+        let class_field_map = HashMap();
+        let mut fields_found = 0;
+        for field_types.each |field| {
+            // XXX: Check visibility here.
+            class_field_map.insert(field.ident, (field.id, false));
+        }
+
+        // Typecheck each field.
+        for ast_fields.each |field| {
+            match class_field_map.find(field.node.ident) {
+                None => {
+                    tcx.sess.span_err(
+                        field.span,
+                        fmt!("structure has no field named field named `%s`",
+                             tcx.sess.str_of(field.node.ident)));
+                }
+                Some((_, true)) => {
+                    tcx.sess.span_err(
+                        field.span,
+                        fmt!("field `%s` specified more than once",
+                             tcx.sess.str_of(field.node.ident)));
+                }
+                Some((field_id, false)) => {
+                    let expected_field_type =
+                        ty::lookup_field_type(
+                            tcx, class_id, field_id, substitutions);
+                    bot |= check_expr(fcx,
+                                      field.node.expr,
+                                      Some(expected_field_type));
+                    fields_found += 1;
+                }
+            }
+        }
+
+        if check_completeness {
+            // Make sure the programmer specified all the fields.
+            assert fields_found <= ast_fields.len();
+            if fields_found < ast_fields.len() {
+                let mut missing_fields = ~[];
+                for ast_fields.each |class_field| {
+                    let name = class_field.node.ident;
+                    let (_, seen) = class_field_map.get(name);
+                    if !seen {
+                        missing_fields.push(
+                            ~"`" + tcx.sess.str_of(name) + ~"`");
+                    }
+                }
+
+                tcx.sess.span_err(span,
+                                  fmt!("missing field%s: %s",
+                                       if missing_fields.len() == 1 {
+                                           ~""
+                                       } else {
+                                           ~"s"
+                                       },
+                                       str::connect(missing_fields, ~", ")));
+            }
+        }
+
+        return bot;
+    }
+
     fn check_struct_constructor(fcx: @fn_ctxt,
                                 id: ast::node_id,
                                 span: syntax::codemap::span,
@@ -1501,76 +1573,99 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
         let struct_type = ty::subst(tcx, &substitutions, raw_type);
 
-        // Look up the class fields and build up a map.
+        // Look up and check the fields.
         let class_fields = ty::lookup_class_fields(tcx, class_id);
-        let class_field_map = HashMap();
-        let mut fields_found = 0;
-        for class_fields.each |field| {
-            // XXX: Check visibility here.
-            class_field_map.insert(field.ident, (field.id, false));
+        bot = check_struct_or_variant_fields(fcx,
+                                             span,
+                                             class_id,
+                                             &substitutions,
+                                             class_fields,
+                                             fields,
+                                             base_expr.is_none()) || bot;
+
+        // Check the base expression if necessary.
+        match base_expr {
+            None => {}
+            Some(base_expr) => {
+                bot = check_expr(fcx, base_expr, Some(struct_type)) || bot
+            }
         }
 
-        // Typecheck each field.
-        for fields.each |field| {
-            match class_field_map.find(field.node.ident) {
-                None => {
-                    tcx.sess.span_err(
-                        field.span,
-                        fmt!("structure has no field named field named `%s`",
-                             tcx.sess.str_of(field.node.ident)));
-                }
-                Some((_, true)) => {
-                    tcx.sess.span_err(
-                        field.span,
-                        fmt!("field `%s` specified more than once",
-                             tcx.sess.str_of(field.node.ident)));
+        // Write in the resulting type.
+        fcx.write_ty(id, struct_type);
+        return bot;
+    }
+
+    fn check_struct_enum_variant(fcx: @fn_ctxt,
+                                 id: ast::node_id,
+                                 span: syntax::codemap::span,
+                                 enum_id: ast::def_id,
+                                 variant_id: ast::def_id,
+                                 fields: ~[ast::field]) -> bool {
+        let mut bot = false;
+        let tcx = fcx.ccx.tcx;
+
+        // Look up the number of type parameters and the raw type, and
+        // determine whether the enum is region-parameterized.
+        let type_parameter_count, region_parameterized, raw_type;
+        if enum_id.crate == ast::local_crate {
+            region_parameterized =
+                tcx.region_paramd_items.find(enum_id.node);
+            match tcx.items.find(enum_id.node) {
+                Some(ast_map::node_item(@{
+                        node: ast::item_enum(_, type_parameters),
+                        _
+                    }, _)) => {
+
+                    type_parameter_count = type_parameters.len();
+
+                    let self_region =
+                        bound_self_region(region_parameterized);
+
+                    raw_type = ty::mk_enum(tcx, enum_id, {
+                        self_r: self_region,
+                        self_ty: None,
+                        tps: ty::ty_params_to_tys(tcx, type_parameters)
+                    });
                 }
-                Some((field_id, false)) => {
-                    let expected_field_type =
-                        ty::lookup_field_type(tcx, class_id, field_id,
-                                              &substitutions);
-                    bot |= check_expr(fcx,
-                                      field.node.expr,
-                                      Some(expected_field_type));
-                    fields_found += 1;
+                _ => {
+                    tcx.sess.span_bug(span,
+                                      ~"resolve didn't map this to an enum");
                 }
             }
+        } else {
+            let item_type = ty::lookup_item_type(tcx, enum_id);
+            type_parameter_count = (*item_type.bounds).len();
+            region_parameterized = item_type.region_param;
+            raw_type = item_type.ty;
         }
 
-        match base_expr {
-            None => {
-                // Make sure the programmer specified all the fields.
-                assert fields_found <= class_fields.len();
-                if fields_found < class_fields.len() {
-                    let mut missing_fields = ~[];
-                    for class_fields.each |class_field| {
-                        let name = class_field.ident;
-                        let (_, seen) = class_field_map.get(name);
-                        if !seen {
-                            missing_fields.push(
-                                ~"`" + tcx.sess.str_of(name) + ~"`");
-                        }
-                    }
+        // Generate the enum type.
+        let self_region =
+            fcx.region_var_if_parameterized(region_parameterized,
+                                            span,
+                                            ty::re_scope(id));
+        let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
+        let substitutions = {
+            self_r: self_region,
+            self_ty: None,
+            tps: type_parameters
+        };
 
-                    tcx.sess.span_err(span,
-                                      fmt!("missing field%s: %s",
-                                           if missing_fields.len() == 1 {
-                                               ~""
-                                           } else {
-                                               ~"s"
-                                           },
-                                           str::connect(missing_fields,
-                                                        ~", ")));
-                }
-            }
-            Some(base_expr) => {
-                // Just check the base expression.
-                check_expr(fcx, base_expr, Some(struct_type));
-            }
-        }
+        let enum_type = ty::subst(tcx, &substitutions, raw_type);
+
+        // Look up and check the enum variant fields.
+        let variant_fields = ty::lookup_class_fields(tcx, variant_id);
+        bot = check_struct_or_variant_fields(fcx,
+                                             span,
+                                             variant_id,
+                                             &substitutions,
+                                             variant_fields,
+                                             fields,
+                                             true) || bot;
 
         // Write in the resulting type.
-        fcx.write_ty(id, struct_type);
+        fcx.write_ty(id, enum_type);
         return bot;
     }
 
@@ -2045,6 +2140,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                 check_struct_constructor(fcx, id, expr.span, type_def_id,
                                          fields, base_expr);
             }
+            Some(ast::def_variant(enum_id, variant_id)) => {
+                check_struct_enum_variant(fcx, id, expr.span, enum_id,
+                                          variant_id, fields);
+            }
             _ => {
                 tcx.sess.span_bug(path.span, ~"structure constructor does \
                                                not name a structure type");
@@ -2314,9 +2413,14 @@ fn check_enum_variants(ccx: @crate_ctxt,
                 ast::tuple_variant_kind(args) if args.len() > 0u => {
                     arg_tys = Some(ty::ty_fn_args(ctor_ty).map(|a| a.ty));
                 }
-                ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) => {
+                ast::tuple_variant_kind(_) => {
                     arg_tys = Some(~[]);
                 }
+                ast::struct_variant_kind(_) => {
+                    arg_tys = Some(ty::lookup_class_fields(
+                        ccx.tcx, local_def(v.node.id)).map(|cf|
+                            ty::node_id_to_type(ccx.tcx, cf.id.node)));
+                }
                 ast::enum_variant_kind(_) => {
                     arg_tys = None;
                     do_check(ccx, sp, vs, id, disr_vals, disr_val, variants);
diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs
index 262a8121ec5..1660f1e076c 100644
--- a/src/rustc/middle/typeck/collect.rs
+++ b/src/rustc/middle/typeck/collect.rs
@@ -139,9 +139,18 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
                                 output: enum_ty}
                 }));
             }
-            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) => {
+            ast::tuple_variant_kind(_) => {
                 result_ty = Some(enum_ty);
             }
+            ast::struct_variant_kind(struct_def) => {
+                result_ty = Some(enum_ty);
+                // XXX: Merge with computation of the the same value below?
+                let tpt = {bounds: ty_param_bounds(ccx, ty_params),
+                           region_param: rp,
+                           ty: enum_ty};
+                convert_struct(
+                    ccx, rp, struct_def, ty_params, tpt, variant.node.id);
+            }
             ast::enum_variant_kind(enum_definition) => {
                 get_enum_variant_types(ccx, enum_ty, enum_definition.variants,
                                        ty_params, rp);
diff --git a/src/test/run-pass/struct-like-variant-construct.rs b/src/test/run-pass/struct-like-variant-construct.rs
new file mode 100644
index 00000000000..ae7068495a5
--- /dev/null
+++ b/src/test/run-pass/struct-like-variant-construct.rs
@@ -0,0 +1,15 @@
+enum Foo {
+    Bar {
+        a: int,
+        b: int
+    },
+    Baz {
+        c: float,
+        d: float
+    }
+}
+
+fn main() {
+    let x = Bar { a: 2, b: 3 };
+}
+