aboutsummaryrefslogtreecommitdiff
path: root/src/comp
diff options
context:
space:
mode:
authorPatrick Walton <[email protected]>2010-11-03 16:43:12 -0700
committerPatrick Walton <[email protected]>2010-11-19 17:50:45 -0800
commitc00bda539d79e4411e086d505c697662942ee00b (patch)
treeac121a5bf1275b57770add6e443434fa13eb6915 /src/comp
parentrustboot: Say when a binary operator is unimplemented rather than asserting i... (diff)
downloadrust-c00bda539d79e4411e086d505c697662942ee00b.tar.xz
rust-c00bda539d79e4411e086d505c697662942ee00b.zip
rustc: First stab at a typechecker
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/driver/rustc.rs2
-rw-r--r--src/comp/front/ast.rs5
-rw-r--r--src/comp/middle/trans.rs96
-rw-r--r--src/comp/middle/typeck.rs857
-rw-r--r--src/comp/rustc.rc1
5 files changed, 912 insertions, 49 deletions
diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs
index 154b87cb..baf00780 100644
--- a/src/comp/driver/rustc.rs
+++ b/src/comp/driver/rustc.rs
@@ -4,6 +4,7 @@ import front.parser;
import front.token;
import middle.trans;
import middle.resolve;
+import middle.typeck;
import std.option;
import std.option.some;
@@ -15,6 +16,7 @@ impure fn compile_input(session.session sess, str input, str output) {
auto p = parser.new_parser(sess, 0, input);
auto crate = parser.parse_crate(p);
crate = resolve.resolve_crate(sess, crate);
+ crate = typeck.check_crate(sess, crate);
trans.trans_crate(sess, crate, output);
}
diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index c3b38cb6..3a67d2f2 100644
--- a/src/comp/front/ast.rs
+++ b/src/comp/front/ast.rs
@@ -1,6 +1,7 @@
import std.map.hashmap;
import std.option;
+import middle.typeck;
import util.common.span;
import util.common.spanned;
@@ -17,7 +18,7 @@ type def_id = tup(crate_num, def_num);
// Annotations added during successive passes.
tag ann {
ann_none;
- ann_type(@ty);
+ ann_type(@typeck.ty);
}
tag def {
@@ -119,6 +120,8 @@ tag lit_ {
lit_bool(bool);
}
+// NB: If you change this, you'll probably want to change the corresponding
+// type structure in middle/typeck.rs as well.
type ty = spanned[ty_];
tag ty_ {
ty_nil;
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 0c85d50b..d2199fae 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -9,6 +9,7 @@ import std.option.none;
import front.ast;
import driver.session;
+import middle.typeck;
import back.x86;
import back.abi;
@@ -237,13 +238,13 @@ fn T_taskptr() -> TypeRef {
ret T_ptr(T_task());
}
-fn type_of(@trans_ctxt cx, @ast.ty t) -> TypeRef {
- alt (t.node) {
- case (ast.ty_nil) { ret T_nil(); }
- case (ast.ty_bool) { ret T_bool(); }
- case (ast.ty_int) { ret T_int(); }
- case (ast.ty_uint) { ret T_int(); }
- case (ast.ty_machine(?tm)) {
+fn type_of(@trans_ctxt cx, @typeck.ty t) -> TypeRef {
+ alt (t.struct) {
+ case (typeck.ty_nil) { ret T_nil(); }
+ case (typeck.ty_bool) { ret T_bool(); }
+ case (typeck.ty_int) { ret T_int(); }
+ case (typeck.ty_uint) { ret T_int(); }
+ case (typeck.ty_machine(?tm)) {
alt (tm) {
case (common.ty_i8) { ret T_i8(); }
case (common.ty_u8) { ret T_i8(); }
@@ -257,24 +258,25 @@ fn type_of(@trans_ctxt cx, @ast.ty t) -> TypeRef {
case (common.ty_f64) { ret T_f64(); }
}
}
- case (ast.ty_char) { ret T_char(); }
- case (ast.ty_str) { ret T_str(0u); }
- case (ast.ty_box(?t)) {
+ case (typeck.ty_char) { ret T_char(); }
+ case (typeck.ty_str) { ret T_str(0u); }
+ case (typeck.ty_box(?t)) {
ret T_ptr(T_box(type_of(cx, t)));
}
- case (ast.ty_vec(?t)) {
+ case (typeck.ty_vec(?t)) {
ret T_ptr(T_vec(type_of(cx, t), 0u));
}
- case (ast.ty_tup(?elts)) {
+ case (typeck.ty_tup(?elts)) {
let vec[TypeRef] tys = vec();
- for (tup(bool, @ast.ty) elt in elts) {
+ for (tup(bool, @typeck.ty) elt in elts) {
tys += type_of(cx, elt._1);
}
ret T_struct(tys);
}
- case (ast.ty_path(?pth, ?def)) {
+ case (typeck.ty_var(_)) {
// FIXME: implement.
- cx.sess.unimpl("ty_path in trans.type_of");
+ log "ty_var in trans.type_of";
+ ret T_i8();
}
}
fail;
@@ -340,27 +342,23 @@ fn C_tydesc(TypeRef t) -> ValueRef {
C_null(T_opaque()))); // is_stateful
}
-fn decl_fn(ModuleRef llmod, str name,
- uint cc, vec[TypeRef] inputs, TypeRef output) -> ValueRef {
- let TypeRef llty = T_fn(inputs, output);
+fn decl_fn(ModuleRef llmod, str name, uint cc, TypeRef llty) -> ValueRef {
let ValueRef llfn =
llvm.LLVMAddFunction(llmod, _str.buf(name), llty);
llvm.LLVMSetFunctionCallConv(llfn, cc);
ret llfn;
}
-fn decl_cdecl_fn(ModuleRef llmod, str name,
- vec[TypeRef] inputs, TypeRef output) -> ValueRef {
- ret decl_fn(llmod, name, lib.llvm.LLVMCCallConv, inputs, output);
+fn decl_cdecl_fn(ModuleRef llmod, str name, TypeRef llty) -> ValueRef {
+ ret decl_fn(llmod, name, lib.llvm.LLVMCCallConv, llty);
}
-fn decl_fastcall_fn(ModuleRef llmod, str name,
- vec[TypeRef] inputs, TypeRef output) -> ValueRef {
- ret decl_fn(llmod, name, lib.llvm.LLVMFastCallConv, inputs, output);
+fn decl_fastcall_fn(ModuleRef llmod, str name, TypeRef llty) -> ValueRef {
+ ret decl_fn(llmod, name, lib.llvm.LLVMFastCallConv, llty);
}
fn decl_glue(ModuleRef llmod, str s) -> ValueRef {
- ret decl_cdecl_fn(llmod, s, vec(T_taskptr()), T_void());
+ ret decl_cdecl_fn(llmod, s, T_fn(vec(T_taskptr()), T_void()));
}
fn decl_upcall(ModuleRef llmod, uint _n) -> ValueRef {
@@ -375,7 +373,7 @@ fn decl_upcall(ModuleRef llmod, uint _n) -> ValueRef {
T_int()) // callee
+ _vec.init_elt[TypeRef](T_int(), n as uint);
- ret decl_fastcall_fn(llmod, s, args, T_int());
+ ret decl_fastcall_fn(llmod, s, T_fn(args, T_int()));
}
fn get_upcall(@trans_ctxt cx, str name, int n_args) -> ValueRef {
@@ -385,7 +383,7 @@ fn get_upcall(@trans_ctxt cx, str name, int n_args) -> ValueRef {
auto inputs = vec(T_taskptr());
inputs += _vec.init_elt[TypeRef](T_int(), n_args as uint);
auto output = T_int();
- auto f = decl_cdecl_fn(cx.llmod, name, inputs, output);
+ auto f = decl_cdecl_fn(cx.llmod, name, T_fn(inputs, output));
cx.upcalls.insert(name, f);
ret f;
}
@@ -937,6 +935,7 @@ impure fn trans_expr(@block_ctxt cx, &ast.expr e) -> result {
case (ast.expr_call(?f, ?args, _)) {
auto f_res = trans_lval(cx, *f);
check (! f_res._1);
+
auto args_res = trans_exprs(f_res._0.bcx, args);
auto llargs = vec(cx.fcx.lltaskptr);
llargs += args_res._1;
@@ -1141,15 +1140,7 @@ impure fn trans_block(@block_ctxt cx, &ast.block b) -> result {
auto bcx = cx;
for each (@ast.local local in block_locals(b)) {
- auto ty = T_nil();
- alt (local.ty) {
- case (some[@ast.ty](?t)) {
- ty = type_of(cx.fcx.tcx, t);
- }
- case (none[@ast.ty]) {
- cx.fcx.tcx.sess.err("missing type for local " + local.ident);
- }
- }
+ auto ty = node_type(cx.fcx.tcx, local.ann);
auto val = bcx.build.Alloca(ty);
cx.fcx.lllocals.insert(local.id, val);
}
@@ -1236,15 +1227,24 @@ impure fn trans_mod(@trans_ctxt cx, &ast._mod m) {
fn collect_item(&@trans_ctxt cx, @ast.item i) -> @trans_ctxt {
alt (i.node) {
- case (ast.item_fn(?name, ?f, ?fid, _)) {
- cx.items.insert(fid, i);
- let TypeRef out = type_of(cx, f.output);
- auto args = vec(T_taskptr());
- for (ast.arg arg in f.inputs) {
- args += type_of(cx, arg.ty);
+ case (ast.item_fn(?name, ?f, ?fid, ?ann)) {
+ auto fn_inputs;
+ auto fn_output;
+ alt (typeck.ann_to_type(ann).struct) {
+ case (typeck.ty_fn(?ins, ?out)) {
+ fn_inputs = ins;
+ fn_output = out;
+ }
+ case (_) {
+ log "trans: fn item doesn't have function type!";
+ fail;
+ }
}
+
+ cx.items.insert(fid, i);
+ auto llty = node_type(cx, ann);
let str s = cx.names.next("_rust_fn") + "." + name;
- let ValueRef llfn = decl_fastcall_fn(cx.llmod, s, args, out);
+ let ValueRef llfn = decl_fastcall_fn(cx.llmod, s, llty);
cx.fn_ids.insert(fid, llfn);
}
@@ -1338,10 +1338,10 @@ fn trans_main_fn(@trans_ctxt cx, ValueRef llcrate) {
}
auto llmain =
- decl_cdecl_fn(cx.llmod, main_name, T_main_args, T_int());
+ decl_cdecl_fn(cx.llmod, main_name, T_fn(T_main_args, T_int()));
- auto llrust_start =
- decl_cdecl_fn(cx.llmod, "rust_start", T_rust_start_args, T_int());
+ auto llrust_start = decl_cdecl_fn(cx.llmod, "rust_start",
+ T_fn(T_rust_start_args, T_int()));
auto llargc = llvm.LLVMGetParam(llmain, 0u);
auto llargv = llvm.LLVMGetParam(llmain, 1u);
@@ -1367,7 +1367,7 @@ fn trans_main_fn(@trans_ctxt cx, ValueRef llcrate) {
fn declare_intrinsics(ModuleRef llmod) {
let vec[TypeRef] T_trap_args = vec();
- decl_cdecl_fn(llmod, "llvm.trap", T_trap_args, T_void());
+ decl_cdecl_fn(llmod, "llvm.trap", T_fn(T_trap_args, T_void()));
}
fn trans_crate(session.session sess, @ast.crate crate, str output) {
@@ -1396,7 +1396,7 @@ fn trans_crate(session.session sess, @ast.crate crate, str output) {
*/
exit_task_glue =
decl_cdecl_fn(llmod, abi.exit_task_glue_name(),
- vec(T_taskptr()), T_void()),
+ T_fn(vec(T_taskptr()), T_void())),
upcall_glues =
_vec.init_fn[ValueRef](bind decl_upcall(llmod, _),
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
new file mode 100644
index 00000000..8ef75ffa
--- /dev/null
+++ b/src/comp/middle/typeck.rs
@@ -0,0 +1,857 @@
+//
+// comp/middle/typeck.rs
+//
+
+import front.ast;
+import front.ast.ann;
+import middle.fold;
+import driver.session;
+import util.common;
+import util.common.span;
+
+import std._str;
+import std._uint;
+import std._vec;
+import std.map;
+import std.map.hashmap;
+import std.option;
+import std.option.none;
+import std.option.some;
+
+type ty_table = hashmap[ast.def_id, @ty];
+type env = rec(session.session sess,
+ @ty_table item_types,
+ mutable int next_var_id);
+
+type arg = rec(ast.mode mode, @ty ty);
+
+// NB: If you change this, you'll probably want to change the corresponding
+// AST structure in front/ast.rs as well.
+type ty = rec(sty struct, option.t[str] cname);
+tag sty {
+ ty_nil;
+ ty_bool;
+ ty_int;
+ ty_uint;
+ ty_machine(util.common.ty_mach);
+ ty_char;
+ ty_str;
+ ty_box(@ty);
+ ty_vec(@ty);
+ ty_tup(vec[tup(bool /* mutability */, @ty)]);
+ ty_fn(vec[arg], @ty); // TODO: effect
+ ty_var(int);
+}
+
+tag type_err {
+ terr_mismatch;
+ terr_tuple_size(uint, uint);
+ terr_tuple_mutability;
+ terr_arg_count;
+}
+
+tag unify_result {
+ ures_ok(@ty);
+ ures_err(type_err, @ty, @ty);
+}
+
+// Used for ast_ty_to_ty() below.
+type ty_getter = fn(ast.def_id) -> @ty;
+
+// Error-reporting utility functions
+
+fn ast_ty_to_str(&@ast.ty ty) -> str {
+ fn ast_tup_elem_to_str(&tup(bool, @ast.ty) elem) -> str {
+ auto s;
+ if (elem._0) {
+ s = "mutable ";
+ } else {
+ s = "";
+ }
+
+ ret s + ast_ty_to_str(elem._1);
+ }
+
+ fn ast_fn_input_to_str(&rec(ast.mode mode, @ast.ty ty) input) -> str {
+ auto s;
+ if (mode_is_alias(input.mode)) {
+ s = "&";
+ } else {
+ s = "";
+ }
+
+ ret s + ast_ty_to_str(input.ty);
+ }
+
+ auto s;
+ alt (ty.node) {
+ case (ast.ty_nil) { s = "()"; }
+ case (ast.ty_bool) { s = "bool"; }
+ case (ast.ty_int) { s = "int"; }
+ case (ast.ty_uint) { s = "uint"; }
+ case (ast.ty_machine(?tm)) { s = common.ty_mach_to_str(tm); }
+ case (ast.ty_char) { s = "char"; }
+ case (ast.ty_str) { s = "str"; }
+ case (ast.ty_box(?t)) { s = "@" + ast_ty_to_str(t); }
+ case (ast.ty_vec(?t)) { s = "vec[" + ast_ty_to_str(t) + "]"; }
+
+ case (ast.ty_tup(?elems)) {
+ auto f = ast_tup_elem_to_str;
+ s = "tup(";
+ s += _str.connect(_vec.map[tup(bool,@ast.ty),str](f, elems), ",");
+ s += ")";
+ }
+
+ case (ast.ty_fn(?inputs, ?output)) {
+ auto f = ast_fn_input_to_str;
+ s = "fn(";
+ auto is = _vec.map[rec(ast.mode mode, @ast.ty ty),str](f, inputs);
+ s += _str.connect(is, ", ");
+ s += ")";
+
+ if (output.node != ast.ty_nil) {
+ s += " -> " + ast_ty_to_str(output);
+ }
+ }
+
+ case (ast.ty_path(?path, _)) {
+ s = path_to_str(path);
+ }
+
+ case (_) {
+ fail; // FIXME: typestate bug
+ }
+ }
+
+ ret s;
+}
+
+fn name_to_str(&ast.name nm) -> str {
+ auto result = nm.node.ident;
+ if (_vec.len[@ast.ty](nm.node.types) > 0u) {
+ auto f = ast_ty_to_str;
+ result += "[";
+ result += _str.connect(_vec.map[@ast.ty,str](f, nm.node.types), ",");
+ result += "]";
+ }
+ ret result;
+}
+
+fn path_to_str(&ast.path path) -> str {
+ auto f = name_to_str;
+ ret _str.connect(_vec.map[ast.name,str](f, path), ".");
+}
+
+fn ty_to_str(@ty typ) -> str {
+ fn tup_elem_to_str(&tup(bool, @ty) elem) -> str {
+ auto s;
+ if (elem._0) {
+ s = "mutable ";
+ } else {
+ s = "";
+ }
+
+ ret s + ty_to_str(elem._1);
+ }
+
+ fn fn_input_to_str(&rec(ast.mode mode, @ty ty) input) -> str {
+ auto s;
+ if (mode_is_alias(input.mode)) {
+ s = "&";
+ } else {
+ s = "";
+ }
+
+ ret s + ty_to_str(input.ty);
+ }
+
+ auto s;
+ alt (typ.struct) {
+ case (ty_nil) { s = "()"; }
+ case (ty_bool) { s = "bool"; }
+ case (ty_int) { s = "int"; }
+ case (ty_uint) { s = "uint"; }
+ case (ty_machine(?tm)) { s = common.ty_mach_to_str(tm); }
+ case (ty_char) { s = "char"; }
+ case (ty_str) { s = "str"; }
+ case (ty_box(?t)) { s = "@" + ty_to_str(t); }
+ case (ty_vec(?t)) { s = "vec[" + ty_to_str(t) + "]"; }
+
+ case (ty_tup(?elems)) {
+ auto f = tup_elem_to_str;
+ auto strs = _vec.map[tup(bool,@ty),str](f, elems);
+ s = "tup(" + _str.connect(strs, ",") + ")";
+ }
+
+ case (ty_fn(?inputs, ?output)) {
+ auto f = fn_input_to_str;
+ s = "fn(" + _str.connect(_vec.map[arg,str](f, inputs), ", ") + ")";
+ if (output.struct != ty_nil) {
+ s += " -> " + ty_to_str(output);
+ }
+ }
+
+ case (ty_var(?v)) {
+ // FIXME: wrap around in the case of many variables
+ auto ch = ('T' as u8) + (v as u8);
+ s = _str.from_bytes(vec(ch));
+ }
+ }
+
+ ret s;
+}
+
+// Parses the programmer's textual representation of a type into our internal
+// notion of a type. `getter` is a function that returns the type
+// corresponding to a definition ID.
+fn ast_ty_to_ty(ty_getter getter, &@ast.ty ast_ty) -> @ty {
+ fn ast_arg_to_arg(ty_getter getter, &rec(ast.mode mode, @ast.ty ty) arg)
+ -> rec(ast.mode mode, @ty ty) {
+ ret rec(mode=arg.mode, ty=ast_ty_to_ty(getter, arg.ty));
+ }
+
+ auto sty;
+ auto cname = none[str];
+ alt (ast_ty.node) {
+ case (ast.ty_nil) { sty = ty_nil; }
+ case (ast.ty_bool) { sty = ty_bool; }
+ case (ast.ty_int) { sty = ty_int; }
+ case (ast.ty_uint) { sty = ty_uint; }
+ case (ast.ty_machine(?tm)) { sty = ty_machine(tm); }
+ case (ast.ty_char) { sty = ty_char; }
+ case (ast.ty_str) { sty = ty_str; }
+ case (ast.ty_box(?t)) { sty = ty_box(ast_ty_to_ty(getter, t)); }
+ case (ast.ty_vec(?t)) { sty = ty_vec(ast_ty_to_ty(getter, t)); }
+
+ case (ast.ty_fn(?inputs, ?output)) {
+ auto f = bind ast_arg_to_arg(getter, _);
+ auto i = _vec.map[rec(ast.mode mode, @ast.ty ty),arg](f, inputs);
+ sty = ty_fn(i, ast_ty_to_ty(getter, output));
+ }
+
+ case (ast.ty_path(?path, ?def)) {
+ auto def_id;
+ alt (option.get[ast.def](def)) {
+ case (ast.def_ty(?id)) { def_id = id; }
+ case (_) { fail; }
+ }
+
+ // TODO: maybe record cname chains so we can do "foo = int" like
+ // OCaml?
+ sty = getter(def_id).struct;
+ cname = some(path_to_str(path));
+ }
+ }
+
+ ret @rec(struct=sty, cname=cname);
+}
+
+// A convenience function to use an environment to resolve names for
+// ast_ty_to_ty.
+fn ast_ty_to_ty_env(@env e, &@ast.ty ast_ty) -> @ty {
+ fn getter(@env e, ast.def_id id) -> @ty {
+ ret e.item_types.get(id);
+ }
+ auto f = bind getter(e, _);
+ ret ast_ty_to_ty(f, ast_ty);
+}
+
+fn type_err_to_str(&type_err err) -> str {
+ alt (err) {
+ case (terr_mismatch) {
+ ret "types differ";
+ }
+ case (terr_tuple_size(?e_sz, ?a_sz)) {
+ ret "expected a tuple with " + _uint.to_str(e_sz, 10u) +
+ " elements but found one with " + _uint.to_str(a_sz, 10u) +
+ " elements";
+ }
+ case (terr_tuple_mutability) {
+ ret "tuple elements differ in mutability";
+ }
+ case (terr_arg_count) {
+ ret "incorrect number of function parameters";
+ }
+ }
+}
+
+// Item collection - a pair of bootstrap passes:
+//
+// 1. Collect the IDs of all type items (typedefs) and store them in a table.
+//
+// 2. Translate the AST fragments that describe types to determine a type for
+// each item. When we encounter a named type, we consult the table built in
+// pass 1 to find its item, and recursively translate it.
+//
+// We then annotate the AST with the resulting types and return the annotated
+// AST, along with a table mapping item IDs to their types.
+
+fn collect_item_types(@ast.crate crate) -> tup(@ast.crate, @ty_table) {
+ fn trans_ty_item_id_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
+ @ty_table item_to_ty,
+ ast.def_id id) -> @ty {
+ auto item = id_to_ty_item.get(id);
+ ret trans_ty_item_to_ty(id_to_ty_item, item_to_ty, item);
+ }
+
+ fn trans_fn_arg_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
+ @ty_table item_to_ty,
+ &ast.arg a) -> arg {
+ auto f = bind trans_ty_item_id_to_ty(id_to_ty_item, item_to_ty, _);
+ ret rec(mode=a.mode, ty=ast_ty_to_ty(f, a.ty));
+ }
+
+ fn trans_ty_item_to_ty(@hashmap[ast.def_id,@ast.item] id_to_ty_item,
+ @ty_table item_to_ty,
+ @ast.item it) -> @ty {
+ alt (it.node) {
+ case (ast.item_fn(?ident, ?fn_info, ?def_id, _)) {
+ auto f = bind trans_fn_arg_to_ty(id_to_ty_item, item_to_ty,
+ _);
+ auto input_tys = _vec.map[ast.arg,arg](f, fn_info.inputs);
+
+ auto g = bind trans_ty_item_id_to_ty(id_to_ty_item,
+ item_to_ty, _);
+ auto output_ty = ast_ty_to_ty(g, fn_info.output);
+
+ auto t_fn = plain_ty(ty_fn(input_tys, output_ty));
+ item_to_ty.insert(def_id, t_fn);
+ ret t_fn;
+ }
+
+ case (ast.item_ty(?ident, ?referent_ty, ?def_id, _)) {
+ if (item_to_ty.contains_key(def_id)) {
+ // Avoid repeating work.
+ ret item_to_ty.get(def_id);
+ }
+
+ // Tell ast_ty_to_ty() that we want to perform a recursive
+ // call to resolve any named types.
+ auto f = bind trans_ty_item_id_to_ty(id_to_ty_item,
+ item_to_ty, _);
+ auto ty = ast_ty_to_ty(f, referent_ty);
+ item_to_ty.insert(def_id, ty);
+ ret ty;
+ }
+
+ case (ast.item_mod(_, _, _)) { fail; }
+ }
+ }
+
+ // First pass: collect all type item IDs.
+ auto module = crate.node.module;
+ auto id_to_ty_item = @common.new_def_hash[@ast.item]();
+ for (@ast.item item in module.items) {
+ alt (item.node) {
+ case (ast.item_ty(_, _, ?def_id, _)) {
+ id_to_ty_item.insert(def_id, item);
+ }
+ case (_) { /* empty */ }
+ }
+ }
+
+ // Second pass: translate the types of all items.
+ auto item_to_ty = @common.new_def_hash[@ty]();
+ let vec[@ast.item] items_t = vec();
+ for (@ast.item it in module.items) {
+ let ast.item_ result;
+ alt (it.node) {
+ case (ast.item_fn(?ident, ?fn_info, ?def_id, _)) {
+ auto t = trans_ty_item_to_ty(id_to_ty_item, item_to_ty, it);
+ result = ast.item_fn(ident, fn_info, def_id, ast.ann_type(t));
+ }
+ case (ast.item_ty(?ident, ?referent_ty, ?def_id, _)) {
+ auto t = trans_ty_item_to_ty(id_to_ty_item, item_to_ty, it);
+ auto ann = ast.ann_type(t);
+ result = ast.item_ty(ident, referent_ty, def_id, ann);
+ }
+ case (ast.item_mod(_, _, _)) {
+ result = it.node;
+ }
+ }
+ items_t += vec(@fold.respan[ast.item_](it.span, result));
+ }
+
+ auto module_t = rec(items=items_t, index=module.index);
+ ret tup(@fold.respan[ast.crate_](crate.span, rec(module=module_t)),
+ item_to_ty);
+}
+
+// Type utilities
+
+// FIXME: remove me when == works on these tags.
+fn mode_is_alias(ast.mode m) -> bool {
+ alt (m) {
+ case (ast.val) { ret false; }
+ case (ast.alias) { ret true; }
+ }
+}
+
+fn plain_ty(&sty st) -> @ty {
+ ret @rec(struct=st, cname=none[str]);
+}
+
+fn ann_to_type(&ast.ann ann) -> @ty {
+ alt (ann) {
+ case (ast.ann_none) {
+ // shouldn't happen, but can until the typechecker is complete
+ ret plain_ty(ty_var(0)); // FIXME: broken, broken, broken
+ }
+ case (ast.ann_type(?ty)) {
+ ret ty;
+ }
+ }
+}
+
+fn type_of(@ast.expr expr) -> @ty {
+ alt (expr.node) {
+ case (ast.expr_vec(_, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_tup(_, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_rec(_, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_call(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_binary(_, _, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_unary(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_lit(_, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_cast(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_if(_, _, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_while(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_do_while(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_block(_, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_assign(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_field(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_index(_, _, ?ann)) { ret ann_to_type(ann); }
+ case (ast.expr_name(_, _, ?ann)) { ret ann_to_type(ann); }
+ }
+
+ fail;
+}
+
+// Type unification
+
+fn unify(@ty expected, @ty actual) -> unify_result {
+ // Wraps the given type in an appropriate cname.
+ //
+ // TODO: This doesn't do anything yet. We should carry the cname up from
+ // the expected and/or actual types when unification results in a type
+ // identical to one or both of the two. The precise algorithm for this is
+ // something we'll probably need to develop over time.
+
+ // Simple structural type comparison.
+ fn struct_cmp(@ty expected, @ty actual) -> unify_result {
+ if (expected == actual) {
+ ret ures_ok(expected);
+ }
+
+ ret ures_err(terr_mismatch, expected, actual);
+ }
+
+ fn unify_step(@ty expected, @ty actual, &hashmap[int,@ty] bindings)
+ -> unify_result {
+ // TODO: rewrite this using tuple pattern matching when available, to
+ // avoid all this rightward drift and spikiness.
+ alt (expected.struct) {
+ case (ty_nil) { ret struct_cmp(expected, actual); }
+ case (ty_bool) { ret struct_cmp(expected, actual); }
+ case (ty_int) { ret struct_cmp(expected, actual); }
+ case (ty_uint) { ret struct_cmp(expected, actual); }
+ case (ty_machine(_)) { ret struct_cmp(expected, actual); }
+ case (ty_char) { ret struct_cmp(expected, actual); }
+ case (ty_str) { ret struct_cmp(expected, actual); }
+
+ case (ty_box(?expected_sub)) {
+ alt (actual.struct) {
+ case (ty_box(?actual_sub)) {
+ auto result = unify_step(expected_sub, actual_sub,
+ bindings);
+ alt (result) {
+ case (ures_ok(?result_sub)) {
+ ret ures_ok(plain_ty(ty_box(result_sub)));
+ }
+ case (_) {
+ ret result;
+ }
+ }
+ }
+
+ // TODO: ty_var
+
+ case (_) {
+ ret ures_err(terr_mismatch, expected, actual);
+ }
+ }
+ }
+
+ case (ty_vec(?expected_sub)) {
+ alt (actual.struct) {
+ case (ty_vec(?actual_sub)) {
+ auto result = unify_step(expected_sub, actual_sub,
+ bindings);
+ alt (result) {
+ case (ures_ok(?result_sub)) {
+ ret ures_ok(plain_ty(ty_vec(result_sub)));
+ }
+ case (_) {
+ ret result;
+ }
+ }
+ }
+
+ // TODO: ty_var
+
+ case (_) {
+ ret ures_err(terr_mismatch, expected, actual);
+ }
+ }
+ }
+
+ case (ty_tup(?expected_elems)) {
+ alt (actual.struct) {
+ case (ty_tup(?actual_elems)) {
+ auto expected_len =
+ _vec.len[tup(bool,@ty)](expected_elems);
+ auto actual_len =
+ _vec.len[tup(bool,@ty)](actual_elems);
+ if (expected_len != actual_len) {
+ auto err = terr_tuple_size(expected_len,
+ actual_len);
+ ret ures_err(err, expected, actual);
+ }
+
+ // TODO: implement an iterator that can iterate over
+ // two arrays simultaneously.
+ let vec[tup(bool, @ty)] result_elems = vec();
+ auto i = 0u;
+ while (i < expected_len) {
+ auto expected_elem = expected_elems.(i);
+ auto actual_elem = actual_elems.(i);
+ if (expected_elem._0 != actual_elem._0) {
+ auto err = terr_tuple_mutability;
+ ret ures_err(err, expected, actual);
+ }
+
+ auto result = unify_step(expected_elem._1,
+ actual_elem._1,
+ bindings);
+ alt (result) {
+ case (ures_ok(?rty)) {
+ result_elems += vec(tup(expected_elem._0,
+ rty));
+ }
+ case (_) {
+ ret result;
+ }
+ }
+
+ i += 1u;
+ }
+
+ ret ures_ok(plain_ty(ty_tup(result_elems)));
+ }
+
+ // 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)) {
+ auto expected_len = _vec.len[arg](expected_inputs);
+ auto actual_len = _vec.len[arg](actual_inputs);
+ if (expected_len != actual_len) {
+ ret ures_err(terr_arg_count, expected, actual);
+ }
+
+ // TODO: as above, we should have an iter2 iterator.
+ let vec[arg] result_ins = vec();
+ auto i = 0u;
+ while (i < expected_len) {
+ auto expected_input = expected_inputs.(i);
+ auto actual_input = actual_inputs.(i);
+
+ // This should be safe, I think?
+ auto result_mode;
+ if (mode_is_alias(expected_input.mode) ||
+ mode_is_alias(actual_input.mode)) {
+ result_mode = ast.alias;
+ } else {
+ result_mode = ast.val;
+ }
+
+ auto result = unify_step(expected_input.ty,
+ actual_input.ty,
+ bindings);
+
+ alt (result) {
+ case (ures_ok(?rty)) {
+ result_ins += vec(rec(mode=result_mode,
+ ty=rty));
+ }
+
+ case (_) {
+ ret result;
+ }
+ }
+
+ i += 1u;
+ }
+
+ // Check the output.
+ auto result_out;
+ auto result = unify_step(expected_output,
+ actual_output, bindings);
+ alt (result) {
+ case (ures_ok(?rty)) {
+ result_out = rty;
+ }
+
+ case (_) {
+ ret result;
+ }
+ }
+
+ ret ures_ok(plain_ty(ty_fn(result_ins, result_out)));
+ }
+
+ case (_) {
+ ret ures_err(terr_mismatch, expected, actual);
+ }
+ }
+ }
+
+ case (ty_var(?expected_id)) {
+ if (bindings.contains_key(expected_id)) {
+ auto binding = bindings.get(expected_id);
+ ret unify_step(binding, actual, bindings);
+ }
+
+ bindings.insert(expected_id, actual);
+ ret ures_ok(actual);
+ }
+ }
+
+ // TODO: remove me once match-exhaustiveness checking works
+ fail;
+ }
+
+ fn hash_int(&int x) -> uint { ret x as uint; }
+ fn eq_int(&int a, &int b) -> bool { ret a == b; }
+ auto hasher = hash_int;
+ auto eqer = eq_int;
+ auto bindings = map.mk_hashmap[int,@ty](hasher, eqer);
+
+ ret unify_step(expected, actual, bindings);
+}
+
+// Requires that the two types unify, and prints an error message if they
+// don't. Returns the unified type.
+fn demand(&@env e, &span sp, @ty expected, @ty actual) -> @ty {
+ alt (unify(expected, actual)) {
+ case (ures_ok(?ty)) {
+ ret ty;
+ }
+
+ case (ures_err(?err, ?expected, ?actual)) {
+ e.sess.err("mismatched types: expected " + ty_to_str(expected) +
+ " but found " + ty_to_str(actual) + " (" +
+ type_err_to_str(err) + ")");
+
+ // TODO: In the future, try returning "expected", reporting the
+ // error, and continue.
+ fail;
+ }
+ }
+}
+
+// Unifies the supplied type with the type of the local `id`, and stores the
+// unified type in the local table. Emits an error if the type is incompatible
+// with the previously-stored type for this local.
+fn demand_local(&@env e, &span sp, &@ty_table locals, ast.def_id local_id,
+ @ty t) {
+ auto prev_ty = locals.get(local_id);
+ auto unified_ty = demand(e, sp, prev_ty, t);
+ locals.insert(local_id, unified_ty);
+}
+
+// Returns true if the two types unify and false if they don't.
+fn are_compatible(@ty expected, @ty actual) -> bool {
+ alt (unify(expected, actual)) {
+ case (ures_ok(_)) { ret true; }
+ case (ures_err(_, _, _)) { ret false; }
+ }
+}
+
+// Writeback: the phase that writes inferred types back into the AST.
+
+fn writeback_local(&@ty_table locals, &span sp, @ast.local local)
+ -> @ast.decl {
+ auto local_wb = @rec(ann=ast.ann_type(locals.get(local.id)) with *local);
+ ret @fold.respan[ast.decl_](sp, ast.decl_local(local_wb));
+}
+
+fn writeback(&@env e, &@ty_table locals, &ast.block block) -> ast.block {
+ auto fld = fold.new_identity_fold[@ty_table]();
+ auto f = writeback_local; // FIXME: trans_const_lval bug
+ fld = @rec(fold_decl_local = f with *fld);
+ ret fold.fold_block[@ty_table](locals, fld, block);
+}
+
+// AST fragment checking
+
+fn check_lit(&@env e, @ast.lit lit) -> @ty {
+ auto sty;
+ alt (lit.node) {
+ case (ast.lit_str(_)) { sty = ty_str; }
+ case (ast.lit_char(_)) { sty = ty_char; }
+ case (ast.lit_int(_)) { sty = ty_int; }
+ case (ast.lit_uint(_)) { sty = ty_uint; }
+ case (ast.lit_nil) { sty = ty_nil; }
+ case (ast.lit_bool(_)) { sty = ty_bool; }
+ }
+
+ ret plain_ty(sty);
+}
+
+fn check_expr(&@env e, &@ty_table locals, @ast.expr expr) -> @ast.expr {
+ alt (expr.node) {
+ case (ast.expr_lit(?lit, _)) {
+ auto ty = check_lit(e, lit);
+ ret @fold.respan[ast.expr_](expr.span,
+ ast.expr_lit(lit, ast.ann_type(ty)));
+ }
+
+ case (_) {
+ // TODO
+ ret expr;
+ }
+ }
+}
+
+fn check_stmt(@env e, @ty_table locals, @ty ret_ty, &@ast.stmt stmt)
+ -> @ast.stmt {
+ alt (stmt.node) {
+ case (ast.stmt_decl(?decl)) {
+ alt (decl.node) {
+ case (ast.decl_local(?local)) {
+ alt (local.init) {
+ case (none[@ast.expr]) {
+ // empty
+ }
+
+ case (some[@ast.expr](?expr)) {
+ auto expr_t = check_expr(e, locals, expr);
+ locals.insert(local.id, type_of(expr_t));
+
+ alt (local.ty) {
+ case (none[@ast.ty]) {
+ // Nothing to do. We'll figure out the
+ // type later.
+ }
+
+ case (some[@ast.ty](?ast_ty)) {
+ auto ty = ast_ty_to_ty_env(e, ast_ty);
+ demand_local(e, decl.span, locals,
+ local.id, ty);
+ }
+ }
+ }
+ }
+ }
+
+ case (ast.decl_item(_)) {
+ // Ignore for now. We'll return later.
+ }
+ }
+
+ ret stmt;
+ }
+
+ case (ast.stmt_ret(?expr_opt)) {
+ alt (expr_opt) {
+ case (none[@ast.expr]) {
+ if (!are_compatible(ret_ty, plain_ty(ty_nil))) {
+ e.sess.err("ret; in function returning non-void");
+ }
+
+ ret stmt;
+ }
+
+ case (some[@ast.expr](?expr)) {
+ auto expr_t = check_expr(e, locals, expr);
+ demand(e, expr.span, ret_ty, type_of(expr_t));
+ ret @fold.respan[ast.stmt_](stmt.span,
+ ast.stmt_ret(some(expr_t)));
+ }
+ }
+ }
+
+ case (ast.stmt_log(?expr)) {
+ auto expr_t = check_expr(e, locals, expr);
+ ret @fold.respan[ast.stmt_](stmt.span, ast.stmt_log(expr_t));
+ }
+
+ case (ast.stmt_check_expr(?expr)) {
+ auto expr_t = check_expr(e, locals, expr);
+ demand(e, expr.span, plain_ty(ty_bool), type_of(expr_t));
+ ret @fold.respan[ast.stmt_](stmt.span,
+ ast.stmt_check_expr(expr_t));
+ }
+
+ case (ast.stmt_expr(?expr)) {
+ auto expr_t = check_expr(e, locals, expr);
+ if (!are_compatible(type_of(expr_t), plain_ty(ty_nil))) {
+ // TODO: real warning function
+ log "warning: expression used as statement should have " +
+ "void type";
+ }
+
+ ret @fold.respan[ast.stmt_](stmt.span, ast.stmt_expr(expr_t));
+ }
+ }
+
+ fail;
+}
+
+fn check_block(&@env e, &@ty_table locals, @ty ret_ty, &ast.block block)
+ -> ast.block {
+ auto f = bind check_stmt(e, locals, ret_ty, _);
+ auto stmts_t = _vec.map[@ast.stmt,@ast.stmt](f, block.node.stmts);
+ ret fold.respan[ast.block_](block.span,
+ rec(stmts=stmts_t, index=block.node.index));
+}
+
+fn check_fn(&@env e, &span sp, ast.ident ident, &ast._fn f, ast.def_id id,
+ ast.ann ann) -> @ast.item {
+ auto local_ty_table = @common.new_def_hash[@ty]();
+
+ // Store the type of each argument in the table.
+ let vec[arg] inputs = vec();
+ for (ast.arg arg in f.inputs) {
+ auto input_ty = ast_ty_to_ty_env(e, arg.ty);
+ inputs += vec(rec(mode=arg.mode, ty=input_ty));
+ local_ty_table.insert(arg.id, input_ty);
+ }
+
+ auto output_ty = ast_ty_to_ty_env(e, f.output);
+ auto fn_sty = ty_fn(inputs, output_ty);
+ auto fn_ann = ast.ann_type(plain_ty(fn_sty));
+
+ auto block_t = check_block(e, local_ty_table, output_ty, f.body);
+ auto block_wb = writeback(e, local_ty_table, block_t);
+ auto fn_t = rec(inputs=f.inputs, output=f.output, body=block_wb);
+ ret @fold.respan[ast.item_](sp, ast.item_fn(ident, fn_t, id, fn_ann));
+}
+
+fn check_crate(session.session sess, @ast.crate crate) -> @ast.crate {
+ auto result = collect_item_types(crate);
+ auto e = @rec(sess=sess, item_types=result._1, mutable next_var_id=0);
+
+ auto fld = fold.new_identity_fold[@env]();
+ auto f = check_fn; // FIXME: trans_const_lval bug
+ fld = @rec(fold_item_fn = f with *fld);
+ ret fold.fold_crate[@env](e, fld, result._0);
+}
+
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 7b314ad7..ef9be053 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -14,6 +14,7 @@ mod middle {
mod fold;
mod resolve;
mod trans;
+ mod typeck;
}
mod back {