Skip to content

Commit 2b67d88

Browse files
committed
Rewrite the coercion code to be more readable, more sound, and to reborrow when
needed. Regarding soundness: there was a subtle bug in how it was done before; see the compile-fail test for an example. Regarding reborrowing: reborrowing allows mut and const slices/borrowed-pointers to be used with pure fns that expect immutable data. r=brson
1 parent c07ae16 commit 2b67d88

23 files changed

+643
-302
lines changed

src/librustc/middle/typeck/check/_match.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use middle::pat_util::{pat_is_variant_or_struct};
1515
use middle::ty;
1616
use middle::typeck::check::demand;
1717
use middle::typeck::check::{check_block, check_expr_has_type, fn_ctxt};
18-
use middle::typeck::check::{instantiate_path, lookup_def, lookup_local};
18+
use middle::typeck::check::{instantiate_path, lookup_def};
1919
use middle::typeck::check::{structure_of, valid_range_bounds};
2020
use middle::typeck::require_same_types;
2121

@@ -365,8 +365,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
365365
fcx.write_ty(pat.id, const_tpt.ty);
366366
}
367367
ast::pat_ident(bm, name, sub) if pat_is_binding(tcx.def_map, pat) => {
368-
let vid = lookup_local(fcx, pat.span, pat.id);
369-
let mut typ = ty::mk_var(tcx, vid);
368+
let typ = fcx.local_ty(pat.span, pat.id);
370369
371370
match bm {
372371
ast::bind_by_ref(mutbl) => {
@@ -389,8 +388,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
389388
390389
let canon_id = pcx.map.get(ast_util::path_to_ident(name));
391390
if canon_id != pat.id {
392-
let tv_id = lookup_local(fcx, pat.span, canon_id);
393-
let ct = ty::mk_var(tcx, tv_id);
391+
let ct = fcx.local_ty(pat.span, canon_id);
394392
demand::eqtype(fcx, pat.span, ct, typ);
395393
}
396394
fcx.write_ty(pat.id, typ);

src/librustc/middle/typeck/check/demand.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ fn eqtype(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t) {
5050
}
5151

5252
// Checks that the type `actual` can be coerced to `expected`.
53-
fn coerce(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) {
53+
fn coerce(fcx: @fn_ctxt,
54+
sp: span,
55+
expected: ty::t,
56+
expr: @ast::expr) {
5457
let expr_ty = fcx.expr_ty(expr);
5558
match fcx.mk_assignty(expr, expr_ty, expected) {
5659
result::Ok(()) => { /* ok */ }

src/librustc/middle/typeck/check/method.rs

+74-2
Original file line numberDiff line numberDiff line change
@@ -700,20 +700,91 @@ impl LookupContext {
700700
autoderefs: uint)
701701
-> Option<method_map_entry>
702702
{
703+
let (self_ty, autoadjust) =
704+
self.consider_reborrow(self_ty, autoderefs);
703705
match self.search_for_method(self_ty) {
704706
None => None,
705707
Some(move mme) => {
706708
debug!("(searching for autoderef'd method) writing \
707709
adjustment (%u) to %d",
708710
autoderefs,
709711
self.self_expr.id);
710-
self.fcx.write_autoderef_adjustment(
711-
self.self_expr.id, autoderefs);
712+
self.fcx.write_adjustment(self.self_expr.id, @autoadjust);
712713
Some(mme)
713714
}
714715
}
715716
}
716717

718+
fn consider_reborrow(&self,
719+
self_ty: ty::t,
720+
autoderefs: uint) -> (ty::t, ty::AutoAdjustment)
721+
{
722+
/*!
723+
*
724+
* In the event that we are invoking a method with a receiver
725+
* of a linear borrowed type like `&mut T` or `&[mut T]`,
726+
* we will "reborrow" the receiver implicitly. For example, if
727+
* you have a call `r.inc()` and where `r` has type `&mut T`,
728+
* then we treat that like `(&mut *r).inc()`. This avoids
729+
* consuming the original pointer.
730+
*
731+
* You might think that this would be a natural byproduct of
732+
* the auto-deref/auto-ref process. This is true for `@mut T`
733+
* but not for an `&mut T` receiver. With `@mut T`, we would
734+
* begin by testing for methods with a self type `@mut T`,
735+
* then autoderef to `T`, then autoref to `&mut T`. But with
736+
* an `&mut T` receiver the process begins with `&mut T`, only
737+
* without any autoadjustments.
738+
*/
739+
740+
let tcx = self.tcx();
741+
return match ty::get(self_ty).sty {
742+
ty::ty_rptr(self_r, self_mt) if self_mt.mutbl == m_mutbl => {
743+
let region = fresh_region(self, self_r);
744+
(ty::mk_rptr(tcx, region, self_mt),
745+
ty::AutoAdjustment {
746+
autoderefs: autoderefs+1,
747+
autoref: Some(ty::AutoRef {kind: AutoPtr,
748+
region: region,
749+
mutbl: self_mt.mutbl})})
750+
}
751+
ty::ty_evec(self_mt, vstore_slice(self_r))
752+
if self_mt.mutbl == m_mutbl => {
753+
let region = fresh_region(self, self_r);
754+
(ty::mk_evec(tcx, self_mt, vstore_slice(region)),
755+
ty::AutoAdjustment {
756+
autoderefs: autoderefs,
757+
autoref: Some(ty::AutoRef {kind: AutoBorrowVec,
758+
region: region,
759+
mutbl: self_mt.mutbl})})
760+
}
761+
_ => {
762+
(self_ty, ty::AutoAdjustment {autoderefs: autoderefs,
763+
autoref: None})
764+
}
765+
};
766+
767+
fn fresh_region(self: &LookupContext,
768+
self_r: ty::Region) -> ty::Region {
769+
let region = self.infcx().next_region_var(self.expr.span,
770+
self.expr.id);
771+
772+
// FIXME(#3148)---in principle this dependency should
773+
// be done more generally as part of regionck
774+
match infer::mk_subr(self.infcx(), true, self.expr.span,
775+
region, self_r) {
776+
Ok(_) => {}
777+
Err(e) => {
778+
self.tcx().sess.span_bug(
779+
self.expr.span,
780+
fmt!("Failed with error: %?", e));
781+
}
782+
}
783+
784+
return region;
785+
}
786+
}
787+
717788
fn search_for_autosliced_method(
718789
&self,
719790
self_ty: ty::t,
@@ -729,6 +800,7 @@ impl LookupContext {
729800
match ty::get(self_ty).sty {
730801
ty_evec(mt, vstore_box) |
731802
ty_evec(mt, vstore_uniq) |
803+
ty_evec(mt, vstore_slice(_)) | // NDM(#3148)
732804
ty_evec(mt, vstore_fixed(_)) => {
733805
// First try to borrow to a slice
734806
let entry = self.search_for_some_kind_of_autorefd_method(

src/librustc/middle/typeck/check/mod.rs

+41-43
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ export regionck;
139139
export demand;
140140
export method;
141141
export fn_ctxt;
142-
export lookup_local;
143142
export impl_self_ty;
144143
export DerefArgs;
145144
export DontDerefArgs;
@@ -189,7 +188,7 @@ type self_info = {
189188
/// share the inherited fields.
190189
struct inherited {
191190
infcx: @infer::InferCtxt,
192-
locals: HashMap<ast::node_id, TyVid>,
191+
locals: HashMap<ast::node_id, ty::t>,
193192
node_types: HashMap<ast::node_id, ty::t>,
194193
node_type_substs: HashMap<ast::node_id, ty::substs>,
195194
adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
@@ -376,8 +375,7 @@ fn check_fn(ccx: @crate_ctxt,
376375
}
377376
};
378377

379-
// XXX: Bad copy.
380-
gather_locals(fcx, decl, body, copy arg_tys, self_info);
378+
gather_locals(fcx, decl, body, arg_tys, self_info);
381379
check_block(fcx, body);
382380

383381
// We unify the tail expr's type with the
@@ -414,38 +412,39 @@ fn check_fn(ccx: @crate_ctxt,
414412
fn gather_locals(fcx: @fn_ctxt,
415413
decl: &ast::fn_decl,
416414
body: ast::blk,
417-
arg_tys: ~[ty::t],
415+
arg_tys: &[ty::t],
418416
self_info: Option<self_info>) {
419417
let tcx = fcx.ccx.tcx;
420418

421-
let assign = fn@(span: span, nid: ast::node_id,
422-
ty_opt: Option<ty::t>) {
423-
let var_id = fcx.infcx().next_ty_var_id();
424-
fcx.inh.locals.insert(nid, var_id);
419+
let assign = fn@(nid: ast::node_id, ty_opt: Option<ty::t>) {
425420
match ty_opt {
426-
None => {/* nothing to do */ }
421+
None => {
422+
// infer the variable's type
423+
let var_id = fcx.infcx().next_ty_var_id();
424+
let var_ty = ty::mk_var(fcx.tcx(), var_id);
425+
fcx.inh.locals.insert(nid, var_ty);
426+
}
427427
Some(typ) => {
428-
infer::mk_eqty(fcx.infcx(), false, span,
429-
ty::mk_var(tcx, var_id), typ);
428+
// take type that the user specified
429+
fcx.inh.locals.insert(nid, typ);
430430
}
431431
}
432432
};
433433

434434
// Add the self parameter
435435
for self_info.each |self_info| {
436-
assign(self_info.explicit_self.span,
437-
self_info.self_id,
438-
Some(self_info.self_ty));
436+
assign(self_info.self_id, Some(self_info.self_ty));
439437
debug!("self is assigned to %s",
440-
fcx.inh.locals.get(self_info.self_id).to_str());
438+
fcx.infcx().ty_to_str(
439+
fcx.inh.locals.get(self_info.self_id)));
441440
}
442441

443442
// Add formal parameters.
444443
for vec::each2(arg_tys, decl.inputs) |arg_ty, input| {
445444
// Create type variables for each argument.
446445
do pat_util::pat_bindings(tcx.def_map, input.pat)
447446
|_bm, pat_id, _sp, _path| {
448-
assign(input.ty.span, pat_id, None);
447+
assign(pat_id, None);
449448
}
450449

451450
// Check the pattern.
@@ -466,10 +465,11 @@ fn check_fn(ccx: @crate_ctxt,
466465
ast::ty_infer => None,
467466
_ => Some(fcx.to_ty(local.node.ty))
468467
};
469-
assign(local.span, local.node.id, o_ty);
470-
debug!("Local variable %s is assigned to %s",
468+
assign(local.node.id, o_ty);
469+
debug!("Local variable %s is assigned type %s",
471470
fcx.pat_to_str(local.node.pat),
472-
fcx.inh.locals.get(local.node.id).to_str());
471+
fcx.infcx().ty_to_str(
472+
fcx.inh.locals.get(local.node.id)));
473473
visit::visit_local(local, e, v);
474474
};
475475

@@ -478,10 +478,11 @@ fn check_fn(ccx: @crate_ctxt,
478478
match p.node {
479479
ast::pat_ident(_, path, _)
480480
if pat_util::pat_is_binding(fcx.ccx.tcx.def_map, p) => {
481-
assign(p.span, p.id, None);
481+
assign(p.id, None);
482482
debug!("Pattern binding %s is assigned to %s",
483483
tcx.sess.str_of(path.idents[0]),
484-
fcx.inh.locals.get(p.id).to_str());
484+
fcx.infcx().ty_to_str(
485+
fcx.inh.locals.get(p.id)));
485486
}
486487
_ => {}
487488
}
@@ -694,6 +695,17 @@ impl @fn_ctxt: region_scope {
694695
impl @fn_ctxt {
695696
fn tag() -> ~str { fmt!("%x", ptr::addr_of(&(*self)) as uint) }
696697

698+
fn local_ty(span: span, nid: ast::node_id) -> ty::t {
699+
match self.inh.locals.find(nid) {
700+
Some(t) => t,
701+
None => {
702+
self.tcx().sess.span_bug(
703+
span,
704+
fmt!("No type for local variable %?", nid));
705+
}
706+
}
707+
}
708+
697709
fn expr_to_str(expr: @ast::expr) -> ~str {
698710
fmt!("expr(%?:%s)", expr.id,
699711
pprust::expr_to_str(expr, self.tcx().sess.intr()))
@@ -1359,10 +1371,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
13591371
fn check_for(fcx: @fn_ctxt, local: @ast::local,
13601372
element_ty: ty::t, body: ast::blk,
13611373
node_id: ast::node_id) -> bool {
1362-
let locid = lookup_local(fcx, local.span, local.node.id);
1363-
demand::suptype(fcx, local.span,
1364-
ty::mk_var(fcx.ccx.tcx, locid),
1365-
element_ty);
1374+
let local_ty = fcx.local_ty(local.span, local.node.id);
1375+
demand::suptype(fcx, local.span, local_ty, element_ty);
13661376
let bot = check_decl_local(fcx, local);
13671377
check_block_no_value(fcx, body);
13681378
fcx.write_nil(node_id);
@@ -2551,15 +2561,15 @@ fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) {
25512561

25522562
fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id,
25532563
init: @ast::expr) -> bool {
2554-
let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.span, nid));
2555-
return check_expr_coercable_to_type(fcx, init, lty);
2564+
let local_ty = fcx.local_ty(init.span, nid);
2565+
return check_expr_coercable_to_type(fcx, init, local_ty);
25562566
}
25572567

25582568
fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
25592569
let mut bot = false;
25602570
let tcx = fcx.ccx.tcx;
25612571

2562-
let t = ty::mk_var(tcx, fcx.inh.locals.get(local.node.id));
2572+
let t = fcx.local_ty(local.span, local.node.id);
25632573
fcx.write_ty(local.node.id, t);
25642574

25652575
match local.node.init {
@@ -2819,17 +2829,6 @@ fn check_enum_variants(ccx: @crate_ctxt,
28192829
check_instantiable(ccx.tcx, sp, id);
28202830
}
28212831

2822-
pub fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid {
2823-
match fcx.inh.locals.find(id) {
2824-
Some(x) => x,
2825-
_ => {
2826-
fcx.ccx.tcx.sess.span_fatal(
2827-
sp,
2828-
~"internal error looking up a local var")
2829-
}
2830-
}
2831-
}
2832-
28332832
fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def {
28342833
lookup_def_ccx(fcx.ccx, sp, id)
28352834
}
@@ -2841,9 +2840,8 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
28412840
match defn {
28422841
ast::def_arg(nid, _, _) | ast::def_local(nid, _) |
28432842
ast::def_self(nid, _) | ast::def_binding(nid, _) => {
2844-
assert (fcx.inh.locals.contains_key(nid));
2845-
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
2846-
return no_params(typ);
2843+
let typ = fcx.local_ty(sp, nid);
2844+
return no_params(typ);
28472845
}
28482846
ast::def_fn(_, ast::extern_fn) => {
28492847
// extern functions are just u8 pointers

src/librustc/middle/typeck/check/writeback.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use core::prelude::*;
1616

1717
use middle::pat_util;
1818
use middle::ty;
19-
use middle::typeck::check::{fn_ctxt, lookup_local, self_info};
19+
use middle::typeck::check::{fn_ctxt, self_info};
2020
use middle::typeck::infer::{force_all, resolve_all, resolve_region};
2121
use middle::typeck::infer::{resolve_type};
2222
use middle::typeck::infer;
@@ -216,8 +216,7 @@ fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) {
216216
}
217217
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
218218
if !wbcx.success { return; }
219-
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
220-
let var_ty = ty::mk_var(wbcx.fcx.tcx(), var_id);
219+
let var_ty = wbcx.fcx.local_ty(l.span, l.node.id);
221220
match resolve_type(wbcx.fcx.infcx(), var_ty, resolve_all | force_all) {
222221
Ok(lty) => {
223222
debug!("Type for local %s (id %d) resolved to %s",

0 commit comments

Comments
 (0)