Teach typechecker about record types.
This commit is contained in:
parent
6c5a05b819
commit
a94046f5d2
1 changed files with 109 additions and 1 deletions
|
@ -26,6 +26,7 @@ type fn_ctxt = rec(@ty ret_ty,
|
|||
@crate_ctxt ccx);
|
||||
|
||||
type arg = rec(ast.mode mode, @ty ty);
|
||||
type field = rec(ast.ident label, @ty ty);
|
||||
|
||||
// NB: If you change this, you'll probably want to change the corresponding
|
||||
// AST structure in front/ast.rs as well.
|
||||
|
@ -41,6 +42,7 @@ tag sty {
|
|||
ty_box(@ty);
|
||||
ty_vec(@ty);
|
||||
ty_tup(vec[@ty]);
|
||||
ty_rec(vec[field]);
|
||||
ty_fn(vec[arg], @ty); // TODO: effect
|
||||
ty_var(int); // ephemeral type var
|
||||
ty_local(ast.def_id); // type of a local var
|
||||
|
@ -52,6 +54,9 @@ tag type_err {
|
|||
terr_mismatch;
|
||||
terr_tuple_size(uint, uint);
|
||||
terr_tuple_mutability;
|
||||
terr_record_size(uint, uint);
|
||||
terr_record_mutability;
|
||||
terr_record_fields(ast.ident,ast.ident);
|
||||
terr_arg_count;
|
||||
}
|
||||
|
||||
|
@ -78,6 +83,10 @@ fn ast_ty_to_str(&@ast.ty ty) -> str {
|
|||
ret s + ast_ty_to_str(input.ty);
|
||||
}
|
||||
|
||||
fn ast_field_to_str(&tup(ast.ident, @ast.ty) f) -> str {
|
||||
ret ast_ty_to_str(f._1) + " " + f._0;
|
||||
}
|
||||
|
||||
auto s;
|
||||
alt (ty.node) {
|
||||
case (ast.ty_nil) { s = "()"; }
|
||||
|
@ -97,6 +106,14 @@ fn ast_ty_to_str(&@ast.ty ty) -> str {
|
|||
s += ")";
|
||||
}
|
||||
|
||||
case (ast.ty_rec(?elems)) {
|
||||
auto f = ast_field_to_str;
|
||||
s = "rec(";
|
||||
s += _str.connect(_vec.map[tup(ast.ident, @ast.ty),str]
|
||||
(f, elems), ",");
|
||||
s += ")";
|
||||
}
|
||||
|
||||
case (ast.ty_fn(?inputs, ?output)) {
|
||||
auto f = ast_fn_input_to_str;
|
||||
s = "fn(";
|
||||
|
@ -154,6 +171,10 @@ fn ty_to_str(&@ty typ) -> str {
|
|||
ret s + ty_to_str(input.ty);
|
||||
}
|
||||
|
||||
fn field_to_str(&field f) -> str {
|
||||
ret ty_to_str(f.ty) + " " + f.label;
|
||||
}
|
||||
|
||||
auto s = "";
|
||||
if (typ.mut == ast.mut) {
|
||||
s += "mutable ";
|
||||
|
@ -176,6 +197,12 @@ fn ty_to_str(&@ty typ) -> str {
|
|||
s = "tup(" + _str.connect(strs, ",") + ")";
|
||||
}
|
||||
|
||||
case (ty_rec(?elems)) {
|
||||
auto f = field_to_str;
|
||||
auto strs = _vec.map[field,str](f, elems);
|
||||
s = "rec(" + _str.connect(strs, ",") + ")";
|
||||
}
|
||||
|
||||
case (ty_fn(?inputs, ?output)) {
|
||||
auto f = fn_input_to_str;
|
||||
s = "fn(" + _str.connect(_vec.map[arg,str](f, inputs),
|
||||
|
@ -222,6 +249,14 @@ fn ast_ty_to_ty(ty_getter getter, &@ast.ty ast_ty) -> @ty {
|
|||
}
|
||||
sty = ty_tup(flds);
|
||||
}
|
||||
case (ast.ty_rec(?fields)) {
|
||||
let vec[field] flds = vec();
|
||||
for (tup(ast.ident, @ast.ty) f in fields) {
|
||||
append[field](flds, rec(label=f._0,
|
||||
ty=ast_ty_to_ty(getter, f._1)));
|
||||
}
|
||||
sty = ty_rec(flds);
|
||||
}
|
||||
|
||||
case (ast.ty_fn(?inputs, ?output)) {
|
||||
auto f = bind ast_arg_to_arg(getter, _);
|
||||
|
@ -281,6 +316,19 @@ fn type_err_to_str(&type_err err) -> str {
|
|||
case (terr_tuple_mutability) {
|
||||
ret "tuple elements differ in mutability";
|
||||
}
|
||||
case (terr_record_size(?e_sz, ?a_sz)) {
|
||||
ret "expected a record with " + _uint.to_str(e_sz, 10u) +
|
||||
" fields but found one with " + _uint.to_str(a_sz, 10u) +
|
||||
" fields";
|
||||
}
|
||||
case (terr_record_mutability) {
|
||||
ret "record elements differ in mutability";
|
||||
}
|
||||
case (terr_record_fields(?e_fld, ?a_fld)) {
|
||||
ret "expected a record with field '" + e_fld +
|
||||
"' but found one with field '" + a_fld +
|
||||
"'";
|
||||
}
|
||||
case (terr_arg_count) {
|
||||
ret "incorrect number of function parameters";
|
||||
}
|
||||
|
@ -445,8 +493,9 @@ fn type_is_nil(@ty t) -> bool {
|
|||
|
||||
fn type_is_structural(@ty t) -> bool {
|
||||
alt (t.struct) {
|
||||
// FIXME: cover rec and tag when we support them.
|
||||
// FIXME: cover tag when we support it.
|
||||
case (ty_tup(_)) { ret true; }
|
||||
case (ty_rec(_)) { ret true; }
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
@ -726,6 +775,65 @@ fn unify(&fn_ctxt fcx, @ty expected, @ty actual) -> unify_result {
|
|||
}
|
||||
}
|
||||
|
||||
case (ty_rec(?expected_fields)) {
|
||||
alt (actual.struct) {
|
||||
case (ty_rec(?actual_fields)) {
|
||||
auto expected_len = _vec.len[field](expected_fields);
|
||||
auto actual_len = _vec.len[field](actual_fields);
|
||||
if (expected_len != actual_len) {
|
||||
auto err = terr_record_size(expected_len,
|
||||
actual_len);
|
||||
ret ures_err(err, expected, actual);
|
||||
}
|
||||
|
||||
// TODO: implement an iterator that can iterate over
|
||||
// two arrays simultaneously.
|
||||
let vec[field] result_fields = vec();
|
||||
auto i = 0u;
|
||||
while (i < expected_len) {
|
||||
auto expected_field = expected_fields.(i);
|
||||
auto actual_field = actual_fields.(i);
|
||||
if (expected_field.ty.mut != actual_field.ty.mut) {
|
||||
auto err = terr_record_mutability;
|
||||
ret ures_err(err, expected, actual);
|
||||
}
|
||||
|
||||
if (!_str.eq(expected_field.label,
|
||||
actual_field.label)) {
|
||||
auto err =
|
||||
terr_record_fields(expected_field.label,
|
||||
actual_field.label);
|
||||
ret ures_err(err, expected, actual);
|
||||
}
|
||||
|
||||
auto result = unify_step(fcx,
|
||||
bindings,
|
||||
expected_field.ty,
|
||||
actual_field.ty);
|
||||
alt (result) {
|
||||
case (ures_ok(?rty)) {
|
||||
append[field](result_fields,
|
||||
rec(ty=rty with expected_field));
|
||||
}
|
||||
case (_) {
|
||||
ret result;
|
||||
}
|
||||
}
|
||||
|
||||
i += 1u;
|
||||
}
|
||||
|
||||
ret ures_ok(plain_ty(ty_rec(result_fields)));
|
||||
}
|
||||
|
||||
// TODO: ty_var
|
||||
|
||||
case (_) {
|
||||
ret ures_err(terr_mismatch, expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case (ty_fn(?expected_inputs, ?expected_output)) {
|
||||
alt (actual.struct) {
|
||||
case (ty_fn(?actual_inputs, ?actual_output)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue