Track the type of self properly. Closes #3247.

This commit is contained in:
Michael Sullivan 2012-08-22 13:18:29 -07:00
parent 7284969292
commit 744fea1a4f
7 changed files with 107 additions and 96 deletions

View File

@ -54,32 +54,28 @@ fn trans_method(ccx: @crate_ctxt,
method: &ast::method,
param_substs: option<param_substs>,
llfn: ValueRef) {
// determine the (monomorphized) type that `self` maps to for this method
let self_ty = ty::node_id_to_type(ccx.tcx, method.self_id);
let self_ty = match param_substs {
none => self_ty,
some({tys: ref tys, _}) => ty::subst_tps(ccx.tcx, *tys, self_ty)
};
// apply any transformations from the explicit self declaration
// figure out how self is being passed
let self_arg = match method.self_ty.node {
ast::sty_static => {
no_self
}
ast::sty_box(_) => {
impl_self(ty::mk_imm_box(ccx.tcx, self_ty))
}
ast::sty_uniq(_) => {
impl_self(ty::mk_imm_uniq(ccx.tcx, self_ty))
}
ast::sty_region(*) => {
impl_self(ty::mk_imm_ptr(ccx.tcx, self_ty))
}
ast::sty_value => {
impl_owned_self(self_ty)
}
ast::sty_by_ref => {
impl_self(self_ty)
_ => {
// determine the (monomorphized) type that `self` maps to for
// this method
let self_ty = ty::node_id_to_type(ccx.tcx, method.self_id);
let self_ty = match param_substs {
none => self_ty,
some({tys: ref tys, _}) => ty::subst_tps(ccx.tcx, *tys, self_ty)
};
match method.self_ty.node {
ast::sty_value => {
impl_owned_self(self_ty)
}
_ => {
impl_self(self_ty)
}
}
}
};

View File

@ -79,15 +79,16 @@ import std::map::str_hash;
type self_info = {
self_ty: ty::t,
self_id: ast::node_id,
def_id: ast::def_id,
explicit_self: ast::self_ty_
explicit_self: ast::self_ty
};
type fn_ctxt_ =
// var_bindings, locals and next_var_id are shared
// with any nested functions that capture the environment
// (and with any functions whose environment is being captured).
{self_info: option<self_info>,
{self_impl_def_id: option<ast::def_id>,
ret_ty: ty::t,
// Used by loop bodies that return from the outer function
indirect_ret_ty: option<ty::t>,
@ -126,7 +127,7 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
region_bnd: ast::node_id) -> @fn_ctxt {
// It's kind of a kludge to manufacture a fake function context
// and statement context, but we might as well do write the code only once
@fn_ctxt_({self_info: none,
@fn_ctxt_({self_impl_def_id: none,
ret_ty: rty,
indirect_ret_ty: none,
purity: ast::pure_fn,
@ -244,7 +245,7 @@ fn check_fn(ccx: @crate_ctxt,
}
} else { none };
@fn_ctxt_({self_info: self_info,
@fn_ctxt_({self_impl_def_id: self_info.map(|info| info.def_id),
ret_ty: ret_ty,
indirect_ret_ty: indirect_ret_ty,
purity: purity,
@ -257,7 +258,22 @@ fn check_fn(ccx: @crate_ctxt,
ccx: ccx})
};
gather_locals(fcx, decl, body, arg_tys);
// Update the self_info to contain an accurate self type (taking
// into account explicit self).
let self_info = do self_info.chain |info| {
// If the self type is sty_static, we don't have a self ty.
if info.explicit_self.node == ast::sty_static {
none
} else {
let self_region = fcx.in_scope_regions.find(ty::br_self);
let ty = method::transform_self_type_for_method(
fcx.tcx(), self_region,
info.self_ty, info.explicit_self.node);
some({self_ty: ty with info})
}
};
gather_locals(fcx, decl, body, arg_tys, self_info);
check_block(fcx, body);
// We unify the tail expr's type with the
@ -270,10 +286,11 @@ fn check_fn(ccx: @crate_ctxt,
none => ()
}
let mut i = 0u;
do vec::iter(arg_tys) |arg| {
fcx.write_ty(decl.inputs[i].id, arg);
i += 1u;
for self_info.each |info| {
fcx.write_ty(info.self_id, info.self_ty);
}
do vec::iter2(decl.inputs, arg_tys) |input, arg| {
fcx.write_ty(input.id, arg);
}
// If we don't have any enclosing function scope, it is time to
@ -283,13 +300,14 @@ fn check_fn(ccx: @crate_ctxt,
if option::is_none(old_fcx) {
vtable::resolve_in_block(fcx, body);
regionck::regionck_fn(fcx, decl, body);
writeback::resolve_type_vars_in_fn(fcx, decl, body);
writeback::resolve_type_vars_in_fn(fcx, decl, body, self_info);
}
fn gather_locals(fcx: @fn_ctxt,
decl: ast::fn_decl,
body: ast::blk,
arg_tys: ~[ty::t]) {
arg_tys: ~[ty::t],
self_info: option<self_info>) {
let tcx = fcx.ccx.tcx;
let assign = fn@(span: span, nid: ast::node_id,
@ -305,6 +323,14 @@ fn check_fn(ccx: @crate_ctxt,
}
};
// Add the self parameter
for self_info.each |info| {
assign(info.explicit_self.span,
info.self_id, some(info.self_ty));
debug!{"self is assigned to %s",
fcx.locals.get(info.self_id).to_str()};
}
// Add formal parameters.
do vec::iter2(arg_tys, decl.inputs) |arg_ty, input| {
assign(input.ty.span, input.id, some(arg_ty));
@ -369,8 +395,11 @@ fn check_fn(ccx: @crate_ctxt,
}
fn check_method(ccx: @crate_ctxt, method: @ast::method,
self_info: self_info) {
self_ty: ty::t, self_impl_def_id: ast::def_id) {
let self_info = {self_ty: self_ty,
self_id: method.self_id,
def_id: self_impl_def_id,
explicit_self: method.self_ty };
check_bare_fn(ccx, method.decl, method.body, method.id, some(self_info));
}
@ -404,33 +433,31 @@ fn check_struct(ccx: @crate_ctxt, struct_def: @ast::struct_def,
do option::iter(struct_def.ctor) |ctor| {
let class_t = {self_ty: self_ty,
self_id: ctor.node.self_id,
def_id: local_def(id),
explicit_self: ast::sty_by_ref};
explicit_self: {node: ast::sty_by_ref,
span: ast_util::dummy_sp()}};
// typecheck the ctor
check_bare_fn(ccx, ctor.node.dec,
ctor.node.body, ctor.node.id,
some(class_t));
// Write the ctor's self's type
write_ty_to_tcx(tcx, ctor.node.self_id, class_t.self_ty);
}
do option::iter(struct_def.dtor) |dtor| {
let class_t = {self_ty: self_ty,
self_id: dtor.node.self_id,
def_id: local_def(id),
explicit_self: ast::sty_by_ref};
explicit_self: {node: ast::sty_by_ref,
span: ast_util::dummy_sp()}};
// typecheck the dtor
check_bare_fn(ccx, ast_util::dtor_dec(),
dtor.node.body, dtor.node.id,
some(class_t));
// Write the dtor's self's type
write_ty_to_tcx(tcx, dtor.node.self_id, class_t.self_ty);
};
// typecheck the methods
for struct_def.methods.each |m| {
check_method(ccx, m, {self_ty: self_ty,
def_id: local_def(id),
explicit_self: m.self_ty.node});
check_method(ccx, m, self_ty, local_def(id));
}
// Check that there's at least one field
if struct_def.fields.len() < 1u {
@ -455,10 +482,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
*it.ident, it.id, rp};
let self_ty = ccx.to_ty(rscope::type_rscope(rp), ty);
for ms.each |m| {
let self_info = {self_ty: self_ty,
def_id: local_def(it.id),
explicit_self: m.self_ty.node };
check_method(ccx, m, self_info)
check_method(ccx, m, self_ty, local_def(it.id));
}
}
ast::item_trait(_, _, trait_methods) => {
@ -469,10 +493,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
// bodies to check.
}
provided(m) => {
let self_info = {self_ty: ty::mk_self(ccx.tcx),
def_id: local_def(it.id),
explicit_self: m.self_ty.node};
check_method(ccx, m, self_info);
check_method(ccx, m, ty::mk_self(ccx.tcx), local_def(it.id));
}
}
}
@ -1204,7 +1225,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
fcx.write_ty(expr.id, fty);
check_fn(fcx.ccx, fcx.self_info, &fn_ty, decl, body,
check_fn(fcx.ccx, none, &fn_ty, decl, body,
is_loop_body, some(fcx));
}
@ -2247,29 +2268,12 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
ty_param_bounds_and_ty {
match defn {
ast::def_arg(nid, _) => {
ast::def_arg(nid, _) | ast::def_local(nid, _) |
ast::def_self(nid) | ast::def_binding(nid, _) => {
assert (fcx.locals.contains_key(nid));
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
return no_params(typ);
}
ast::def_local(nid, _) => {
assert (fcx.locals.contains_key(nid));
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
return no_params(typ);
}
ast::def_self(_) => {
match fcx.self_info {
some(self_info) => {
let self_region = fcx.in_scope_regions.find(ty::br_self);
return no_params(method::transform_self_type_for_method(
fcx.tcx(), self_region,
self_info.self_ty, self_info.explicit_self));
}
none => {
fcx.ccx.tcx.sess.span_bug(sp, ~"def_self with no self_info");
}
}
}
ast::def_fn(id, ast::extern_fn) => {
// extern functions are just u8 pointers
return {
@ -2296,20 +2300,15 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
ast::def_class(id, _) => {
return ty::lookup_item_type(fcx.ccx.tcx, id);
}
ast::def_binding(nid, _) => {
assert (fcx.locals.contains_key(nid));
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
return no_params(typ);
}
ast::def_ty(_) | ast::def_prim_ty(_) => {
fcx.ccx.tcx.sess.span_fatal(sp, ~"expected value but found type");
}
ast::def_upvar(_, inner, _, _) => {
return ty_param_bounds_and_ty_for_def(fcx, sp, *inner);
}
ast::def_ty_param(did, n) => {
return no_params(ty::mk_param(fcx.ccx.tcx, n, did));
}
ast::def_ty(_) | ast::def_prim_ty(_) => {
fcx.ccx.tcx.sess.span_fatal(sp, ~"expected value but found type");
}
ast::def_mod(*) | ast::def_foreign_mod(*) => {
fcx.ccx.tcx.sess.span_fatal(sp, ~"expected value but found module");
}

View File

@ -150,14 +150,8 @@ struct lookup {
ty::ty_self => {
// Call is of the form "self.foo()" and appears in one
// of a trait's provided methods.
let self_def_id = match self.fcx.self_info {
some(self_info) => self_info.def_id,
none => {
// Shouldn't happen; there should always be a
// self_info in this case.
self.tcx().sess.bug(~"unexpected `none` for self_info")
}
};
let self_def_id = self.fcx.self_impl_def_id.expect(
~"unexpected `none` for self_impl_def_id");
let substs = {
self_r: none,

View File

@ -23,7 +23,7 @@ fn replace_bound_regions_in_fn_ty(
let mut all_tys = ty::tys_in_fn_ty(fn_ty);
match self_info {
some({explicit_self: ast::sty_region(m), _}) => {
some({explicit_self: {node: ast::sty_region(m), _}, _}) => {
let region = ty::re_bound(ty::br_self);
let ty = ty::mk_rptr(tcx, region,
{ ty: ty::mk_self(tcx), mutbl: m });

View File

@ -171,10 +171,16 @@ fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool {
fn resolve_type_vars_in_fn(fcx: @fn_ctxt,
decl: ast::fn_decl,
blk: ast::blk) -> bool {
blk: ast::blk,
self_info: option<self_info>) -> bool {
let wbcx = {fcx: fcx, mut success: true};
let visit = mk_visitor();
visit.visit_block(blk, wbcx, visit);
for self_info.each |self_info| {
if self_info.explicit_self.node == ast::sty_static { break; }
resolve_type_vars_for_node(wbcx, self_info.explicit_self.span,
self_info.self_id);
}
for decl.inputs.each |arg| {
resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id);
}

View File

@ -381,12 +381,10 @@ type converted_method = {mty: ty::method, id: ast::node_id, span: span};
fn convert_methods(ccx: @crate_ctxt,
ms: ~[@ast::method],
rp: bool,
rcvr_bounds: @~[ty::param_bounds],
self_ty: ty::t) -> ~[converted_method] {
rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] {
let tcx = ccx.tcx;
do vec::map(ms) |m| {
write_ty_to_tcx(tcx, m.self_id, self_ty);
let bounds = ty_param_bounds(ccx, m.tps);
let mty = ty_of_method(ccx, m, rp);
let fty = ty::mk_fn(tcx, mty.fty);
@ -423,7 +421,7 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
rp: rp,
ty: selfty});
let cms = convert_methods(ccx, ms, rp, i_bounds, selfty);
let cms = convert_methods(ccx, ms, rp, i_bounds);
for trait_ref.each |t| {
check_methods_against_trait(ccx, tps, rp, selfty, t, cms);
}
@ -436,9 +434,8 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
ensure_trait_methods(ccx, it.id, tpt.ty);
let (_, provided_methods) = split_trait_methods(trait_methods);
let selfty = ty::mk_self(tcx);
let {bounds, _} = mk_substs(ccx, tps, rp);
let _cms = convert_methods(ccx, provided_methods, rp, bounds, selfty);
let _cms = convert_methods(ccx, provided_methods, rp, bounds);
// FIXME (#2616): something like this, when we start having
// trait inheritance?
// for trait_ref.each |t| {
@ -510,7 +507,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def,
}
let {bounds, substs} = mk_substs(ccx, tps, rp);
let selfty = ty::mk_class(tcx, local_def(id), substs);
let cms = convert_methods(ccx, struct_def.methods, rp, bounds, selfty);
let cms = convert_methods(ccx, struct_def.methods, rp, bounds);
for struct_def.traits.each |trait_ref| {
check_methods_against_trait(ccx, tps, rp, selfty, trait_ref, cms);
// trait_ref.impl_id represents (class, trait) pair

View File

@ -0,0 +1,19 @@
// Test to make sure that explicit self params work inside closures
struct Box {
x: uint;
}
impl Box {
fn set_many(&mut self, xs: &[uint]) {
for xs.each |x| { self.x = x; }
}
fn set_many2(@mut self, xs: &[uint]) {
for xs.each |x| { self.x = x; }
}
fn set_many3(~mut self, xs: &[uint]) {
for xs.each |x| { self.x = x; }
}
}
fn main() {}