librustc: Don't overwrite vtables when coercing to trait object.

This commit is contained in:
Luqman Aden 2014-06-11 18:01:48 -04:00
parent 5c81a186e9
commit 4eb5d7baf9
13 changed files with 164 additions and 61 deletions

View File

@ -552,16 +552,17 @@ impl tr for freevar_entry {
// Encoding and decoding of MethodCallee
trait read_method_callee_helper {
fn read_method_callee(&mut self, xcx: &ExtendedDecodeContext) -> (u32, MethodCallee);
fn read_method_callee(&mut self, xcx: &ExtendedDecodeContext)
-> (typeck::ExprAdjustment, MethodCallee);
}
fn encode_method_callee(ecx: &e::EncodeContext,
ebml_w: &mut Encoder,
autoderef: u32,
adjustment: typeck::ExprAdjustment,
method: &MethodCallee) {
ebml_w.emit_struct("MethodCallee", 4, |ebml_w| {
ebml_w.emit_struct_field("autoderef", 0u, |ebml_w| {
autoderef.encode(ebml_w)
ebml_w.emit_struct_field("adjustment", 0u, |ebml_w| {
adjustment.encode(ebml_w)
});
ebml_w.emit_struct_field("origin", 1u, |ebml_w| {
method.origin.encode(ebml_w)
@ -576,12 +577,14 @@ fn encode_method_callee(ecx: &e::EncodeContext,
}
impl<'a> read_method_callee_helper for reader::Decoder<'a> {
fn read_method_callee(&mut self, xcx: &ExtendedDecodeContext) -> (u32, MethodCallee) {
fn read_method_callee(&mut self, xcx: &ExtendedDecodeContext)
-> (typeck::ExprAdjustment, MethodCallee) {
self.read_struct("MethodCallee", 4, |this| {
let autoderef = this.read_struct_field("autoderef", 0, |this| {
let adjustment = this.read_struct_field("adjustment", 0, |this| {
Decodable::decode(this)
}).unwrap();
Ok((autoderef, MethodCallee {
Ok((adjustment, MethodCallee {
origin: this.read_struct_field("origin", 1, |this| {
let method_origin: MethodOrigin =
Decodable::decode(this).unwrap();
@ -627,11 +630,11 @@ impl tr for MethodOrigin {
fn encode_vtable_res_with_key(ecx: &e::EncodeContext,
ebml_w: &mut Encoder,
autoderef: u32,
adjustment: typeck::ExprAdjustment,
dr: &typeck::vtable_res) {
ebml_w.emit_struct("VtableWithKey", 2, |ebml_w| {
ebml_w.emit_struct_field("autoderef", 0u, |ebml_w| {
autoderef.encode(ebml_w)
ebml_w.emit_struct_field("adjustment", 0u, |ebml_w| {
adjustment.encode(ebml_w)
});
ebml_w.emit_struct_field("vtable_res", 1u, |ebml_w| {
Ok(encode_vtable_res(ecx, ebml_w, dr))
@ -705,7 +708,7 @@ pub trait vtable_decoder_helpers {
fn read_vtable_res_with_key(&mut self,
tcx: &ty::ctxt,
cdata: &cstore::crate_metadata)
-> (u32, typeck::vtable_res);
-> (typeck::ExprAdjustment, typeck::vtable_res);
fn read_vtable_res(&mut self,
tcx: &ty::ctxt, cdata: &cstore::crate_metadata)
-> typeck::vtable_res;
@ -731,12 +734,12 @@ impl<'a> vtable_decoder_helpers for reader::Decoder<'a> {
fn read_vtable_res_with_key(&mut self,
tcx: &ty::ctxt,
cdata: &cstore::crate_metadata)
-> (u32, typeck::vtable_res) {
-> (typeck::ExprAdjustment, typeck::vtable_res) {
self.read_struct("VtableWithKey", 2, |this| {
let autoderef = this.read_struct_field("autoderef", 0, |this| {
let adjustment = this.read_struct_field("adjustment", 0, |this| {
Decodable::decode(this)
}).unwrap();
Ok((autoderef, this.read_struct_field("vtable_res", 1, |this| {
Ok((adjustment, this.read_struct_field("vtable_res", 1, |this| {
Ok(this.read_vtable_res(tcx, cdata))
}).unwrap()))
}).unwrap()
@ -1050,7 +1053,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
ebml_w.tag(c::tag_table_method_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_method_callee(ecx, ebml_w, method_call.autoderef, method)
encode_method_callee(ecx, ebml_w, method_call.adjustment, method)
})
})
}
@ -1059,7 +1062,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
ebml_w.tag(c::tag_table_vtable_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_vtable_res_with_key(ecx, ebml_w, method_call.autoderef, dr);
encode_vtable_res_with_key(ecx, ebml_w, method_call.adjustment, dr);
})
})
}
@ -1068,12 +1071,13 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
match *adj {
ty::AutoDerefRef(adj) => {
for autoderef in range(0, adj.autoderefs) {
let method_call = MethodCall::autoderef(id, autoderef as u32);
let method_call = MethodCall::autoderef(id, autoderef);
for &method in tcx.method_map.borrow().find(&method_call).iter() {
ebml_w.tag(c::tag_table_method_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_method_callee(ecx, ebml_w, method_call.autoderef, method)
encode_method_callee(ecx, ebml_w,
method_call.adjustment, method)
})
})
}
@ -1083,12 +1087,32 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_vtable_res_with_key(ecx, ebml_w,
method_call.autoderef, dr);
method_call.adjustment, dr);
})
})
}
}
}
ty::AutoObject(..) => {
let method_call = MethodCall::autoobject(id);
for &method in tcx.method_map.borrow().find(&method_call).iter() {
ebml_w.tag(c::tag_table_method_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_method_callee(ecx, ebml_w, method_call.adjustment, method)
})
})
}
for &dr in tcx.vtable_map.borrow().find(&method_call).iter() {
ebml_w.tag(c::tag_table_vtable_map, |ebml_w| {
ebml_w.id(id);
ebml_w.tag(c::tag_table_val, |ebml_w| {
encode_vtable_res_with_key(ecx, ebml_w, method_call.adjustment, dr);
})
})
}
}
_ => {}
}
@ -1393,20 +1417,20 @@ fn decode_side_tables(xcx: &ExtendedDecodeContext,
dcx.tcx.ty_param_defs.borrow_mut().insert(id, bounds);
}
c::tag_table_method_map => {
let (autoderef, method) = val_dsr.read_method_callee(xcx);
let (adjustment, method) = val_dsr.read_method_callee(xcx);
let method_call = MethodCall {
expr_id: id,
autoderef: autoderef
adjustment: adjustment
};
dcx.tcx.method_map.borrow_mut().insert(method_call, method);
}
c::tag_table_vtable_map => {
let (autoderef, vtable_res) =
let (adjustment, vtable_res) =
val_dsr.read_vtable_res_with_key(xcx.dcx.tcx,
xcx.dcx.cdata);
let vtable_key = MethodCall {
expr_id: id,
autoderef: autoderef
adjustment: adjustment
};
dcx.tcx.vtable_map.borrow_mut().insert(vtable_key, vtable_res);
}

View File

@ -611,7 +611,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
for i in range(0, autoderefs) {
let deref_id = typeck::MethodCall::autoderef(expr.id, i as u32);
let deref_id = typeck::MethodCall::autoderef(expr.id, i);
match self.typer.node_method_ty(deref_id) {
None => {}
Some(method_ty) => {

View File

@ -697,9 +697,14 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
base_cmt: cmt,
deref_cnt: uint)
-> cmt {
let adjustment = match self.typer.adjustments().borrow().find(&node.id()) {
Some(&ty::AutoObject(..)) => typeck::AutoObject,
_ if deref_cnt != 0 => typeck::AutoDeref(deref_cnt),
_ => typeck::NoAdjustment
};
let method_call = typeck::MethodCall {
expr_id: node.id(),
autoderef: deref_cnt as u32
adjustment: adjustment
};
let method_ty = self.typer.node_method_ty(method_call);

View File

@ -65,8 +65,8 @@ use middle::ty::struct_fields;
use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe};
use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef};
use middle::ty;
use middle::typeck::MethodCall;
use middle::typeck;
use middle::typeck::MethodCall;
use util::common::indenter;
use util::ppaux::Repr;
use util::nodemap::NodeMap;
@ -1178,7 +1178,7 @@ fn trans_unary<'a>(bcx: &'a Block<'a>,
}
ast::UnDeref => {
let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
deref_once(bcx, expr, datum, 0)
deref_once(bcx, expr, datum, method_call)
}
}
}
@ -1487,7 +1487,7 @@ fn trans_overloaded_call<'a>(
SaveIn(addr))
}));
let method_call = typeck::MethodCall::expr(expr.id);
let method_call = MethodCall::expr(expr.id);
let method_type = bcx.tcx()
.method_map
.borrow()
@ -1737,8 +1737,9 @@ fn deref_multiple<'a>(bcx: &'a Block<'a>,
-> DatumBlock<'a, Expr> {
let mut bcx = bcx;
let mut datum = datum;
for i in range(1, times+1) {
datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, i));
for i in range(0, times) {
let method_call = MethodCall::autoderef(expr.id, i);
datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, method_call));
}
DatumBlock { bcx: bcx, datum: datum }
}
@ -1746,22 +1747,18 @@ fn deref_multiple<'a>(bcx: &'a Block<'a>,
fn deref_once<'a>(bcx: &'a Block<'a>,
expr: &ast::Expr,
datum: Datum<Expr>,
derefs: uint)
method_call: MethodCall)
-> DatumBlock<'a, Expr> {
let ccx = bcx.ccx();
debug!("deref_once(expr={}, datum={}, derefs={})",
debug!("deref_once(expr={}, datum={}, method_call={})",
expr.repr(bcx.tcx()),
datum.to_str(ccx),
derefs);
method_call);
let mut bcx = bcx;
// Check for overloaded deref.
let method_call = MethodCall {
expr_id: expr.id,
autoderef: derefs as u32
};
let method_ty = ccx.tcx.method_map.borrow()
.find(&method_call).map(|method| method.ty);
let datum = match method_ty {
@ -1771,11 +1768,10 @@ fn deref_once<'a>(bcx: &'a Block<'a>,
// converts from the `Shaht<T>` pointer that we have into
// a `&T` pointer. We can then proceed down the normal
// path (below) to dereference that `&T`.
let datum = if derefs == 0 {
datum
} else {
// Always perform an AutoPtr when applying an overloaded auto-deref.
unpack_datum!(bcx, auto_ref(bcx, datum, expr))
let datum = match method_call.adjustment {
// Always perform an AutoPtr when applying an overloaded auto-deref
typeck::AutoDeref(_) => unpack_datum!(bcx, auto_ref(bcx, datum, expr)),
_ => datum
};
let val = unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call,
datum, None, None));
@ -1834,8 +1830,8 @@ fn deref_once<'a>(bcx: &'a Block<'a>,
}
};
debug!("deref_once(expr={}, derefs={}, result={})",
expr.id, derefs, r.datum.to_str(ccx));
debug!("deref_once(expr={}, method_call={}, result={})",
expr.id, method_call, r.datum.to_str(ccx));
return r;

View File

@ -538,9 +538,13 @@ pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>,
// Store the vtable into the second half of pair.
let origins = {
let vtable_map = ccx.tcx.vtable_map.borrow();
resolve_param_vtables_under_param_substs(ccx.tcx(),
bcx.fcx.param_substs,
vtable_map.get(&MethodCall::expr(id)).get_self().unwrap())
// This trait cast might be because of implicit coercion
let method_call = match ccx.tcx.adjustments.borrow().find(&id) {
Some(&ty::AutoObject(..)) => MethodCall::autoobject(id),
_ => MethodCall::expr(id)
};
let vres = vtable_map.get(&method_call).get_self().unwrap();
resolve_param_vtables_under_param_substs(ccx.tcx(), bcx.fcx.param_substs, vres)
};
let vtable = get_vtable(bcx, v_ty, origins);
let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]);

View File

@ -2823,7 +2823,7 @@ pub fn adjust_ty(cx: &ctxt,
if !ty::type_is_error(adjusted_ty) {
for i in range(0, adj.autoderefs) {
let method_call = typeck::MethodCall::autoderef(expr_id, i as u32);
let method_call = typeck::MethodCall::autoderef(expr_id, i);
match method_type(method_call) {
Some(method_ty) => {
adjusted_ty = ty_fn_ret(method_ty);

View File

@ -1331,8 +1331,7 @@ pub fn autoderef<T>(fcx: &FnCtxt, sp: Span, base_ty: ty::t,
let mt = match ty::deref(resolved_t, false) {
Some(mt) => Some(mt),
None => {
let method_call =
expr_id.map(|id| MethodCall::autoderef(id, autoderefs as u32));
let method_call = expr_id.map(|id| MethodCall::autoderef(id, autoderefs));
try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref)
}
};

View File

@ -879,7 +879,7 @@ fn constrain_autoderefs(rcx: &mut Rcx,
rcx.fcx.infcx().ty_to_str(derefd_ty),
i, derefs);
let method_call = MethodCall::autoderef(deref_expr.id, i as u32);
let method_call = MethodCall::autoderef(deref_expr.id, i);
derefd_ty = match rcx.fcx.inh.method_map.borrow().find(&method_call) {
Some(method) => {
// Treat overloaded autoderefs as if an AutoRef adjustment

View File

@ -529,7 +529,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
let _indent = indenter();
let cx = fcx.ccx;
let resolve_object_cast = |src: &ast::Expr, target_ty: ty::t| {
let resolve_object_cast = |src: &ast::Expr, target_ty: ty::t, key: MethodCall| {
// Look up vtables for the type we're casting to,
// passing in the source and target type. The source
// must be a pointer type suitable to the object sigil,
@ -596,7 +596,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
if !is_early {
let mut r = VecPerParamSpace::empty();
r.push(subst::SelfSpace, vtables);
insert_vtables(fcx, MethodCall::expr(ex.id), r);
insert_vtables(fcx, key, r);
}
// Now, if this is &trait, we need to link the
@ -694,7 +694,8 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
ast::ExprCast(ref src, _) => {
debug!("vtable resolution on expr {}", ex.repr(fcx.tcx()));
let target_ty = fcx.expr_ty(ex);
resolve_object_cast(&**src, target_ty);
let key = MethodCall::expr(ex.id);
resolve_object_cast(&**src, target_ty, key);
}
_ => ()
}
@ -705,7 +706,7 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
match *adjustment {
AutoDerefRef(adj) => {
for autoderef in range(0, adj.autoderefs) {
let method_call = MethodCall::autoderef(ex.id, autoderef as u32);
let method_call = MethodCall::autoderef(ex.id, autoderef);
match fcx.inh.method_map.borrow().find(&method_call) {
Some(method) => {
debug!("vtable resolution on parameter bounds for autoderef {}",
@ -745,7 +746,8 @@ pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) {
}
};
resolve_object_cast(ex, object_ty);
let key = MethodCall::autoobject(ex.id);
resolve_object_cast(ex, object_ty, key);
}
AutoAddEnv(..) => {}
}

View File

@ -248,7 +248,7 @@ impl<'cx> WritebackCx<'cx> {
ty::AutoDerefRef(adj) => {
for autoderef in range(0, adj.autoderefs) {
let method_call = MethodCall::autoderef(id, autoderef as u32);
let method_call = MethodCall::autoderef(id, autoderef);
self.visit_method_map_entry(reason, method_call);
self.visit_vtable_map_entry(reason, method_call);
}
@ -260,6 +260,10 @@ impl<'cx> WritebackCx<'cx> {
}
ty::AutoObject(trait_store, bb, def_id, substs) => {
let method_call = MethodCall::autoobject(id);
self.visit_method_map_entry(reason, method_call);
self.visit_vtable_map_entry(reason, method_call);
ty::AutoObject(
self.resolve(&trait_store, reason),
self.resolve(&bb, reason),

View File

@ -149,24 +149,52 @@ pub struct MethodCallee {
pub substs: subst::Substs
}
/**
* With method calls, we store some extra information in
* side tables (i.e method_map, vtable_map). We use
* MethodCall as a key to index into these tables instead of
* just directly using the expression's NodeId. The reason
* for this being that we may apply adjustments (coercions)
* with the resulting expression also needing to use the
* side tables. The problem with this is that we don't
* assign a separate NodeId to this new expression
* and so it would clash with the base expression if both
* needed to add to the side tables. Thus to disambiguate
* we also keep track of whether there's an adjustment in
* our key.
*/
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub struct MethodCall {
pub expr_id: ast::NodeId,
pub autoderef: u32
pub adjustment: ExprAdjustment
}
#[deriving(Clone, PartialEq, Eq, Hash, Show, Encodable, Decodable)]
pub enum ExprAdjustment {
NoAdjustment,
AutoDeref(uint),
AutoObject
}
impl MethodCall {
pub fn expr(id: ast::NodeId) -> MethodCall {
MethodCall {
expr_id: id,
autoderef: 0
adjustment: NoAdjustment
}
}
pub fn autoderef(expr_id: ast::NodeId, autoderef: u32) -> MethodCall {
pub fn autoobject(id: ast::NodeId) -> MethodCall {
MethodCall {
expr_id: id,
adjustment: AutoObject
}
}
pub fn autoderef(expr_id: ast::NodeId, autoderef: uint) -> MethodCall {
MethodCall {
expr_id: expr_id,
autoderef: 1 + autoderef
adjustment: AutoDeref(1 + autoderef)
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rc::Rc;
fn main() {
let x = Rc::new([1, 2, 3, 4]);
assert!(*x == [1, 2, 3, 4]);
}

View File

@ -0,0 +1,25 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// #14399
// We'd previously ICE if we had a method call whose return
// value was coerced to a trait object. (v.clone() returns Box<B1>
// which is coerced to Box<A>).
#[deriving(Clone)]
struct B1;
trait A {}
impl A for B1 {}
fn main() {
let v = box B1;
let _c: Box<A> = v.clone();
}