rustc: Implement construction of monomorphic struct-like variants. r=nmatsakis

This commit is contained in:
Patrick Walton 2012-10-23 15:56:40 -07:00
parent 759e1c165f
commit 61bb3571a5
9 changed files with 305 additions and 75 deletions

View file

@ -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 \

View file

@ -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));

View file

@ -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);

View file

@ -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),

View file

@ -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 = ~[];

View file

@ -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",

View file

@ -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,71 +1573,21 @@ 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));
}
// 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)));
}
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;
}
}
}
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 => {
// 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) + ~"`");
}
}
tcx.sess.span_err(span,
fmt!("missing field%s: %s",
if missing_fields.len() == 1 {
~""
} else {
~"s"
},
str::connect(missing_fields,
~", ")));
}
}
None => {}
Some(base_expr) => {
// Just check the base expression.
check_expr(fcx, base_expr, Some(struct_type));
bot = check_expr(fcx, base_expr, Some(struct_type)) || bot
}
}
@ -1574,6 +1596,79 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
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)
});
}
_ => {
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;
}
// 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
};
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, enum_type);
return bot;
}
let tcx = fcx.ccx.tcx;
let id = expr.id;
let mut bot = false;
@ -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);

View file

@ -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);

View file

@ -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 };
}