break apart typeck a little (more to come)
This commit is contained in:
parent
adb61e3e99
commit
26dc48d67f
File diff suppressed because it is too large
Load Diff
711
src/rustc/middle/typeck/astconv.rs
Normal file
711
src/rustc/middle/typeck/astconv.rs
Normal 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;
|
||||
}
|
343
src/rustc/middle/typeck/collect.rs
Normal file
343
src/rustc/middle/typeck/collect.rs
Normal 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}
|
||||
}
|
271
src/rustc/middle/typeck/vtable.rs
Normal file
271
src/rustc/middle/typeck/vtable.rs
Normal 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()
|
||||
}));
|
||||
}
|
||||
|
||||
|
184
src/rustc/middle/typeck/writeback.rs
Normal file
184
src/rustc/middle/typeck/writeback.rs
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user