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);
|
@crate_ctxt ccx);
|
||||||
|
|
||||||
type arg = rec(ast.mode mode, @ty ty);
|
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
|
// NB: If you change this, you'll probably want to change the corresponding
|
||||||
// AST structure in front/ast.rs as well.
|
// AST structure in front/ast.rs as well.
|
||||||
|
@ -41,6 +42,7 @@ tag sty {
|
||||||
ty_box(@ty);
|
ty_box(@ty);
|
||||||
ty_vec(@ty);
|
ty_vec(@ty);
|
||||||
ty_tup(vec[@ty]);
|
ty_tup(vec[@ty]);
|
||||||
|
ty_rec(vec[field]);
|
||||||
ty_fn(vec[arg], @ty); // TODO: effect
|
ty_fn(vec[arg], @ty); // TODO: effect
|
||||||
ty_var(int); // ephemeral type var
|
ty_var(int); // ephemeral type var
|
||||||
ty_local(ast.def_id); // type of a local var
|
ty_local(ast.def_id); // type of a local var
|
||||||
|
@ -52,6 +54,9 @@ tag type_err {
|
||||||
terr_mismatch;
|
terr_mismatch;
|
||||||
terr_tuple_size(uint, uint);
|
terr_tuple_size(uint, uint);
|
||||||
terr_tuple_mutability;
|
terr_tuple_mutability;
|
||||||
|
terr_record_size(uint, uint);
|
||||||
|
terr_record_mutability;
|
||||||
|
terr_record_fields(ast.ident,ast.ident);
|
||||||
terr_arg_count;
|
terr_arg_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +83,10 @@ fn ast_ty_to_str(&@ast.ty ty) -> str {
|
||||||
ret s + ast_ty_to_str(input.ty);
|
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;
|
auto s;
|
||||||
alt (ty.node) {
|
alt (ty.node) {
|
||||||
case (ast.ty_nil) { s = "()"; }
|
case (ast.ty_nil) { s = "()"; }
|
||||||
|
@ -97,6 +106,14 @@ fn ast_ty_to_str(&@ast.ty ty) -> str {
|
||||||
s += ")";
|
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)) {
|
case (ast.ty_fn(?inputs, ?output)) {
|
||||||
auto f = ast_fn_input_to_str;
|
auto f = ast_fn_input_to_str;
|
||||||
s = "fn(";
|
s = "fn(";
|
||||||
|
@ -154,6 +171,10 @@ fn ty_to_str(&@ty typ) -> str {
|
||||||
ret s + ty_to_str(input.ty);
|
ret s + ty_to_str(input.ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_to_str(&field f) -> str {
|
||||||
|
ret ty_to_str(f.ty) + " " + f.label;
|
||||||
|
}
|
||||||
|
|
||||||
auto s = "";
|
auto s = "";
|
||||||
if (typ.mut == ast.mut) {
|
if (typ.mut == ast.mut) {
|
||||||
s += "mutable ";
|
s += "mutable ";
|
||||||
|
@ -176,6 +197,12 @@ fn ty_to_str(&@ty typ) -> str {
|
||||||
s = "tup(" + _str.connect(strs, ",") + ")";
|
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)) {
|
case (ty_fn(?inputs, ?output)) {
|
||||||
auto f = fn_input_to_str;
|
auto f = fn_input_to_str;
|
||||||
s = "fn(" + _str.connect(_vec.map[arg,str](f, inputs),
|
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);
|
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)) {
|
case (ast.ty_fn(?inputs, ?output)) {
|
||||||
auto f = bind ast_arg_to_arg(getter, _);
|
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) {
|
case (terr_tuple_mutability) {
|
||||||
ret "tuple elements differ in 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) {
|
case (terr_arg_count) {
|
||||||
ret "incorrect number of function parameters";
|
ret "incorrect number of function parameters";
|
||||||
}
|
}
|
||||||
|
@ -445,8 +493,9 @@ fn type_is_nil(@ty t) -> bool {
|
||||||
|
|
||||||
fn type_is_structural(@ty t) -> bool {
|
fn type_is_structural(@ty t) -> bool {
|
||||||
alt (t.struct) {
|
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_tup(_)) { ret true; }
|
||||||
|
case (ty_rec(_)) { ret true; }
|
||||||
}
|
}
|
||||||
ret false;
|
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)) {
|
case (ty_fn(?expected_inputs, ?expected_output)) {
|
||||||
alt (actual.struct) {
|
alt (actual.struct) {
|
||||||
case (ty_fn(?actual_inputs, ?actual_output)) {
|
case (ty_fn(?actual_inputs, ?actual_output)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue