diff --git a/src/rustc/middle/privacy.rs b/src/rustc/middle/privacy.rs index 98260a0d0819b..acf56ad81e965 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 6de9ad977c9fb..4d2ced35338b6 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 61d4e6379a296..27caf3cc87cf1 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 a3803ffb91af0..98ff7329b0f5c 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 b0702c80b39bf..df44199b51737 100644 --- a/src/rustc/middle/trans/expr.rs +++ b/src/rustc/middle/trans/expr.rs @@ -855,7 +855,9 @@ fn fn_data_to_datum(bcx: block, return bcx; } -fn with_field_tys(tcx: ty::ctxt, ty: ty::t, +fn with_field_tys(tcx: ty::ctxt, + ty: ty::t, + node_id_opt: Option, op: fn(bool, (&[ty::field])) -> R) -> R { match ty::get(ty).sty { ty::ty_rec(ref fields) => { @@ -867,6 +869,30 @@ fn with_field_tys(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", @@ -882,7 +908,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), @@ -974,9 +1000,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 03e75957a92f1..c81894b2b6659 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -3722,6 +3722,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 0641dc3bd7cf9..099d3634501c7 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; } @@ -2048,6 +2143,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"); @@ -2317,9 +2416,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 262a8121ec537..1660f1e076ce9 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 0000000000000..ae7068495a5bb --- /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 }; +} +