Skip to content

Commit 0930b95

Browse files
committed
rustc: Typecheck struct literals
1 parent bf96298 commit 0930b95

File tree

1 file changed

+134
-3
lines changed

1 file changed

+134
-3
lines changed

src/rustc/middle/typeck/check.rs

+134-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ type parameter).
6666
6767
*/
6868

69-
import astconv::{ast_conv, ast_ty_to_ty, ast_region_to_region};
69+
import astconv::{ast_conv, ast_path_to_ty, ast_ty_to_ty};
70+
import astconv::{ast_region_to_region};
7071
import collect::{methods}; // ccx.to_ty()
7172
import middle::ty::{tv_vid, vid};
7273
import regionmanip::{replace_bound_regions_in_fn_ty, region_of};
@@ -76,6 +77,8 @@ import syntax::ast::ty_i;
7677
import typeck::infer::{unify_methods}; // infcx.set()
7778
import typeck::infer::{resolve_type, force_tvar};
7879

80+
import std::map::str_hash;
81+
7982
type fn_ctxt_ =
8083
// var_bindings, locals and next_var_id are shared
8184
// with any nested functions that capture the environment
@@ -1628,8 +1631,136 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16281631
}
16291632
}
16301633
}
1631-
ast::expr_struct(*) {
1632-
fail ~"XXX structs";
1634+
ast::expr_struct(path, fields) {
1635+
// Resolve the path.
1636+
let class_id;
1637+
alt tcx.def_map.find(id) {
1638+
some(ast::def_class(type_def_id)) => {
1639+
class_id = type_def_id;
1640+
}
1641+
_ => {
1642+
tcx.sess.span_bug(path.span, ~"structure constructor does \
1643+
not name a structure type");
1644+
}
1645+
}
1646+
1647+
// Look up the number of type parameters and the raw type, and
1648+
// determine whether the class is region-parameterized.
1649+
let type_parameter_count, region_parameterized, raw_type;
1650+
if class_id.crate == ast::local_crate {
1651+
region_parameterized =
1652+
tcx.region_paramd_items.contains_key(class_id.node);
1653+
alt tcx.items.find(class_id.node) {
1654+
some(ast_map::node_item(@{
1655+
node: ast::item_class(type_parameters, _, _, _, _),
1656+
_
1657+
}, _)) => {
1658+
1659+
type_parameter_count = type_parameters.len();
1660+
1661+
let self_region;
1662+
if region_parameterized {
1663+
self_region = some(ty::re_bound(ty::br_self));
1664+
} else {
1665+
self_region = none;
1666+
}
1667+
1668+
raw_type = ty::mk_class(tcx, class_id, {
1669+
self_r: self_region,
1670+
self_ty: none,
1671+
tps: ty::ty_params_to_tys(tcx, type_parameters)
1672+
});
1673+
}
1674+
_ => {
1675+
tcx.sess.span_bug(expr.span,
1676+
~"resolve didn't map this to a class");
1677+
}
1678+
}
1679+
} else {
1680+
let item_type = ty::lookup_item_type(tcx, class_id);
1681+
type_parameter_count = (*item_type.bounds).len();
1682+
region_parameterized = item_type.rp;
1683+
raw_type = item_type.ty;
1684+
}
1685+
1686+
// Generate the struct type.
1687+
let self_region;
1688+
if region_parameterized {
1689+
self_region = some(fcx.infcx.next_region_var_nb());
1690+
} else {
1691+
self_region = none;
1692+
}
1693+
1694+
let type_parameters = fcx.infcx.next_ty_vars(type_parameter_count);
1695+
let substitutions = {
1696+
self_r: self_region,
1697+
self_ty: none,
1698+
tps: type_parameters
1699+
};
1700+
1701+
let struct_type = ty::subst(tcx, substitutions, raw_type);
1702+
1703+
// Look up the class fields and build up a map.
1704+
let class_fields = ty::lookup_class_fields(tcx, class_id);
1705+
let class_field_map = str_hash();
1706+
let mut fields_found = 0;
1707+
for class_fields.each |field| {
1708+
// XXX: Check visibility here.
1709+
class_field_map.insert(*field.ident, (field.id, false));
1710+
}
1711+
1712+
// Typecheck each field.
1713+
for fields.each |field| {
1714+
alt class_field_map.find(*field.node.ident) {
1715+
none => {
1716+
tcx.sess.span_err(field.span,
1717+
#fmt("structure has no field named \
1718+
field named `%s`",
1719+
*field.node.ident));
1720+
}
1721+
some((_, true)) => {
1722+
tcx.sess.span_err(field.span,
1723+
#fmt("field `%s` specified more than \
1724+
once",
1725+
*field.node.ident));
1726+
}
1727+
some((field_id, false)) => {
1728+
let expected_field_type =
1729+
ty::lookup_field_type(tcx, class_id, field_id,
1730+
substitutions);
1731+
bot |= check_expr(fcx,
1732+
field.node.expr,
1733+
some(expected_field_type));
1734+
fields_found += 1;
1735+
}
1736+
}
1737+
}
1738+
1739+
// Make sure the programmer specified all the fields.
1740+
assert fields_found <= class_fields.len();
1741+
if fields_found < class_fields.len() {
1742+
let mut missing_fields = ~[];
1743+
for class_fields.each |class_field| {
1744+
let name = *class_field.ident;
1745+
let (_, seen) = class_field_map.get(name);
1746+
if !seen {
1747+
vec::push(missing_fields,
1748+
~"`" + name + ~"`");
1749+
}
1750+
}
1751+
1752+
tcx.sess.span_err(expr.span,
1753+
#fmt("missing field%s: %s",
1754+
if missing_fields.len() == 1 {
1755+
~""
1756+
} else {
1757+
~"s"
1758+
},
1759+
str::connect(missing_fields, ~", ")));
1760+
}
1761+
1762+
// Write in the resulting type.
1763+
fcx.write_ty(id, struct_type);
16331764
}
16341765
ast::expr_field(base, field, tys) {
16351766
bot = check_field(fcx, expr, false, base, field, tys);

0 commit comments

Comments
 (0)