rustc: Implement construction of monomorphic struct-like variants. r=nmatsakis
This commit is contained in:
parent
759e1c165f
commit
61bb3571a5
9 changed files with 305 additions and 75 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 = ~[];
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
15
src/test/run-pass/struct-like-variant-construct.rs
Normal file
15
src/test/run-pass/struct-like-variant-construct.rs
Normal 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 };
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue