break apart typeck a little (more to come)

This commit is contained in:
Niko Matsakis 2012-05-15 08:29:22 -07:00
parent adb61e3e99
commit 26dc48d67f
6 changed files with 1531 additions and 1500 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,711 @@
#[doc = "
Conversion from AST representation of types to the ty.rs representation.
The main routine here is `ast_ty_to_ty()`: each use is parameterized
by an instance of `ast_conv` and a `region_scope`.
The `ast_conv` interface is the conversion context. It has two
implementations, one for the crate context and one for the function
context. The main purpose is to provide the `get_item_ty()` hook
which looks up the type of an item by its def-id. This can be done in
two ways: in the initial phase, when a crate context is provided, this
will potentially trigger a call to `ty_of_item()`. Later, when a
function context is used, this will simply be a lookup.
The `region_scope` interface controls how region references are
handled. It has two methods which are used to resolve anonymous
region references (e.g., `&T`) and named region references (e.g.,
`&a.T`). There are numerous region scopes that can be used, but most
commonly you want either `empty_rscope`, which permits only the static
region, or `type_rscope`, which permits the self region if the type in
question is parameterized by a region.
"];
iface ast_conv {
fn tcx() -> ty::ctxt;
fn ccx() -> @crate_ctxt;
fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty;
// what type should we use when a type is omitted?
fn ty_infer(span: span) -> ty::t;
}
impl of ast_conv for @crate_ctxt {
fn tcx() -> ty::ctxt { self.tcx }
fn ccx() -> @crate_ctxt { self }
fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
if id.crate != ast::local_crate {
csearch::get_type(self.tcx, id)
} else {
alt self.tcx.items.find(id.node) {
some(ast_map::node_item(item, _)) {
ty_of_item(self, item)
}
some(ast_map::node_native_item(native_item, _, _)) {
ty_of_native_item(self, native_item)
}
x {
self.tcx.sess.bug(#fmt["unexpected sort of item \
in get_item_ty(): %?", x]);
}
}
}
}
fn ty_infer(span: span) -> ty::t {
self.tcx.sess.span_bug(span,
"found `ty_infer` in unexpected place");
}
}
impl of ast_conv for @fn_ctxt {
fn tcx() -> ty::ctxt { self.ccx.tcx }
fn ccx() -> @crate_ctxt { self.ccx }
fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty {
ty::lookup_item_type(self.tcx(), id)
}
fn ty_infer(_span: span) -> ty::t {
self.next_ty_var()
}
}
iface region_scope {
fn anon_region() -> result<ty::region, str>;
fn named_region(id: str) -> result<ty::region, str>;
}
enum empty_rscope { empty_rscope }
impl of region_scope for empty_rscope {
fn anon_region() -> result<ty::region, str> {
result::err("region types are not allowed here")
}
fn named_region(id: str) -> result<ty::region, str> {
if id == "static" { result::ok(ty::re_static) }
else { result::err("only the static region is allowed here") }
}
}
enum type_rscope = ast::region_param;
impl of region_scope for type_rscope {
fn anon_region() -> result<ty::region, str> {
alt *self {
ast::rp_self { result::ok(ty::re_bound(ty::br_self)) }
ast::rp_none {
result::err("to use region types here, the containing type \
must be declared with a region bound")
}
}
}
fn named_region(id: str) -> result<ty::region, str> {
empty_rscope.named_region(id).chain_err { |_e|
if id == "self" { self.anon_region() }
else {
result::err("named regions other than `self` are not \
allowed as part of a type declaration")
}
}
}
}
impl of region_scope for @fn_ctxt {
fn anon_region() -> result<ty::region, str> {
result::ok(self.next_region_var())
}
fn named_region(id: str) -> result<ty::region, str> {
empty_rscope.named_region(id).chain_err { |_e|
alt self.in_scope_regions.find(ty::br_named(id)) {
some(r) { result::ok(r) }
none if id == "blk" { self.block_region() }
none {
result::err(#fmt["named region `%s` not in scope here", id])
}
}
}
}
}
enum anon_rscope = {anon: ty::region, base: region_scope};
fn in_anon_rscope<RS: region_scope copy>(self: RS, r: ty::region)
-> @anon_rscope {
@anon_rscope({anon: r, base: self as region_scope})
}
impl of region_scope for @anon_rscope {
fn anon_region() -> result<ty::region, str> {
result::ok(self.anon)
}
fn named_region(id: str) -> result<ty::region, str> {
self.base.named_region(id)
}
}
enum binding_rscope = {base: region_scope};
fn in_binding_rscope<RS: region_scope copy>(self: RS) -> @binding_rscope {
let base = self as region_scope;
@binding_rscope({base: base})
}
impl of region_scope for @binding_rscope {
fn anon_region() -> result<ty::region, str> {
result::ok(ty::re_bound(ty::br_anon))
}
fn named_region(id: str) -> result<ty::region, str> {
self.base.named_region(id).chain_err {|_e|
result::ok(ty::re_bound(ty::br_named(id)))
}
}
}
fn ast_region_to_region<AC: ast_conv, RS: region_scope>(
self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region {
let res = alt a_r.node {
ast::re_anon { rscope.anon_region() }
ast::re_named(id) { rscope.named_region(id) }
};
get_region_reporting_err(self.tcx(), span, res)
}
fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, did: ast::def_id,
path: @ast::path) -> ty_param_substs_and_ty {
let tcx = self.tcx();
let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} =
self.get_item_ty(did);
// If the type is parameterized by the self region, then replace self
// region with the current anon region binding (in other words,
// whatever & would get replaced with).
let self_r = alt (decl_rp, path.rp) {
(ast::rp_none, none) {
none
}
(ast::rp_none, some(_)) {
tcx.sess.span_err(
path.span,
#fmt["No region bound is permitted on %s, \
which is not declared as containing region pointers",
ty::item_path_str(tcx, did)]);
none
}
(ast::rp_self, none) {
let res = rscope.anon_region();
let r = get_region_reporting_err(self.tcx(), path.span, res);
some(r)
}
(ast::rp_self, some(r)) {
some(ast_region_to_region(self, rscope, path.span, r))
}
};
// Convert the type parameters supplied by the user.
if !vec::same_length(*decl_bounds, path.types) {
self.tcx().sess.span_fatal(
path.span,
#fmt["wrong number of type arguments, expected %u but found %u",
(*decl_bounds).len(), path.types.len()]);
}
let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) };
let substs = {self_r:self_r, self_ty:none, tps:tps};
{substs: substs, ty: ty::subst(tcx, substs, decl_ty)}
}
fn ast_path_to_ty<AC: ast_conv, RS: region_scope copy>(
self: AC,
rscope: RS,
did: ast::def_id,
path: @ast::path,
path_id: ast::node_id) -> ty_param_substs_and_ty {
// Lookup the polytype of the item and then substitute the provided types
// for any type/region parameters.
let tcx = self.tcx();
let {substs: substs, ty: ty} =
ast_path_to_substs_and_ty(self, rscope, did, path);
write_ty_to_tcx(tcx, path_id, ty);
write_substs_to_tcx(tcx, path_id, substs.tps);
ret {substs: substs, ty: ty};
}
/*
Instantiates the path for the given iface reference, assuming that
it's bound to a valid iface type. Returns the def_id for the defining
iface. Fails if the type is a type other than an iface type.
*/
fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref,
rp: ast::region_param)
-> (ast::def_id, ty_param_substs_and_ty) {
let sp = t.path.span, err = "can only implement interface types",
sess = ccx.tcx.sess;
let rscope = type_rscope(rp);
alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) {
ast::def_ty(t_id) {
let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id);
alt ty::get(tpt.ty).struct {
ty::ty_iface(*) {
(t_id, tpt)
}
_ { sess.span_fatal(sp, err); }
}
}
_ {
sess.span_fatal(sp, err);
}
}
}
const NO_REGIONS: uint = 1u;
const NO_TPS: uint = 2u;
// 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<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t {
fn ast_mt_to_mt<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, mt: ast::mt) -> ty::mt {
ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl};
}
fn mk_vstore<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t {
let tcx = self.tcx();
let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty);
alt ty::get(seq_ty).struct {
ty::ty_vec(mt) {
ret ty::mk_evec(tcx, mt, vst);
}
ty::ty_str {
ret ty::mk_estr(tcx, vst);
}
_ {
tcx.sess.span_err(
a_seq_ty.span,
#fmt["Bound not allowed on a %s.",
ty::ty_sort_str(tcx, seq_ty)]);
ret seq_ty;
}
}
}
fn check_path_args(tcx: ty::ctxt,
path: @ast::path,
flags: uint) {
if (flags & NO_TPS) != 0u {
if path.types.len() > 0u {
tcx.sess.span_err(
path.span,
"Type parameters are not allowed on this type.");
}
}
if (flags & NO_REGIONS) != 0u {
if path.rp.is_some() {
tcx.sess.span_err(
path.span,
"Region parameters are not allowed on this type.");
}
}
}
let tcx = self.tcx();
alt tcx.ast_ty_to_ty_cache.find(ast_ty) {
some(ty::atttce_resolved(ty)) { ret ty; }
some(ty::atttce_unresolved) {
tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \
insert a enum in the cycle, \
if this is desired)");
}
none { /* go on */ }
}
tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_unresolved);
let typ = alt ast_ty.node {
ast::ty_nil { ty::mk_nil(tcx) }
ast::ty_bot { ty::mk_bot(tcx) }
ast::ty_box(mt) {
ty::mk_box(tcx, ast_mt_to_mt(self, rscope, mt))
}
ast::ty_uniq(mt) {
ty::mk_uniq(tcx, ast_mt_to_mt(self, rscope, mt))
}
ast::ty_vec(mt) {
ty::mk_vec(tcx, ast_mt_to_mt(self, rscope, mt))
}
ast::ty_ptr(mt) {
ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt))
}
ast::ty_rptr(region, mt) {
let r = ast_region_to_region(self, rscope, ast_ty.span, region);
let mt = ast_mt_to_mt(self, in_anon_rscope(rscope, r), mt);
ty::mk_rptr(tcx, r, mt)
}
ast::ty_tup(fields) {
let flds = vec::map(fields) { |t| ast_ty_to_ty(self, rscope, t) };
ty::mk_tup(tcx, flds)
}
ast::ty_rec(fields) {
let flds = fields.map {|f|
let tm = ast_mt_to_mt(self, rscope, f.node.mt);
{ident: f.node.ident, mt: tm}
};
ty::mk_rec(tcx, flds)
}
ast::ty_fn(proto, decl) {
ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none))
}
ast::ty_path(path, id) {
let a_def = alt tcx.def_map.find(id) {
none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s",
path_to_str(path))); }
some(d) { d }};
alt a_def {
ast::def_ty(did) | ast::def_class(did) {
ast_path_to_ty(self, rscope, did, path, id).ty
}
ast::def_prim_ty(nty) {
alt nty {
ast::ty_bool {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
ty::mk_bool(tcx)
}
ast::ty_int(it) {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
ty::mk_mach_int(tcx, it)
}
ast::ty_uint(uit) {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
ty::mk_mach_uint(tcx, uit)
}
ast::ty_float(ft) {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
ty::mk_mach_float(tcx, ft)
}
ast::ty_str {
check_path_args(tcx, path, NO_TPS);
// This is a bit of a hack, but basically str/& needs to be
// converted into a vstore:
alt path.rp {
none {
ty::mk_str(tcx)
}
some(ast_r) {
let r = ast_region_to_region(self, rscope,
ast_ty.span, ast_r);
ty::mk_estr(tcx, ty::vstore_slice(r))
}
}
}
}
}
ast::def_ty_param(id, n) {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
ty::mk_param(tcx, n, id)
}
ast::def_self(_) {
// n.b.: resolve guarantees that the self type only appears in an
// iface, which we rely upon in various places when creating
// substs
ty::mk_self(tcx)
}
_ {
tcx.sess.span_fatal(ast_ty.span,
"found type name used as a variable");
}
}
}
ast::ty_vstore(a_t, ast::vstore_slice(a_r)) {
let r = ast_region_to_region(self, rscope, ast_ty.span, a_r);
mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r))
}
ast::ty_vstore(a_t, ast::vstore_uniq) {
mk_vstore(self, rscope, a_t, ty::vstore_uniq)
}
ast::ty_vstore(a_t, ast::vstore_box) {
mk_vstore(self, rscope, a_t, ty::vstore_box)
}
ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) {
mk_vstore(self, rscope, a_t, ty::vstore_fixed(u))
}
ast::ty_vstore(_, ast::vstore_fixed(none)) {
tcx.sess.span_bug(
ast_ty.span,
"implied fixed length for bound");
}
ast::ty_constr(t, cs) {
let mut out_cs = [];
for cs.each {|constr|
out_cs += [ty::ast_constr_to_constr(tcx, constr)];
}
ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs)
}
ast::ty_infer {
// ty_infer should only appear as the type of arguments or return
// values in a fn_expr, or as the type of local variables. Both of
// these cases are handled specially and should not descend into this
// routine.
self.tcx().sess.span_bug(
ast_ty.span,
"found `ty_infer` in unexpected place");
}
ast::ty_mac(_) {
tcx.sess.span_bug(ast_ty.span,
"found `ty_mac` in unexpected place");
}
};
tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_resolved(typ));
ret typ;
}
fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
-> ty::ty_param_bounds_and_ty {
let def_id = local_def(it.id);
let tcx = ccx.tcx;
alt tcx.tcache.find(def_id) {
some(tpt) { ret tpt; }
_ {}
}
alt it.node {
ast::item_const(t, _) {
let typ = ccx.to_ty(empty_rscope, t);
let tpt = no_params(typ);
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_fn(decl, tps, _) {
let bounds = ty_param_bounds(ccx, tps);
let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare,
decl, none);
let tpt = {bounds: bounds,
rp: ast::rp_none, // functions do not have a self
ty: ty::mk_fn(ccx.tcx, tofd)};
ccx.tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_ty(t, tps, rp) {
alt tcx.tcache.find(local_def(it.id)) {
some(tpt) { ret tpt; }
none { }
}
let tpt = {
let ty = {
let t0 = ccx.to_ty(type_rscope(rp), t);
// Do not associate a def id with a named, parameterized type
// like "foo<X>". This is because otherwise ty_to_str will
// print the name as merely "foo", as it has no way to
// reconstruct the value of X.
if !vec::is_empty(tps) { t0 } else {
ty::mk_with_id(tcx, t0, def_id)
}
};
{bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty}
};
check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty);
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_res(decl, tps, _, _, _, rp) {
let {bounds, substs} = mk_substs(ccx, tps, rp);
let t_arg = ty_of_arg(ccx, type_rscope(rp),
decl.inputs[0], none);
let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs);
let t_res = {bounds: bounds, rp: rp, ty: t};
tcx.tcache.insert(local_def(it.id), t_res);
ret t_res;
}
ast::item_enum(_, tps, rp) {
// Create a new generic polytype.
let {bounds, substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_enum(tcx, local_def(it.id), substs);
let tpt = {bounds: bounds, rp: rp, ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_iface(tps, rp, ms) {
let {bounds, substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_iface(tcx, local_def(it.id), substs);
let tpt = {bounds: bounds, rp: rp, ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_class(tps, _, _, _, _, rp) {
let {bounds,substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_class(tcx, local_def(it.id), substs);
let tpt = {bounds: bounds, rp: rp, ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
ret tpt;
}
ast::item_impl(*) | ast::item_mod(_) |
ast::item_native_mod(_) { fail; }
}
}
fn ty_of_native_item(ccx: @crate_ctxt, it: @ast::native_item)
-> ty::ty_param_bounds_and_ty {
alt it.node {
ast::native_item_fn(fn_decl, params) {
ret ty_of_native_fn_decl(ccx, fn_decl, params,
local_def(it.id));
}
}
}
fn ty_of_arg<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, a: ast::arg,
expected_ty: option<ty::arg>) -> ty::arg {
let ty = alt a.ty.node {
ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty}
ast::ty_infer {self.ty_infer(a.ty.span)}
_ {ast_ty_to_ty(self, rscope, a.ty)}
};
let mode = {
alt a.mode {
ast::infer(_) if expected_ty.is_some() {
result::get(ty::unify_mode(self.tcx(), a.mode,
expected_ty.get().mode))
}
ast::infer(_) {
alt ty::get(ty).struct {
// If the type is not specified, then this must be a fn expr.
// Leave the mode as infer(_), it will get inferred based
// on constraints elsewhere.
ty::ty_var(_) {a.mode}
// If the type is known, then use the default for that type.
// Here we unify m and the default. This should update the
// tables in tcx but should never fail, because nothing else
// will have been unified with m yet:
_ {
let m1 = ast::expl(ty::default_arg_mode_for_ty(ty));
result::get(ty::unify_mode(self.tcx(), a.mode, m1))
}
}
}
ast::expl(_) {a.mode}
}
};
{mode: mode, ty: ty}
}
type expected_tys = option<{inputs: [ty::arg],
output: ty::t}>;
fn ty_of_fn_decl<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS,
proto: ast::proto,
decl: ast::fn_decl,
expected_tys: expected_tys) -> ty::fn_ty {
#debug["ty_of_fn_decl"];
indent {||
// new region names that appear inside of the fn decl are bound to
// that function type
let rb = in_binding_rscope(rscope);
let input_tys = decl.inputs.mapi { |i, a|
let expected_arg_ty = expected_tys.chain { |e|
// no guarantee that the correct number of expected args
// were supplied
if i < e.inputs.len() {some(e.inputs[i])} else {none}
};
ty_of_arg(self, rb, a, expected_arg_ty)
};
let expected_ret_ty = expected_tys.map { |e| e.output };
let output_ty = alt decl.output.node {
ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()}
ast::ty_infer {self.ty_infer(decl.output.span)}
_ {ast_ty_to_ty(self, rb, decl.output)}
};
let out_constrs = vec::map(decl.constraints) {|constr|
ty::ast_constr_to_constr(self.tcx(), constr)
};
{proto: proto, inputs: input_tys,
output: output_ty, ret_style: decl.cf, constraints: out_constrs}
}
}
fn ty_param_bounds(ccx: @crate_ctxt,
params: [ast::ty_param]) -> @[ty::param_bounds] {
fn compute_bounds(ccx: @crate_ctxt,
param: ast::ty_param) -> ty::param_bounds {
@vec::flat_map(*param.bounds) { |b|
alt b {
ast::bound_send { [ty::bound_send] }
ast::bound_copy { [ty::bound_copy] }
ast::bound_iface(t) {
let ity = ast_ty_to_ty(ccx, empty_rscope, t);
alt ty::get(ity).struct {
ty::ty_iface(*) {
[ty::bound_iface(ity)]
}
_ {
ccx.tcx.sess.span_err(
t.span, "type parameter bounds must be \
interface types");
[]
}
}
}
}
}
}
@params.map { |param|
alt ccx.tcx.ty_param_bounds.find(param.id) {
some(bs) { bs }
none {
let bounds = compute_bounds(ccx, param);
ccx.tcx.ty_param_bounds.insert(param.id, bounds);
bounds
}
}
}
}
fn ty_of_native_fn_decl(ccx: @crate_ctxt,
decl: ast::fn_decl,
ty_params: [ast::ty_param],
def_id: ast::def_id) -> ty::ty_param_bounds_and_ty {
let bounds = ty_param_bounds(ccx, ty_params);
let rb = in_binding_rscope(empty_rscope);
let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) };
let output_ty = ast_ty_to_ty(ccx, rb, decl.output);
let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare,
inputs: input_tys,
output: output_ty,
ret_style: ast::return_val,
constraints: []});
let tpt = {bounds: bounds, rp: ast::rp_none, ty: t_fn};
ccx.tcx.tcache.insert(def_id, tpt);
ret tpt;
}

View File

@ -0,0 +1,343 @@
import astconv::{type_rscope, instantiate_iface_ref, ty_of_item,
empty_rscope, ty_of_native_item, ast_conv};
// 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 get_enum_variant_types(ccx: @crate_ctxt,
enum_ty: ty::t,
variants: [ast::variant],
ty_params: [ast::ty_param],
rp: ast::region_param) {
let tcx = ccx.tcx;
// Create a set of parameter types shared among all the variants.
for variants.each {|variant|
// Nullary enum constructors get turned into constants; n-ary enum
// constructors get turned into functions.
let result_ty = if vec::len(variant.node.args) == 0u {
enum_ty
} else {
let rs = type_rscope(rp);
let args = variant.node.args.map { |va|
let arg_ty = ccx.to_ty(rs, va.ty);
{mode: ast::expl(ast::by_copy), ty: arg_ty}
};
ty::mk_fn(tcx, {proto: ast::proto_box,
inputs: args,
output: enum_ty,
ret_style: ast::return_val,
constraints: []})
};
let tpt = {bounds: astconv::ty_param_bounds(ccx, ty_params),
rp: rp,
ty: result_ty};
tcx.tcache.insert(local_def(variant.node.id), tpt);
write_ty_to_tcx(tcx, variant.node.id, result_ty);
}
}
fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) {
fn store_methods<T>(ccx: @crate_ctxt, id: ast::node_id,
stuff: [T], f: fn@(T) -> ty::method) {
ty::store_iface_methods(ccx.tcx, id, @vec::map(stuff, f));
}
let tcx = ccx.tcx;
alt check tcx.items.get(id) {
ast_map::node_item(@{node: ast::item_iface(_, rp, ms), _}, _) {
store_methods::<ast::ty_method>(ccx, id, ms) {|m|
ty_of_ty_method(ccx, m, rp)
};
}
ast_map::node_item(@{node: ast::item_class(_,_,its,_,_,rp), _}, _) {
let (_,ms) = split_class_items(its);
// All methods need to be stored, since lookup_method
// relies on the same method cache for self-calls
store_methods::<@ast::method>(ccx, id, ms) {|m|
ty_of_method(ccx, m, rp)
};
}
}
}
fn check_methods_against_iface(ccx: @crate_ctxt,
tps: [ast::ty_param],
rp: ast::region_param,
selfty: ty::t,
a_ifacety: @ast::iface_ref,
ms: [@ast::method]) {
let tcx = ccx.tcx;
let i_bounds = astconv::ty_param_bounds(ccx, tps);
let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty);
let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp);
if did.crate == ast::local_crate {
ensure_iface_methods(ccx, did.node);
}
for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) {
some({mty: m, id, span}) {
if m.purity != if_m.purity {
ccx.tcx.sess.span_err(
span, #fmt["method `%s`'s purity \
not match the iface method's \
purity", m.ident]);
}
let mt = compare_impl_method(
ccx.tcx, span, m, vec::len(tps),
if_m, tpt.substs, selfty);
let old = tcx.tcache.get(local_def(id));
if old.ty != mt {
tcx.tcache.insert(
local_def(id),
{bounds: old.bounds,
rp: old.rp,
ty: mt});
write_ty_to_tcx(tcx, id, mt);
}
}
none {
tcx.sess.span_err(
a_ifacety.path.span,
#fmt["missing method `%s`", if_m.ident]);
}
} // alt
} // |if_m|
} // fn
fn convert_class_item(ccx: @crate_ctxt,
rp: ast::region_param,
v: ast_util::ivar) {
/* we want to do something here, b/c within the
scope of the class, it's ok to refer to fields &
methods unqualified */
/* they have these types *within the scope* of the
class. outside the class, it's done with expr_field */
let tt = ccx.to_ty(type_rscope(rp), v.ty);
write_ty_to_tcx(ccx.tcx, v.id, tt);
}
fn convert_methods(ccx: @crate_ctxt,
ms: [@ast::method],
rp: ast::region_param,
i_bounds: @[ty::param_bounds],
self_ty: ty::t)
-> [{mty: ty::method, id: ast::node_id, span: span}] {
let tcx = ccx.tcx;
vec::map(ms) { |m|
write_ty_to_tcx(tcx, m.self_id, self_ty);
let bounds = astconv::ty_param_bounds(ccx, m.tps);
let mty = ty_of_method(ccx, m, rp);
let fty = ty::mk_fn(tcx, mty.fty);
tcx.tcache.insert(
local_def(m.id),
// n.b. This code is kind of sketchy (concat'ing i_bounds
// with bounds), but removing *i_bounds breaks other stuff
{bounds: @(*i_bounds + *bounds), rp: rp, ty: fty});
write_ty_to_tcx(tcx, m.id, fty);
{mty: mty, id: m.id, span: m.span}
}
}
fn convert(ccx: @crate_ctxt, it: @ast::item) {
let tcx = ccx.tcx;
alt it.node {
// These don't define types.
ast::item_mod(_) {}
ast::item_native_mod(m) {
if syntax::attr::native_abi(it.attrs) ==
either::right(ast::native_abi_rust_intrinsic) {
for m.items.each { |item| check_intrinsic_type(ccx, item); }
}
}
ast::item_enum(variants, ty_params, rp) {
let tpt = ty_of_item(ccx, it);
write_ty_to_tcx(tcx, it.id, tpt.ty);
get_enum_variant_types(ccx, tpt.ty, variants,
ty_params, rp);
}
ast::item_impl(tps, rp, ifce, selfty, ms) {
let i_bounds = astconv::ty_param_bounds(ccx, tps);
let selfty = ccx.to_ty(type_rscope(rp), selfty);
write_ty_to_tcx(tcx, it.id, selfty);
tcx.tcache.insert(local_def(it.id),
{bounds: i_bounds,
rp: rp,
ty: selfty});
alt ifce {
some(t) {
check_methods_against_iface(
ccx, tps, rp,
selfty, t, ms);
}
_ {
// Still have to do this to write method types
// into the table
convert_methods(
ccx, ms, rp,
i_bounds, selfty);
}
}
}
ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) {
let {bounds, substs} = mk_substs(ccx, tps, rp);
let def_id = local_def(it.id);
let t_arg = astconv::ty_of_arg(ccx, type_rscope(rp),
decl.inputs[0], none);
let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs);
let t_ctor = ty::mk_fn(tcx, {
proto: ast::proto_box,
inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}],
output: t_res,
ret_style: ast::return_val, constraints: []
});
let t_dtor = ty::mk_fn(tcx, {
proto: ast::proto_box,
inputs: [t_arg], output: ty::mk_nil(tcx),
ret_style: ast::return_val, constraints: []
});
write_ty_to_tcx(tcx, it.id, t_res);
write_ty_to_tcx(tcx, ctor_id, t_ctor);
tcx.tcache.insert(local_def(ctor_id),
{bounds: bounds,
rp: rp,
ty: t_ctor});
tcx.tcache.insert(def_id, {bounds: bounds,
rp: rp,
ty: t_res});
write_ty_to_tcx(tcx, dtor_id, t_dtor);
}
ast::item_iface(*) {
let tpt = ty_of_item(ccx, it);
#debug["item_iface(it.id=%d, tpt.ty=%s)",
it.id, ty_to_str(tcx, tpt.ty)];
write_ty_to_tcx(tcx, it.id, tpt.ty);
ensure_iface_methods(ccx, it.id);
}
ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) {
// Write the class type
let tpt = ty_of_item(ccx, it);
write_ty_to_tcx(tcx, it.id, tpt.ty);
// Write the ctor type
let t_ctor =
ty::mk_fn(
tcx,
astconv::ty_of_fn_decl(ccx,
empty_rscope,
ast::proto_any,
ctor.node.dec,
none));
write_ty_to_tcx(tcx, ctor.node.id, t_ctor);
tcx.tcache.insert(local_def(ctor.node.id),
{bounds: tpt.bounds,
rp: ast::rp_none,
ty: t_ctor});
option::iter(m_dtor) {|dtor|
// Write the dtor type
let t_dtor = ty::mk_fn(
tcx,
// not sure about empty_rscope
// FIXME
astconv::ty_of_fn_decl(ccx,
empty_rscope,
ast::proto_any,
ast_util::dtor_dec(),
none));
write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
tcx.tcache.insert(local_def(dtor.node.id),
{bounds: tpt.bounds,
rp: ast::rp_none,
ty: t_dtor});
};
ensure_iface_methods(ccx, it.id);
/* FIXME: check for proper public/privateness */
// Write the type of each of the members
let (fields, methods) = split_class_items(members);
for fields.each {|f|
convert_class_item(ccx, rp, f);
}
// The selfty is just the class type
let {bounds:_, substs} = mk_substs(ccx, tps, rp);
let selfty = ty::mk_class(tcx, local_def(it.id), substs);
// Need to convert all methods so we can check internal
// references to private methods
// NDM to TJC---I think we ought to be using bounds here, not @[].
// But doing so causes errors later on.
convert_methods(ccx, methods, rp, @[], selfty);
/*
Finally, check that the class really implements the ifaces
that it claims to implement.
*/
for ifaces.each { |ifce|
check_methods_against_iface(ccx, tps, rp, selfty,
ifce, methods);
let t = ty::node_id_to_type(tcx, ifce.id);
// FIXME: This assumes classes only implement
// non-parameterized ifaces. add a test case for
// a class implementing a parameterized iface.
// -- tjc (#1726)
tcx.tcache.insert(local_def(ifce.id), no_params(t));
}
}
_ {
// This call populates the type cache with the converted type
// of the item in passing. All we have to do here is to write
// it into the node type table.
let tpt = ty_of_item(ccx, it);
write_ty_to_tcx(tcx, it.id, tpt.ty);
}
}
}
fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) {
// As above, this call populates the type table with the converted
// type of the native item. We simply write it into the node type
// table.
let tpt = ty_of_native_item(ccx, i);
alt i.node {
ast::native_item_fn(_, _) {
write_ty_to_tcx(ccx.tcx, i.id, tpt.ty);
}
}
}
fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{
visit_item: bind convert(ccx, _),
visit_native_item: bind convert_native(ccx, _)
with *visit::default_simple_visitor()
}));
}
fn ty_of_method(ccx: @crate_ctxt,
m: @ast::method,
rp: ast::region_param) -> ty::method {
{ident: m.ident,
tps: astconv::ty_param_bounds(ccx, m.tps),
fty: astconv::ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare,
m.decl, none),
purity: m.decl.purity,
vis: m.vis}
}
fn ty_of_ty_method(self: @crate_ctxt,
m: ast::ty_method,
rp: ast::region_param) -> ty::method {
{ident: m.ident,
tps: astconv::ty_param_bounds(self, m.tps),
fty: astconv::ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare,
m.decl, none),
// assume public, because this is only invoked on iface methods
purity: m.decl.purity, vis: ast::public}
}

View File

@ -0,0 +1,271 @@
fn has_iface_bounds(tps: [ty::param_bounds]) -> bool {
vec::any(tps, {|bs|
vec::any(*bs, {|b|
alt b { ty::bound_iface(_) { true } _ { false } }
})
})
}
fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
bounds: @[ty::param_bounds], substs: ty::substs,
allow_unsafe: bool) -> vtable_res {
let tcx = fcx.ccx.tcx;
let mut result = [], i = 0u;
for substs.tps.each {|ty|
for vec::each(*bounds[i]) {|bound|
alt bound {
ty::bound_iface(i_ty) {
let i_ty = ty::subst(tcx, substs, i_ty);
result += [lookup_vtable(fcx, isc, sp, ty, i_ty,
allow_unsafe)];
}
_ {}
}
}
i += 1u;
}
@result
}
fn fixup_substs(fcx: @fn_ctxt, sp: span,
id: ast::def_id, substs: ty::substs) -> ty::substs {
let tcx = fcx.ccx.tcx;
// use a dummy type just to package up the substs that need fixing up
let t = ty::mk_iface(tcx, id, substs);
let t_f = fixup_ty(fcx, sp, t);
alt check ty::get(t_f).struct {
ty::ty_iface(_, substs_f) { substs_f }
}
}
fn relate_iface_tys(fcx: @fn_ctxt, sp: span,
exp_iface_ty: ty::t, act_iface_ty: ty::t) {
demand::suptype(fcx, sp, exp_iface_ty, act_iface_ty)
}
/*
Look up the vtable to use when treating an item of type <t>
as if it has type <iface_ty>
*/
fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
ty: ty::t, iface_ty: ty::t, allow_unsafe: bool)
-> vtable_origin {
#debug["lookup_vtable(ty=%s, iface_ty=%s)",
fcx.ty_to_str(ty), fcx.ty_to_str(iface_ty)];
let _i = indenter();
let tcx = fcx.ccx.tcx;
let (iface_id, iface_substs) = alt check ty::get(iface_ty).struct {
ty::ty_iface(did, substs) { (did, substs) }
};
let ty = fixup_ty(fcx, sp, ty);
alt ty::get(ty).struct {
ty::ty_param(n, did) {
let mut n_bound = 0u;
for vec::each(*tcx.ty_param_bounds.get(did.node)) { |bound|
alt bound {
ty::bound_send | ty::bound_copy { /* ignore */ }
ty::bound_iface(ity) {
alt check ty::get(ity).struct {
ty::ty_iface(idid, substs) {
if iface_id == idid {
relate_iface_tys(fcx, sp, iface_ty, ity);
ret vtable_param(n, n_bound);
}
}
}
n_bound += 1u;
}
}
}
}
ty::ty_iface(did, substs) if iface_id == did {
relate_iface_tys(fcx, sp, iface_ty, ty);
if !allow_unsafe {
for vec::each(*ty::iface_methods(tcx, did)) {|m|
if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
tcx.sess.span_err(
sp, "a boxed iface with self types may not be \
passed as a bounded type");
} else if (*m.tps).len() > 0u {
tcx.sess.span_err(
sp, "a boxed iface with generic methods may not \
be passed as a bounded type");
}
}
}
ret vtable_iface(did, substs.tps);
}
_ {
let mut found = [];
for list::each(isc) {|impls|
/* For each impl in scope... */
for vec::each(*impls) {|im|
// im = one specific impl
// find the iface that im implements (if any)
let of_ty = alt ty::impl_iface(tcx, im.did) {
some(of_ty) { of_ty }
_ { cont; }
};
// it must have the same id as the expected one
alt ty::get(of_ty).struct {
ty::ty_iface(id, _) if id != iface_id { cont; }
_ { /* ok */ }
}
// check whether the type unifies with the type
// that the impl is for, and continue if not
let {substs: substs, ty: for_ty} =
impl_self_ty(fcx, im.did);
let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
alt fcx.mk_subty(ty, for_ty) {
result::err(_) { cont; }
result::ok(()) { }
}
// check that desired iface type unifies
let of_ty = ty::subst(tcx, substs, of_ty);
relate_iface_tys(fcx, sp, iface_ty, of_ty);
// recursively process the bounds
let iface_tps = iface_substs.tps;
let substs_f = fixup_substs(fcx, sp, iface_id, substs);
connect_iface_tps(fcx, sp, substs_f.tps,
iface_tps, im.did);
let subres = lookup_vtables(fcx, isc, sp,
im_bs, substs_f, false);
found += [vtable_static(im.did, substs_f.tps, subres)];
}
alt found.len() {
0u { /* fallthrough */ }
1u { ret found[0]; }
_ {
fcx.ccx.tcx.sess.span_err(
sp, "multiple applicable methods in scope");
ret found[0];
}
}
}
}
}
tcx.sess.span_fatal(
sp, "failed to find an implementation of interface " +
ty_to_str(tcx, iface_ty) + " for " +
ty_to_str(tcx, ty));
}
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
let tcx = fcx.ccx.tcx;
alt infer::resolve_deep(fcx.infcx, ty, true) {
result::ok(new_type) { new_type }
result::err(e) {
tcx.sess.span_fatal(
sp,
#fmt["cannot determine a type \
for this bounded type parameter: %s",
infer::fixup_err_to_str(e)])
}
}
}
fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t],
iface_tys: [ty::t], impl_did: ast::def_id) {
let tcx = fcx.ccx.tcx;
let ity = option::get(ty::impl_iface(tcx, impl_did));
let iface_ty = ty::subst_tps(tcx, impl_tys, ity);
alt check ty::get(iface_ty).struct {
ty::ty_iface(_, substs) {
vec::iter2(substs.tps, iface_tys,
{|a, b| demand::suptype(fcx, sp, a, b);});
}
}
}
fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
let cx = fcx.ccx;
alt ex.node {
ast::expr_path(*) {
alt fcx.opt_node_ty_substs(ex.id) {
some(substs) {
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
let item_ty = ty::lookup_item_type(cx.tcx, did);
if has_iface_bounds(*item_ty.bounds) {
let impls = cx.impl_map.get(ex.id);
cx.vtable_map.insert(ex.id, lookup_vtables(
fcx, impls, ex.span,
item_ty.bounds, substs, false));
}
}
_ {}
}
}
// Must resolve bounds on methods with bounded params
ast::expr_field(*) | ast::expr_binary(*) |
ast::expr_unary(*) | ast::expr_assign_op(*) |
ast::expr_index(*) {
alt cx.method_map.find(ex.id) {
some(method_static(did)) {
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
if has_iface_bounds(*bounds) {
let callee_id = alt ex.node {
ast::expr_field(_, _, _) { ex.id }
_ { ast_util::op_expr_callee_id(ex) }
};
let substs = fcx.node_ty_substs(callee_id);
let iscs = cx.impl_map.get(ex.id);
cx.vtable_map.insert(callee_id, lookup_vtables(
fcx, iscs, ex.span, bounds, substs, false));
}
}
_ {}
}
}
ast::expr_cast(src, _) {
let target_ty = fcx.expr_ty(ex);
alt ty::get(target_ty).struct {
ty::ty_iface(*) {
/* Casting to an interface type.
Look up all impls for the cast expr...
*/
let impls = cx.impl_map.get(ex.id);
/*
Look up vtables for the type we're casting to,
passing in the source and target type
*/
let vtable = lookup_vtable(fcx, impls, ex.span,
fcx.expr_ty(src), target_ty,
true);
/*
Map this expression to that vtable (that is: "ex has
vtable <vtable>")
*/
cx.vtable_map.insert(ex.id, @[vtable]);
}
_ {}
}
}
_ {}
}
visit::visit_expr(ex, fcx, v);
}
// Detect points where an interface-bounded type parameter is
// instantiated, resolve the impls for the parameters.
fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
visit::visit_block(bl, fcx, visit::mk_vt(@{
visit_expr: resolve_expr,
visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt,
_v: visit::vt<@fn_ctxt>) {}
with *visit::default_visitor()
}));
}

View File

@ -0,0 +1,184 @@
// Type resolution: the phase that finds all the types in the AST with
// unresolved type variables and replaces "ty_var" types with their
// substitutions.
export resolve_type_vars_in_fn;
export resolve_type_vars_in_expr;
fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
option<ty::t> {
if !ty::type_needs_infer(typ) { ret some(typ); }
alt infer::resolve_deep(fcx.infcx, typ, true) {
result::ok(new_type) { ret some(new_type); }
result::err(e) {
if !fcx.ccx.tcx.sess.has_errors() {
fcx.ccx.tcx.sess.span_err(
sp,
#fmt["cannot determine a type \
for this expression: %s",
infer::fixup_err_to_str(e)])
}
ret none;
}
}
}
fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
-> option<ty::t> {
let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
let n_ty = fcx.node_ty(id);
alt resolve_type_vars_in_type(fcx, sp, n_ty) {
none {
wbcx.success = false;
ret none;
}
some(t) {
#debug["resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)",
id, ty_to_str(tcx, n_ty), ty_to_str(tcx, t)];
write_ty_to_tcx(tcx, id, t);
alt fcx.opt_node_ty_substs(id) {
some(substs) {
let mut new_tps = [];
for substs.tps.each {|subst|
alt resolve_type_vars_in_type(fcx, sp, subst) {
some(t) { new_tps += [t]; }
none { wbcx.success = false; ret none; }
}
}
write_substs_to_tcx(tcx, id, new_tps);
}
none {}
}
ret some(t);
}
}
}
fn maybe_resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span,
id: ast::node_id)
-> option<ty::t> {
if wbcx.fcx.node_types.contains_key(id as uint) {
resolve_type_vars_for_node(wbcx, sp, id)
} else {
none
}
}
type wb_ctxt =
// As soon as we hit an error we have to stop resolving
// the entire function
{fcx: @fn_ctxt, mut success: bool};
type wb_vt = visit::vt<wb_ctxt>;
fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
visit::visit_stmt(s, wbcx, v);
}
fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
resolve_type_vars_for_node(wbcx, e.span, e.id);
alt e.node {
ast::expr_fn(_, decl, _, _) |
ast::expr_fn_block(decl, _, _) {
vec::iter(decl.inputs) {|input|
let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
// Just in case we never constrained the mode to anything,
// constrain it to the default for the type in question.
alt (r_ty, input.mode) {
(some(t), ast::infer(_)) {
let tcx = wbcx.fcx.ccx.tcx;
let m_def = ty::default_arg_mode_for_ty(t);
ty::set_default_mode(tcx, input.mode, m_def);
}
_ {}
}
}
}
ast::expr_new(_, alloc_id, _) {
resolve_type_vars_for_node(wbcx, e.span, alloc_id);
}
ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
maybe_resolve_type_vars_for_node(wbcx, e.span,
ast_util::op_expr_callee_id(e));
}
_ { }
}
visit::visit_expr(e, wbcx, v);
}
fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
resolve_type_vars_for_node(wbcx, b.span, b.node.id);
visit::visit_block(b, wbcx, v);
}
fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
resolve_type_vars_for_node(wbcx, p.span, p.id);
#debug["Type for pattern binding %s (id %d) resolved to %s",
pat_to_str(p), p.id,
wbcx.fcx.ty_to_str(
ty::node_id_to_type(wbcx.fcx.ccx.tcx,
p.id))];
visit::visit_pat(p, wbcx, v);
}
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) {
result::ok(lty) {
#debug["Type for local %s (id %d) resolved to %s",
pat_to_str(l.node.pat), l.node.id,
wbcx.fcx.ty_to_str(lty)];
write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.node.id, lty);
}
result::err(e) {
wbcx.fcx.ccx.tcx.sess.span_err(
l.span,
#fmt["cannot determine a type \
for this local variable: %s",
infer::fixup_err_to_str(e)]);
wbcx.success = false;
}
}
visit::visit_local(l, wbcx, v);
}
fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) {
// Ignore items
}
fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
let wbcx = {fcx: fcx, mut success: true};
let visit =
visit::mk_vt(@{visit_item: visit_item,
visit_stmt: visit_stmt,
visit_expr: visit_expr,
visit_block: visit_block,
visit_pat: visit_pat,
visit_local: visit_local
with *visit::default_visitor()});
visit.visit_expr(e, wbcx, visit);
ret wbcx.success;
}
fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
decl: ast::fn_decl,
blk: ast::blk) -> bool {
let wbcx = {fcx: fcx, mut success: true};
let visit =
visit::mk_vt(@{visit_item: visit_item,
visit_stmt: visit_stmt,
visit_expr: visit_expr,
visit_block: visit_block,
visit_pat: visit_pat,
visit_local: visit_local
with *visit::default_visitor()});
visit.visit_block(blk, wbcx, visit);
for decl.inputs.each {|arg|
resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
}
ret wbcx.success;
}

View File

@ -52,7 +52,12 @@ mod middle {
mod infer;
mod ast_map;
mod resolve;
mod typeck;
mod typeck {
mod astconv;
mod collect;
mod vtable;
mod writeback;
}
mod check_loop;
mod check_alt;
mod check_const;