Introduce auto adjustment table to subsume autoderef/autoref/borrowings.
Fixes #3261 Fixes #3443
This commit is contained in:
parent
02b41097e4
commit
8a8f200d10
@ -1177,7 +1177,7 @@ type SharedChan<T: Send> = unsafe::Exclusive<Chan<T>>;
|
||||
impl<T: Send> SharedChan<T>: Channel<T> {
|
||||
fn send(+x: T) {
|
||||
let mut xx = Some(move x);
|
||||
do self.with |chan| {
|
||||
do self.with_imm |chan| {
|
||||
let mut x = None;
|
||||
x <-> xx;
|
||||
chan.send(option::unwrap(move x))
|
||||
@ -1186,7 +1186,7 @@ impl<T: Send> SharedChan<T>: Channel<T> {
|
||||
|
||||
fn try_send(+x: T) -> bool {
|
||||
let mut xx = Some(move x);
|
||||
do self.with |chan| {
|
||||
do self.with_imm |chan| {
|
||||
let mut x = None;
|
||||
x <-> xx;
|
||||
chan.try_send(option::unwrap(move x))
|
||||
|
@ -354,6 +354,13 @@ impl<T: Send> Exclusive<T> {
|
||||
move result
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn with_imm<U>(f: fn(x: &T) -> U) -> U {
|
||||
do self.with |x| {
|
||||
f(unsafe::transmute_immut(x))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#2585) make this a by-move method on the exclusive
|
||||
|
@ -314,6 +314,8 @@ enum EbmlSerializerTag {
|
||||
EsEnum, EsEnumVid, EsEnumBody,
|
||||
EsVec, EsVecLen, EsVecElt,
|
||||
|
||||
EsOpaque,
|
||||
|
||||
EsLabel // Used only when debugging
|
||||
}
|
||||
|
||||
@ -340,6 +342,14 @@ impl ebml::Writer: SerializerPriv {
|
||||
}
|
||||
}
|
||||
|
||||
impl ebml::Writer {
|
||||
fn emit_opaque(f: fn()) {
|
||||
do self.wr_tag(EsOpaque as uint) {
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ebml::Writer: serialization::Serializer {
|
||||
fn emit_nil() {}
|
||||
|
||||
@ -397,7 +407,7 @@ impl ebml::Writer: serialization::Serializer {
|
||||
}
|
||||
|
||||
type EbmlDeserializer_ = {mut parent: ebml::Doc,
|
||||
mut pos: uint};
|
||||
mut pos: uint};
|
||||
|
||||
enum EbmlDeserializer {
|
||||
EbmlDeserializer_(EbmlDeserializer_)
|
||||
@ -462,6 +472,14 @@ priv impl EbmlDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
impl EbmlDeserializer {
|
||||
fn read_opaque<R>(op: fn(ebml::Doc) -> R) -> R {
|
||||
do self.push_doc(self.next_doc(EsOpaque)) {
|
||||
op(copy self.parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EbmlDeserializer: serialization::Deserializer {
|
||||
fn read_nil() -> () { () }
|
||||
|
||||
|
@ -794,7 +794,7 @@ impl TcpSocketBuf: io::Reader {
|
||||
count
|
||||
}
|
||||
fn read_byte() -> int {
|
||||
let bytes = ~[0];
|
||||
let mut bytes = ~[0];
|
||||
if self.read(bytes, 1u) == 0 { fail } else { bytes[0] as int }
|
||||
}
|
||||
fn unread_byte(amt: int) {
|
||||
|
@ -775,7 +775,7 @@ mod tests {
|
||||
let (c,p) = pipes::stream();
|
||||
let m = ~Mutex();
|
||||
let m2 = ~m.clone();
|
||||
let sharedstate = ~0;
|
||||
let mut sharedstate = ~0;
|
||||
let ptr = ptr::addr_of(*sharedstate);
|
||||
do task::spawn {
|
||||
let sharedstate: &mut int =
|
||||
@ -1047,7 +1047,7 @@ mod tests {
|
||||
// mutex mutual exclusion test, a ways above.
|
||||
let (c,p) = pipes::stream();
|
||||
let x2 = ~x.clone();
|
||||
let sharedstate = ~0;
|
||||
let mut sharedstate = ~0;
|
||||
let ptr = ptr::addr_of(*sharedstate);
|
||||
do task::spawn {
|
||||
let sharedstate: &mut int =
|
||||
|
@ -421,6 +421,15 @@ enum vstore {
|
||||
vstore_slice(@region) // &[1,2,3,4](foo)?
|
||||
}
|
||||
|
||||
#[auto_serialize]
|
||||
enum expr_vstore {
|
||||
// FIXME (#2112): Change uint to @expr (actually only constant exprs)
|
||||
expr_vstore_fixed(Option<uint>), // [1,2,3,4]/_ or 4
|
||||
expr_vstore_uniq, // ~[1,2,3,4]
|
||||
expr_vstore_box, // @[1,2,3,4]
|
||||
expr_vstore_slice // &[1,2,3,4]
|
||||
}
|
||||
|
||||
pure fn is_blockish(p: ast::proto) -> bool {
|
||||
match p {
|
||||
proto_block => true,
|
||||
@ -662,7 +671,7 @@ enum alt_mode { alt_check, alt_exhaustive, }
|
||||
|
||||
#[auto_serialize]
|
||||
enum expr_ {
|
||||
expr_vstore(@expr, vstore),
|
||||
expr_vstore(@expr, expr_vstore),
|
||||
expr_vec(~[@expr], mutability),
|
||||
expr_rec(~[field], Option<@expr>),
|
||||
expr_call(@expr, ~[@expr], bool), // True iff last argument is a block
|
||||
|
@ -260,7 +260,7 @@ impl ext_ctxt: ext_ctxt_helpers {
|
||||
ast::expr_lit(
|
||||
@{node: ast::lit_str(s),
|
||||
span: span})),
|
||||
ast::vstore_uniq))
|
||||
ast::expr_vstore_uniq))
|
||||
}
|
||||
|
||||
fn lit_uint(span: span, i: uint) -> @ast::expr {
|
||||
|
@ -65,24 +65,26 @@ fn mk_base_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
|
||||
let vecexpr = ast::expr_vec(exprs, ast::m_imm);
|
||||
mk_expr(cx, sp, vecexpr)
|
||||
}
|
||||
fn mk_vstore_e(cx: ext_ctxt, sp: span, expr: @ast::expr, vst: ast::vstore) ->
|
||||
fn mk_vstore_e(cx: ext_ctxt, sp: span, expr: @ast::expr,
|
||||
vst: ast::expr_vstore) ->
|
||||
@ast::expr {
|
||||
mk_expr(cx, sp, ast::expr_vstore(expr, vst))
|
||||
}
|
||||
fn mk_uniq_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
|
||||
@ast::expr {
|
||||
mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::vstore_uniq)
|
||||
mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::expr_vstore_uniq)
|
||||
}
|
||||
fn mk_fixed_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
|
||||
@ast::expr {
|
||||
mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::vstore_fixed(None))
|
||||
mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs),
|
||||
ast::expr_vstore_fixed(None))
|
||||
}
|
||||
fn mk_base_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr {
|
||||
let lit = ast::lit_str(@s);
|
||||
return mk_lit(cx, sp, lit);
|
||||
}
|
||||
fn mk_uniq_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr {
|
||||
mk_vstore_e(cx, sp, mk_base_str(cx, sp, s), ast::vstore_uniq)
|
||||
mk_vstore_e(cx, sp, mk_base_str(cx, sp, s), ast::expr_vstore_uniq)
|
||||
}
|
||||
|
||||
fn mk_rec_e(cx: ext_ctxt, sp: span,
|
||||
|
@ -64,7 +64,9 @@ use ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
|
||||
variant, view_item, view_item_, view_item_export,
|
||||
view_item_import, view_item_use, view_path, view_path_glob,
|
||||
view_path_list, view_path_simple, visibility, vstore, vstore_box,
|
||||
vstore_fixed, vstore_slice, vstore_uniq};
|
||||
vstore_fixed, vstore_slice, vstore_uniq,
|
||||
expr_vstore_fixed, expr_vstore_slice, expr_vstore_box,
|
||||
expr_vstore_uniq};
|
||||
|
||||
export file_type;
|
||||
export parser;
|
||||
@ -1071,7 +1073,8 @@ impl parser {
|
||||
None => (),
|
||||
Some(v) => {
|
||||
hi = self.span.hi;
|
||||
ex = expr_vstore(self.mk_expr(lo, hi, ex), vstore_fixed(v));
|
||||
ex = expr_vstore(self.mk_expr(lo, hi, ex),
|
||||
expr_vstore_fixed(v));
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
@ -1370,7 +1373,7 @@ impl parser {
|
||||
ex = match e.node {
|
||||
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
|
||||
if m == m_imm => {
|
||||
expr_vstore(e, vstore_slice(self.region_from_name(None)))
|
||||
expr_vstore(e, expr_vstore_slice)
|
||||
}
|
||||
_ => expr_addr_of(m, e)
|
||||
};
|
||||
@ -1386,7 +1389,7 @@ impl parser {
|
||||
// HACK: turn @[...] into a @-evec
|
||||
ex = match e.node {
|
||||
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
|
||||
if m == m_imm => expr_vstore(e, vstore_box),
|
||||
if m == m_imm => expr_vstore(e, expr_vstore_box),
|
||||
_ => expr_unary(box(m), e)
|
||||
};
|
||||
}
|
||||
@ -1398,7 +1401,7 @@ impl parser {
|
||||
// HACK: turn ~[...] into a ~-evec
|
||||
ex = match e.node {
|
||||
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
|
||||
if m == m_imm => expr_vstore(e, vstore_uniq),
|
||||
if m == m_imm => expr_vstore(e, expr_vstore_uniq),
|
||||
_ => expr_unary(uniq(m), e)
|
||||
};
|
||||
}
|
||||
@ -1849,7 +1852,7 @@ impl parser {
|
||||
node: expr_lit(@{node: lit_str(_), span: _}), _
|
||||
}) => {
|
||||
let vst = @{id: self.get_id(), callee_id: self.get_id(),
|
||||
node: expr_vstore(e, vstore_box),
|
||||
node: expr_vstore(e, expr_vstore_box),
|
||||
span: mk_sp(lo, hi)};
|
||||
pat_lit(vst)
|
||||
}
|
||||
@ -1866,7 +1869,7 @@ impl parser {
|
||||
node: expr_lit(@{node: lit_str(_), span: _}), _
|
||||
}) => {
|
||||
let vst = @{id: self.get_id(), callee_id: self.get_id(),
|
||||
node: expr_vstore(e, vstore_uniq),
|
||||
node: expr_vstore(e, expr_vstore_uniq),
|
||||
span: mk_sp(lo, hi)};
|
||||
pat_lit(vst)
|
||||
}
|
||||
@ -1884,10 +1887,12 @@ impl parser {
|
||||
pat_lit(e@@{
|
||||
node: expr_lit(@{node: lit_str(_), span: _}), _
|
||||
}) => {
|
||||
let vst = @{id: self.get_id(), callee_id: self.get_id(),
|
||||
node: expr_vstore(e,
|
||||
vstore_slice(self.region_from_name(None))),
|
||||
span: mk_sp(lo, hi)};
|
||||
let vst = @{
|
||||
id: self.get_id(),
|
||||
callee_id: self.get_id(),
|
||||
node: expr_vstore(e, expr_vstore_slice),
|
||||
span: mk_sp(lo, hi)
|
||||
};
|
||||
pat_lit(vst)
|
||||
}
|
||||
_ => pat_region(sub)
|
||||
|
@ -976,6 +976,16 @@ fn print_vstore(s: ps, t: ast::vstore) {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_expr_vstore(s: ps, t: ast::expr_vstore) {
|
||||
match t {
|
||||
ast::expr_vstore_fixed(Some(i)) => word(s.s, fmt!("%u", i)),
|
||||
ast::expr_vstore_fixed(None) => word(s.s, ~"_"),
|
||||
ast::expr_vstore_uniq => word(s.s, ~"~"),
|
||||
ast::expr_vstore_box => word(s.s, ~"@"),
|
||||
ast::expr_vstore_slice => word(s.s, ~"&"),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
fn print_field(s: ps, field: ast::field) {
|
||||
ibox(s, indent_unit);
|
||||
@ -992,17 +1002,17 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
let ann_node = node_expr(s, expr);
|
||||
s.ann.pre(ann_node);
|
||||
match expr.node {
|
||||
ast::expr_vstore(e, v) => match v {
|
||||
ast::vstore_fixed(_) => {
|
||||
print_expr(s, e);
|
||||
word(s.s, ~"/");
|
||||
print_vstore(s, v);
|
||||
}
|
||||
_ => {
|
||||
print_vstore(s, v);
|
||||
print_expr(s, e);
|
||||
}
|
||||
},
|
||||
ast::expr_vstore(e, v) => match v {
|
||||
ast::expr_vstore_fixed(_) => {
|
||||
print_expr(s, e);
|
||||
word(s.s, ~"/");
|
||||
print_expr_vstore(s, v);
|
||||
}
|
||||
_ => {
|
||||
print_expr_vstore(s, v);
|
||||
print_expr(s, e);
|
||||
}
|
||||
},
|
||||
ast::expr_vec(exprs, mutbl) => {
|
||||
ibox(s, indent_unit);
|
||||
word(s.s, ~"[");
|
||||
|
@ -294,7 +294,7 @@ fn mk_test_desc_vec(cx: test_ctxt) -> @ast::expr {
|
||||
span: dummy_sp()};
|
||||
return @{id: cx.sess.next_node_id(),
|
||||
callee_id: cx.sess.next_node_id(),
|
||||
node: ast::expr_vstore(inner_expr, ast::vstore_uniq),
|
||||
node: ast::expr_vstore(inner_expr, ast::expr_vstore_uniq),
|
||||
span: dummy_sp()};
|
||||
}
|
||||
|
||||
@ -316,7 +316,7 @@ fn mk_test_desc_rec(cx: test_ctxt, test: test) -> @ast::expr {
|
||||
let name_expr = {id: cx.sess.next_node_id(),
|
||||
callee_id: cx.sess.next_node_id(),
|
||||
node: ast::expr_vstore(name_expr_inner,
|
||||
ast::vstore_uniq),
|
||||
ast::expr_vstore_uniq),
|
||||
span: dummy_sp()};
|
||||
|
||||
|
||||
|
@ -120,7 +120,7 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
|
||||
tag_table_spill = 0x5f,
|
||||
tag_table_method_map = 0x60,
|
||||
tag_table_vtable_map = 0x61,
|
||||
tag_table_borrowings = 0x62
|
||||
tag_table_adjustments = 0x62
|
||||
}
|
||||
|
||||
// djb's cdb hashes.
|
||||
|
@ -11,8 +11,10 @@ use middle::ty;
|
||||
use std::map::HashMap;
|
||||
use ty::{FnTyBase, FnMeta, FnSig};
|
||||
|
||||
export parse_ty_data, parse_def_id, parse_ident;
|
||||
export parse_state_from_data;
|
||||
export parse_arg_data, parse_ty_data, parse_def_id, parse_ident;
|
||||
export parse_bounds_data;
|
||||
export pstate;
|
||||
|
||||
// Compact string representation for ty::t values. API ty_str &
|
||||
// parse_from_str. Extra parameters are for converting to/from def_ids in the
|
||||
@ -53,13 +55,25 @@ fn parse_ident_(st: @pstate, is_last: fn@(char) -> bool) ->
|
||||
return st.tcx.sess.ident_of(rslt);
|
||||
}
|
||||
|
||||
fn parse_state_from_data(data: @~[u8], crate_num: int,
|
||||
pos: uint, tcx: ty::ctxt)
|
||||
-> @pstate
|
||||
{
|
||||
@{data: data, crate: crate_num, mut pos: pos, tcx: tcx}
|
||||
}
|
||||
|
||||
fn parse_ty_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
|
||||
conv: conv_did) -> ty::t {
|
||||
let st = @{data: data, crate: crate_num, mut pos: pos, tcx: tcx};
|
||||
let st = parse_state_from_data(data, crate_num, pos, tcx);
|
||||
parse_ty(st, conv)
|
||||
}
|
||||
|
||||
fn parse_arg_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
|
||||
conv: conv_did) -> ty::arg {
|
||||
let st = parse_state_from_data(data, crate_num, pos, tcx);
|
||||
parse_arg(st, conv)
|
||||
}
|
||||
|
||||
fn parse_ret_ty(st: @pstate, conv: conv_did) -> (ast::ret_style, ty::t) {
|
||||
match peek(st) {
|
||||
'!' => { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) }
|
||||
@ -373,6 +387,23 @@ fn parse_purity(c: char) -> purity {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
|
||||
{mode: parse_mode(st),
|
||||
ty: parse_ty(st, conv)}
|
||||
}
|
||||
|
||||
fn parse_mode(st: @pstate) -> ast::mode {
|
||||
let m = ast::expl(match next(st) {
|
||||
'&' => ast::by_mutbl_ref,
|
||||
'-' => ast::by_move,
|
||||
'+' => ast::by_copy,
|
||||
'=' => ast::by_ref,
|
||||
'#' => ast::by_val,
|
||||
_ => fail ~"bad mode"
|
||||
});
|
||||
return m;
|
||||
}
|
||||
|
||||
fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
|
||||
let proto = parse_proto(st);
|
||||
let purity = parse_purity(next(st));
|
||||
@ -380,16 +411,8 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
|
||||
assert (next(st) == '[');
|
||||
let mut inputs: ~[ty::arg] = ~[];
|
||||
while peek(st) != ']' {
|
||||
let mode = match peek(st) {
|
||||
'&' => ast::by_mutbl_ref,
|
||||
'-' => ast::by_move,
|
||||
'+' => ast::by_copy,
|
||||
'=' => ast::by_ref,
|
||||
'#' => ast::by_val,
|
||||
_ => fail ~"bad mode"
|
||||
};
|
||||
st.pos += 1u;
|
||||
vec::push(inputs, {mode: ast::expl(mode), ty: parse_ty(st, conv)});
|
||||
let mode = parse_mode(st);
|
||||
vec::push(inputs, {mode: mode, ty: parse_ty(st, conv)});
|
||||
}
|
||||
st.pos += 1u; // eat the ']'
|
||||
let (ret_style, ret_ty) = parse_ret_ty(st, conv);
|
||||
@ -432,8 +455,9 @@ fn parse_def_id(buf: &[u8]) -> ast::def_id {
|
||||
|
||||
fn parse_bounds_data(data: @~[u8], start: uint,
|
||||
crate_num: int, tcx: ty::ctxt, conv: conv_did)
|
||||
-> @~[ty::param_bound] {
|
||||
let st = @{data: data, crate: crate_num, mut pos: start, tcx: tcx};
|
||||
-> @~[ty::param_bound]
|
||||
{
|
||||
let st = parse_state_from_data(data, crate_num, start, tcx);
|
||||
parse_bounds(st, conv)
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ export ac_use_abbrevs;
|
||||
export enc_ty;
|
||||
export enc_bounds;
|
||||
export enc_mode;
|
||||
export enc_arg;
|
||||
|
||||
type ctxt = {
|
||||
diag: span_handler,
|
||||
@ -323,6 +324,11 @@ fn enc_proto(w: io::Writer, cx: @ctxt, proto: ty::fn_proto) {
|
||||
}
|
||||
}
|
||||
|
||||
fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
|
||||
enc_mode(w, cx, arg.mode);
|
||||
enc_ty(w, cx, arg.ty);
|
||||
}
|
||||
|
||||
fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
|
||||
match ty::resolved_mode(cx.tcx, m) {
|
||||
by_mutbl_ref => w.write_char('&'),
|
||||
@ -348,8 +354,7 @@ fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) {
|
||||
enc_bounds(w, cx, ft.meta.bounds);
|
||||
w.write_char('[');
|
||||
for ft.sig.inputs.each |arg| {
|
||||
enc_mode(w, cx, arg.mode);
|
||||
enc_ty(w, cx, arg.ty);
|
||||
enc_arg(w, cx, arg);
|
||||
}
|
||||
w.write_char(']');
|
||||
match ft.meta.ret_style {
|
||||
|
@ -18,10 +18,8 @@ use std::serialization::DeserializerHelpers;
|
||||
use std::prettyprint::Serializer;
|
||||
use middle::{ty, typeck};
|
||||
use middle::typeck::{method_origin, method_map_entry,
|
||||
serialize_method_map_entry,
|
||||
deserialize_method_map_entry,
|
||||
vtable_res,
|
||||
vtable_origin};
|
||||
vtable_res,
|
||||
vtable_origin};
|
||||
use driver::session::session;
|
||||
use middle::freevars::{freevar_entry,
|
||||
serialize_freevar_entry,
|
||||
@ -385,6 +383,45 @@ impl ast::def: tr {
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
// Encoding and decoding of adjustment information
|
||||
|
||||
impl ty::AutoAdjustment: tr {
|
||||
fn tr(xcx: extended_decode_ctxt) -> ty::AutoAdjustment {
|
||||
{autoderefs: self.autoderefs,
|
||||
autoref: self.autoref.map(|ar| ar.tr(xcx))}
|
||||
}
|
||||
}
|
||||
|
||||
impl ty::AutoRef: tr {
|
||||
fn tr(xcx: extended_decode_ctxt) -> ty::AutoRef {
|
||||
{kind: self.kind,
|
||||
region: self.region.tr(xcx),
|
||||
mutbl: self.mutbl}
|
||||
}
|
||||
}
|
||||
|
||||
impl ty::region: tr {
|
||||
fn tr(xcx: extended_decode_ctxt) -> ty::region {
|
||||
match self {
|
||||
ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
|
||||
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
|
||||
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
|
||||
ty::re_static | ty::re_var(*) => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ty::bound_region: tr {
|
||||
fn tr(xcx: extended_decode_ctxt) -> ty::bound_region {
|
||||
match self {
|
||||
ty::br_anon(_) | ty::br_named(_) | ty::br_self => self,
|
||||
ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id),
|
||||
@br.tr(xcx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
// Encoding and decoding of freevar information
|
||||
|
||||
@ -416,12 +453,31 @@ trait read_method_map_entry_helper {
|
||||
fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry;
|
||||
}
|
||||
|
||||
fn serialize_method_map_entry(ecx: @e::encode_ctxt,
|
||||
ebml_w: ebml::Writer,
|
||||
mme: method_map_entry) {
|
||||
do ebml_w.emit_rec {
|
||||
do ebml_w.emit_rec_field(~"self_arg", 0u) {
|
||||
ebml_w.emit_arg(ecx, mme.self_arg);
|
||||
}
|
||||
do ebml_w.emit_rec_field(~"origin", 1u) {
|
||||
typeck::serialize_method_origin(ebml_w, mme.origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ebml::EbmlDeserializer: read_method_map_entry_helper {
|
||||
fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry {
|
||||
let mme = deserialize_method_map_entry(self);
|
||||
{derefs: mme.derefs,
|
||||
self_mode: mme.self_mode,
|
||||
origin: mme.origin.tr(xcx)}
|
||||
do self.read_rec {
|
||||
{self_arg:
|
||||
self.read_rec_field(~"self_arg", 0u, || {
|
||||
self.read_arg(xcx)
|
||||
}),
|
||||
origin:
|
||||
self.read_rec_field(~"origin", 1u, || {
|
||||
typeck::deserialize_method_origin(self).tr(xcx)
|
||||
})}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,8 +501,8 @@ impl method_origin: tr {
|
||||
// Encoding and decoding vtable_res
|
||||
|
||||
fn encode_vtable_res(ecx: @e::encode_ctxt,
|
||||
ebml_w: ebml::Writer,
|
||||
dr: typeck::vtable_res) {
|
||||
ebml_w: ebml::Writer,
|
||||
dr: typeck::vtable_res) {
|
||||
// can't autogenerate this code because automatic serialization of
|
||||
// ty::t doesn't work, and there is no way (atm) to have
|
||||
// hand-written serialization routines combine with auto-generated
|
||||
@ -573,6 +629,7 @@ impl @e::encode_ctxt: get_ty_str_ctxt {
|
||||
}
|
||||
|
||||
trait ebml_writer_helpers {
|
||||
fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg);
|
||||
fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t);
|
||||
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]);
|
||||
fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds);
|
||||
@ -581,17 +638,27 @@ trait ebml_writer_helpers {
|
||||
|
||||
impl ebml::Writer: ebml_writer_helpers {
|
||||
fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) {
|
||||
e::write_type(ecx, self, ty)
|
||||
}
|
||||
|
||||
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
|
||||
do self.emit_from_vec(tys) |ty| {
|
||||
do self.emit_opaque {
|
||||
e::write_type(ecx, self, ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg) {
|
||||
do self.emit_opaque {
|
||||
tyencode::enc_arg(self.writer, ecx.ty_str_ctxt(), arg);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
|
||||
do self.emit_from_vec(tys) |ty| {
|
||||
self.emit_ty(ecx, ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds) {
|
||||
tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
|
||||
do self.emit_opaque {
|
||||
tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_tpbt(ecx: @e::encode_ctxt, tpbt: ty::ty_param_bounds_and_ty) {
|
||||
@ -664,7 +731,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
||||
do ebml_w.tag(c::tag_table_node_type) {
|
||||
ebml_w.id(id);
|
||||
do ebml_w.tag(c::tag_table_val) {
|
||||
e::write_type(ecx, ebml_w, ty)
|
||||
ebml_w.emit_ty(ecx, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -743,7 +810,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
||||
do ebml_w.tag(c::tag_table_method_map) {
|
||||
ebml_w.id(id);
|
||||
do ebml_w.tag(c::tag_table_val) {
|
||||
serialize_method_map_entry(ebml_w, mme)
|
||||
serialize_method_map_entry(ecx, ebml_w, mme)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -757,13 +824,11 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
do option::iter(tcx.borrowings.find(id)) |_borrow| {
|
||||
do ebml_w.tag(c::tag_table_borrowings) {
|
||||
do option::iter(tcx.adjustments.find(id)) |adj| {
|
||||
do ebml_w.tag(c::tag_table_adjustments) {
|
||||
ebml_w.id(id);
|
||||
do ebml_w.tag(c::tag_table_val) {
|
||||
// N.B. We don't actually serialize borrows as, in
|
||||
// trans, we only care whether a value is borrowed or
|
||||
// not.
|
||||
ty::serialize_AutoAdjustment(ebml_w, *adj)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -782,6 +847,7 @@ impl ebml::Doc: doc_decoder_helpers {
|
||||
}
|
||||
|
||||
trait ebml_deserializer_decoder_helpers {
|
||||
fn read_arg(xcx: extended_decode_ctxt) -> ty::arg;
|
||||
fn read_ty(xcx: extended_decode_ctxt) -> ty::t;
|
||||
fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t];
|
||||
fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound];
|
||||
@ -791,15 +857,25 @@ trait ebml_deserializer_decoder_helpers {
|
||||
|
||||
impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
|
||||
|
||||
fn read_arg(xcx: extended_decode_ctxt) -> ty::arg {
|
||||
do self.read_opaque |doc| {
|
||||
tydecode::parse_arg_data(
|
||||
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|
||||
|a| xcx.tr_def_id(a))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ty(xcx: extended_decode_ctxt) -> ty::t {
|
||||
// Note: regions types embed local node ids. In principle, we
|
||||
// should translate these node ids into the new decode
|
||||
// context. However, we do not bother, because region types
|
||||
// are not used during trans.
|
||||
|
||||
tydecode::parse_ty_data(
|
||||
self.parent.data, xcx.dcx.cdata.cnum, self.pos, xcx.dcx.tcx,
|
||||
|a| xcx.tr_def_id(a) )
|
||||
do self.read_opaque |doc| {
|
||||
tydecode::parse_ty_data(
|
||||
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|
||||
|a| xcx.tr_def_id(a))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t] {
|
||||
@ -807,13 +883,16 @@ impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
|
||||
}
|
||||
|
||||
fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound] {
|
||||
tydecode::parse_bounds_data(
|
||||
self.parent.data, self.pos, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
|
||||
|a| xcx.tr_def_id(a) )
|
||||
do self.read_opaque |doc| {
|
||||
tydecode::parse_bounds_data(
|
||||
doc.data, doc.start, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
|
||||
|a| xcx.tr_def_id(a))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ty_param_bounds_and_ty(xcx: extended_decode_ctxt)
|
||||
-> ty::ty_param_bounds_and_ty {
|
||||
-> ty::ty_param_bounds_and_ty
|
||||
{
|
||||
do self.read_rec {
|
||||
{
|
||||
bounds: self.read_rec_field(~"bounds", 0u, || {
|
||||
@ -881,12 +960,9 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
||||
} else if tag == (c::tag_table_vtable_map as uint) {
|
||||
dcx.maps.vtable_map.insert(id,
|
||||
val_dsr.read_vtable_res(xcx));
|
||||
} else if tag == (c::tag_table_borrowings as uint) {
|
||||
// N.B.: we don't actually *serialize* borrows because, in
|
||||
// trans, the only thing we care about is whether a value was
|
||||
// borrowed or not.
|
||||
let borrow = {region: ty::re_static, mutbl: ast::m_imm};
|
||||
dcx.tcx.borrowings.insert(id, borrow);
|
||||
} else if tag == (c::tag_table_adjustments as uint) {
|
||||
let adj = @ty::deserialize_AutoAdjustment(val_dsr).tr(xcx);
|
||||
dcx.tcx.adjustments.insert(id, adj);
|
||||
} else {
|
||||
xcx.dcx.tcx.sess.bug(
|
||||
fmt!("unknown tag found in side tables: %x", tag));
|
||||
|
@ -221,7 +221,7 @@ use syntax::ast_util;
|
||||
use syntax::ast_map;
|
||||
use syntax::codemap::span;
|
||||
use util::ppaux::{ty_to_str, region_to_str, explain_region,
|
||||
note_and_explain_region};
|
||||
expr_repr, note_and_explain_region};
|
||||
use std::map::{int_hash, HashMap, Set};
|
||||
use std::list;
|
||||
use std::list::{List, Cons, Nil};
|
||||
@ -318,7 +318,7 @@ enum bckerr_code {
|
||||
err_mut_uniq,
|
||||
err_mut_variant,
|
||||
err_root_not_permitted,
|
||||
err_mutbl(ast::mutability, ast::mutability),
|
||||
err_mutbl(ast::mutability),
|
||||
err_out_of_root_scope(ty::region, ty::region), // superscope, subscope
|
||||
err_out_of_scope(ty::region, ty::region) // superscope, subscope
|
||||
}
|
||||
@ -344,9 +344,9 @@ impl bckerr_code : cmp::Eq {
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
err_mutbl(e0a, e1a) => {
|
||||
err_mutbl(e0a) => {
|
||||
match other {
|
||||
err_mutbl(e0b, e1b) => e0a == e0b && e1a == e1b,
|
||||
err_mutbl(e0b) => e0a == e0b,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
@ -444,10 +444,6 @@ impl borrowck_ctxt {
|
||||
cat_expr(self.tcx, self.method_map, expr)
|
||||
}
|
||||
|
||||
fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
|
||||
cat_borrow_of_expr(self.tcx, self.method_map, expr)
|
||||
}
|
||||
|
||||
fn cat_def(id: ast::node_id,
|
||||
span: span,
|
||||
ty: ty::t,
|
||||
@ -482,8 +478,8 @@ impl borrowck_ctxt {
|
||||
self.span_err(
|
||||
err.cmt.span,
|
||||
fmt!("illegal borrow: %s",
|
||||
self.bckerr_code_to_str(err.code)));
|
||||
self.note_and_explain_bckerr(err.code);
|
||||
self.bckerr_to_str(err)));
|
||||
self.note_and_explain_bckerr(err);
|
||||
}
|
||||
|
||||
fn span_err(s: span, m: ~str) {
|
||||
@ -506,11 +502,12 @@ impl borrowck_ctxt {
|
||||
}
|
||||
}
|
||||
|
||||
fn bckerr_code_to_str(code: bckerr_code) -> ~str {
|
||||
match code {
|
||||
err_mutbl(req, act) => {
|
||||
fmt!("creating %s alias to aliasable, %s memory",
|
||||
self.mut_to_str(req), self.mut_to_str(act))
|
||||
fn bckerr_to_str(err: bckerr) -> ~str {
|
||||
match err.code {
|
||||
err_mutbl(req) => {
|
||||
fmt!("creating %s alias to %s",
|
||||
self.mut_to_str(req),
|
||||
self.cmt_to_str(err.cmt))
|
||||
}
|
||||
err_mut_uniq => {
|
||||
~"unique value in aliasable, mutable location"
|
||||
@ -533,7 +530,8 @@ impl borrowck_ctxt {
|
||||
}
|
||||
}
|
||||
|
||||
fn note_and_explain_bckerr(code: bckerr_code) {
|
||||
fn note_and_explain_bckerr(err: bckerr) {
|
||||
let code = err.code;
|
||||
match code {
|
||||
err_mutbl(*) | err_mut_uniq | err_mut_variant |
|
||||
err_root_not_permitted => {}
|
||||
|
@ -75,8 +75,7 @@ fn check_loans(bccx: borrowck_ctxt,
|
||||
|
||||
enum assignment_type {
|
||||
at_straight_up,
|
||||
at_swap,
|
||||
at_mutbl_ref,
|
||||
at_swap
|
||||
}
|
||||
|
||||
impl assignment_type : cmp::Eq {
|
||||
@ -92,15 +91,13 @@ impl assignment_type {
|
||||
// are only assigned once; but it doesn't consider &mut
|
||||
match self {
|
||||
at_straight_up => true,
|
||||
at_swap => true,
|
||||
at_mutbl_ref => false
|
||||
at_swap => true
|
||||
}
|
||||
}
|
||||
fn ing_form(desc: ~str) -> ~str {
|
||||
match self {
|
||||
at_straight_up => ~"assigning to " + desc,
|
||||
at_swap => ~"swapping to and from " + desc,
|
||||
at_mutbl_ref => ~"taking mut reference to " + desc
|
||||
at_swap => ~"swapping to and from " + desc
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,11 +366,9 @@ impl check_loan_ctxt {
|
||||
// taking a mutable ref. that will create a loan of its own
|
||||
// which will be checked for compat separately in
|
||||
// check_for_conflicting_loans()
|
||||
if at != at_mutbl_ref {
|
||||
for cmt.lp.each |lp| {
|
||||
self.check_for_loan_conflicting_with_assignment(
|
||||
at, ex, cmt, lp);
|
||||
}
|
||||
for cmt.lp.each |lp| {
|
||||
self.check_for_loan_conflicting_with_assignment(
|
||||
at, ex, cmt, lp);
|
||||
}
|
||||
|
||||
self.bccx.add_to_mutbl_map(cmt);
|
||||
@ -430,8 +425,8 @@ impl check_loan_ctxt {
|
||||
self.tcx().sess.span_err(
|
||||
e.cmt.span,
|
||||
fmt!("illegal borrow unless pure: %s",
|
||||
self.bccx.bckerr_code_to_str(e.code)));
|
||||
self.bccx.note_and_explain_bckerr(e.code);
|
||||
self.bccx.bckerr_to_str(e)));
|
||||
self.bccx.note_and_explain_bckerr(e);
|
||||
self.tcx().sess.span_note(
|
||||
sp,
|
||||
fmt!("impure due to %s", msg));
|
||||
@ -531,14 +526,12 @@ impl check_loan_ctxt {
|
||||
ty::node_id_to_type(self.tcx(), callee_id));
|
||||
do vec::iter2(args, arg_tys) |arg, arg_ty| {
|
||||
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||
ast::by_move => {
|
||||
self.check_move_out(arg);
|
||||
}
|
||||
ast::by_mutbl_ref => {
|
||||
self.check_assignment(at_mutbl_ref, arg);
|
||||
}
|
||||
ast::by_ref | ast::by_copy | ast::by_val => {
|
||||
}
|
||||
ast::by_move => {
|
||||
self.check_move_out(arg);
|
||||
}
|
||||
ast::by_mutbl_ref | ast::by_ref |
|
||||
ast::by_copy | ast::by_val => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -645,19 +638,6 @@ fn check_loans_in_expr(expr: @ast::expr,
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::expr_addr_of(mutbl, base) => {
|
||||
match mutbl {
|
||||
m_const => { /*all memory is const*/ }
|
||||
m_mutbl => {
|
||||
// If we are taking an &mut ptr, make sure the memory
|
||||
// being pointed at is assignable in the first place:
|
||||
self.check_assignment(at_mutbl_ref, base);
|
||||
}
|
||||
m_imm => {
|
||||
// XXX explain why no check is req'd here
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::expr_call(f, args, _) => {
|
||||
self.check_call(expr, Some(f), f.id, f.span, args);
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
// their associated scopes. In phase two, checking loans, we will then make
|
||||
// sure that all of these loans are honored.
|
||||
|
||||
use mem_categorization::{opt_deref_kind};
|
||||
use mem_categorization::{mem_categorization_ctxt, opt_deref_kind};
|
||||
use preserve::{preserve_condition, pc_ok, pc_if_pure};
|
||||
use ty::ty_region;
|
||||
use ty::{ty_region};
|
||||
|
||||
export gather_loans;
|
||||
|
||||
@ -94,9 +94,8 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||
ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
|
||||
|
||||
// If this expression is borrowed, have to ensure it remains valid:
|
||||
for tcx.borrowings.find(ex.id).each |borrow| {
|
||||
let cmt = self.bccx.cat_borrow_of_expr(ex);
|
||||
self.guarantee_valid(cmt, borrow.mutbl, borrow.region);
|
||||
for tcx.adjustments.find(ex.id).each |adjustments| {
|
||||
self.guarantee_adjustments(ex, adjustments);
|
||||
}
|
||||
|
||||
// Special checks for various kinds of expressions:
|
||||
@ -125,45 +124,9 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
||||
}
|
||||
ast::by_val => {
|
||||
// Rust's by-val does not actually give ownership to
|
||||
// the callee. This means that if a pointer type is
|
||||
// passed, it is effectively a borrow, and so the
|
||||
// caller must guarantee that the data remains valid.
|
||||
//
|
||||
// Subtle: we only guarantee that the pointer is valid
|
||||
// and const. Technically, we ought to pass in the
|
||||
// mutability that the caller expects (e.g., if the
|
||||
// formal argument has type @mut, we should guarantee
|
||||
// validity and mutability, not validity and const).
|
||||
// However, the type system already guarantees that
|
||||
// the caller's mutability is compatible with the
|
||||
// callee, so this is not necessary. (Note that with
|
||||
// actual borrows, typeck is more liberal and allows
|
||||
// the pointer to be borrowed as immutable even if it
|
||||
// is mutable in the caller's frame, thus effectively
|
||||
// passing the buck onto us to enforce this)
|
||||
//
|
||||
// FIXME (#2493): this handling is not really adequate.
|
||||
// For example, if there is a type like, {f: ~[int]}, we
|
||||
// will ignore it, but we ought to be requiring it to be
|
||||
// immutable (whereas something like {f:int} would be
|
||||
// fine).
|
||||
//
|
||||
match opt_deref_kind(arg_ty.ty) {
|
||||
Some(deref_ptr(region_ptr(_))) |
|
||||
Some(deref_ptr(unsafe_ptr)) => {
|
||||
/* region pointers are (by induction) guaranteed */
|
||||
/* unsafe pointers are the user's problem */
|
||||
}
|
||||
Some(deref_comp(_)) |
|
||||
None => {
|
||||
/* not a pointer, no worries */
|
||||
}
|
||||
Some(deref_ptr(_)) => {
|
||||
let arg_cmt = self.bccx.cat_borrow_of_expr(arg);
|
||||
self.guarantee_valid(arg_cmt, m_const, scope_r);
|
||||
}
|
||||
}
|
||||
// FIXME (#2493): safety checks would be required here,
|
||||
// but the correct set is really hard to get right,
|
||||
// and modes are going away anyhow.
|
||||
}
|
||||
ast::by_move | ast::by_copy => {}
|
||||
}
|
||||
@ -261,6 +224,42 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||
impl gather_loan_ctxt {
|
||||
fn tcx() -> ty::ctxt { self.bccx.tcx }
|
||||
|
||||
fn guarantee_adjustments(expr: @ast::expr,
|
||||
adjustment: &ty::AutoAdjustment) {
|
||||
debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
|
||||
expr_repr(self.tcx(), expr), adjustment);
|
||||
let _i = indenter();
|
||||
|
||||
match adjustment.autoref {
|
||||
None => {
|
||||
debug!("no autoref");
|
||||
return;
|
||||
}
|
||||
|
||||
Some(ref autoref) => {
|
||||
let mcx = &mem_categorization_ctxt {
|
||||
tcx: self.tcx(),
|
||||
method_map: self.bccx.method_map};
|
||||
let mut cmt = mcx.cat_expr_autoderefd(expr, adjustment);
|
||||
debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
|
||||
|
||||
match autoref.kind {
|
||||
ty::AutoPtr => {
|
||||
self.guarantee_valid(cmt,
|
||||
autoref.mutbl,
|
||||
autoref.region)
|
||||
}
|
||||
ty::AutoSlice => {
|
||||
let cmt_index = mcx.cat_index(expr, cmt);
|
||||
self.guarantee_valid(cmt_index,
|
||||
autoref.mutbl,
|
||||
autoref.region)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// guarantees that addr_of(cmt) will be valid for the duration of
|
||||
// `static_scope_r`, or reports an error. This may entail taking
|
||||
// out loans, which will be added to the `req_loan_map`. This can
|
||||
@ -387,25 +386,17 @@ impl gather_loan_ctxt {
|
||||
// mutable memory.
|
||||
fn check_mutbl(req_mutbl: ast::mutability,
|
||||
cmt: cmt) -> bckres<preserve_condition> {
|
||||
match (req_mutbl, cmt.mutbl) {
|
||||
(m_const, _) |
|
||||
(m_imm, m_imm) |
|
||||
(m_mutbl, m_mutbl) => {
|
||||
if req_mutbl == m_const || req_mutbl == cmt.mutbl {
|
||||
Ok(pc_ok)
|
||||
}
|
||||
|
||||
(_, m_const) |
|
||||
(m_imm, m_mutbl) |
|
||||
(m_mutbl, m_imm) => {
|
||||
} else {
|
||||
let e = {cmt: cmt,
|
||||
code: err_mutbl(req_mutbl, cmt.mutbl)};
|
||||
code: err_mutbl(req_mutbl)};
|
||||
if req_mutbl == m_imm {
|
||||
// you can treat mutable things as imm if you are pure
|
||||
Ok(pc_if_pure(e))
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,23 +36,40 @@ enum loan_ctxt {
|
||||
impl loan_ctxt {
|
||||
fn tcx() -> ty::ctxt { self.bccx.tcx }
|
||||
|
||||
fn ok_with_loan_of(cmt: cmt,
|
||||
scope_ub: ty::region,
|
||||
mutbl: ast::mutability) -> bckres<()> {
|
||||
fn issue_loan(cmt: cmt,
|
||||
scope_ub: ty::region,
|
||||
req_mutbl: ast::mutability) -> bckres<()> {
|
||||
if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
|
||||
// Note: all cmt's that we deal with will have a non-none
|
||||
// lp, because the entry point into this routine,
|
||||
// `borrowck_ctxt::loan()`, rejects any cmt with a
|
||||
// none-lp.
|
||||
(*self.loans).push({lp: option::get(cmt.lp),
|
||||
cmt: cmt,
|
||||
mutbl: mutbl});
|
||||
Ok(())
|
||||
match req_mutbl {
|
||||
m_mutbl => {
|
||||
// We do not allow non-mutable data to be loaned
|
||||
// out as mutable under any circumstances.
|
||||
if cmt.mutbl != m_mutbl {
|
||||
return Err({cmt:cmt,
|
||||
code:err_mutbl(req_mutbl)});
|
||||
}
|
||||
}
|
||||
m_const | m_imm => {
|
||||
// However, mutable data can be loaned out as
|
||||
// immutable (and any data as const). The
|
||||
// `check_loans` pass will then guarantee that no
|
||||
// writes occur for the duration of the loan.
|
||||
}
|
||||
}
|
||||
|
||||
(*self.loans).push({
|
||||
// Note: cmt.lp must be Some(_) because otherwise this
|
||||
// loan process does not apply at all.
|
||||
lp: cmt.lp.get(),
|
||||
cmt: cmt,
|
||||
mutbl: req_mutbl});
|
||||
return Ok(());
|
||||
} else {
|
||||
// The loan being requested lives longer than the data
|
||||
// being loaned out!
|
||||
Err({cmt:cmt, code:err_out_of_scope(scope_ub,
|
||||
self.scope_region)})
|
||||
return Err({cmt:cmt,
|
||||
code:err_out_of_scope(scope_ub,
|
||||
self.scope_region)});
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +95,7 @@ impl loan_ctxt {
|
||||
}
|
||||
cat_local(local_id) | cat_arg(local_id) => {
|
||||
let local_scope_id = self.tcx().region_map.get(local_id);
|
||||
self.ok_with_loan_of(cmt, ty::re_scope(local_scope_id), req_mutbl)
|
||||
self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl)
|
||||
}
|
||||
cat_stack_upvar(cmt) => {
|
||||
self.loan(cmt, req_mutbl) // NDM correct?
|
||||
@ -138,7 +155,7 @@ impl loan_ctxt {
|
||||
do self.loan(cmt_base, base_mutbl).chain |_ok| {
|
||||
// can use static for the scope because the base
|
||||
// determines the lifetime, ultimately
|
||||
self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
|
||||
self.issue_loan(cmt, ty::re_static, req_mutbl)
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +170,7 @@ impl loan_ctxt {
|
||||
// could change.
|
||||
do self.loan(cmt_base, m_imm).chain |_ok| {
|
||||
// can use static, as in loan_stable_comp()
|
||||
self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
|
||||
self.issue_loan(cmt, ty::re_static, req_mutbl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ priv impl &preserve_ctxt {
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("must root @T, err: %s",
|
||||
self.bccx.bckerr_code_to_str(e.code));
|
||||
self.bccx.bckerr_to_str(e));
|
||||
self.attempt_root(cmt, base, derefs)
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
|
||||
fn is_str(e: @expr) -> bool {
|
||||
match e.node {
|
||||
expr_vstore(@{node: expr_lit(@{node: lit_str(_), _}), _},
|
||||
vstore_uniq) => true,
|
||||
expr_vstore_uniq) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
@ -98,8 +98,8 @@ fn check_expr(sess: session, def_map: resolve::DefMap,
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_vstore(_, vstore_slice(_)) |
|
||||
expr_vstore(_, vstore_fixed(_)) |
|
||||
expr_vstore(_, expr_vstore_slice) |
|
||||
expr_vstore(_, expr_vstore_fixed(_)) |
|
||||
expr_vec(_, m_imm) |
|
||||
expr_addr_of(m_imm, _) |
|
||||
expr_field(*) |
|
||||
|
@ -87,12 +87,12 @@ fn classify(e: @expr,
|
||||
}
|
||||
|
||||
ast::expr_vstore(e, vstore) => {
|
||||
match vstore {
|
||||
ast::vstore_fixed(_) |
|
||||
ast::vstore_slice(_) => classify(e, def_map, tcx),
|
||||
ast::vstore_uniq |
|
||||
ast::vstore_box => non_const
|
||||
}
|
||||
match vstore {
|
||||
ast::expr_vstore_fixed(_) |
|
||||
ast::expr_vstore_slice => classify(e, def_map, tcx),
|
||||
ast::expr_vstore_uniq |
|
||||
ast::expr_vstore_box => non_const
|
||||
}
|
||||
}
|
||||
|
||||
ast::expr_struct(_, fs, None) |
|
||||
|
@ -332,8 +332,13 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
// If this is a method call with a by-val argument, we need
|
||||
// to check the copy
|
||||
match cx.method_map.find(e.id) {
|
||||
Some({self_mode: by_copy, _}) => maybe_copy(cx, lhs, None),
|
||||
_ => ()
|
||||
Some(ref mme) => {
|
||||
match ty::arg_mode(cx.tcx, mme.self_arg) {
|
||||
by_copy => maybe_copy(cx, lhs, None),
|
||||
by_ref | by_val | by_mutbl_ref | by_move => ()
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
expr_repeat(element, count_expr, _) => {
|
||||
@ -437,11 +442,18 @@ fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
|
||||
!is_nullary_variant(cx, ex) &&
|
||||
|
||||
// borrowed unique value isn't really a copy
|
||||
!cx.tcx.borrowings.contains_key(ex.id)
|
||||
!is_autorefd(cx, ex)
|
||||
{
|
||||
let ty = ty::expr_ty(cx.tcx, ex);
|
||||
check_copy(cx, ex.id, ty, ex.span, implicit_copy, why);
|
||||
}
|
||||
|
||||
fn is_autorefd(cx: ctx, ex: @expr) -> bool {
|
||||
match cx.tcx.adjustments.find(ex.id) {
|
||||
None => false,
|
||||
Some(ref adj) => adj.autoref.is_some()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_imm_free_var(cx: ctx, def: def, sp: span) {
|
||||
|
@ -340,17 +340,6 @@ fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind {
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_borrow_of_expr(
|
||||
tcx: ty::ctxt,
|
||||
method_map: typeck::method_map,
|
||||
expr: @ast::expr) -> cmt {
|
||||
|
||||
let mcx = &mem_categorization_ctxt {
|
||||
tcx: tcx, method_map: method_map
|
||||
};
|
||||
return mcx.cat_borrow_of_expr(expr);
|
||||
}
|
||||
|
||||
fn cat_expr(
|
||||
tcx: ty::ctxt,
|
||||
method_map: typeck::method_map,
|
||||
@ -420,33 +409,40 @@ struct mem_categorization_ctxt {
|
||||
}
|
||||
|
||||
impl &mem_categorization_ctxt {
|
||||
fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
|
||||
// Any expression can be borrowed (to account for auto-ref on method
|
||||
// receivers), but @, ~, @vec, and ~vec are handled specially.
|
||||
let expr_ty = ty::expr_ty(self.tcx, expr);
|
||||
match ty::get(expr_ty).sty {
|
||||
ty::ty_evec(*) | ty::ty_estr(*) => {
|
||||
self.cat_index(expr, expr)
|
||||
}
|
||||
fn cat_expr(expr: @ast::expr) -> cmt {
|
||||
match self.tcx.adjustments.find(expr.id) {
|
||||
None => {
|
||||
// No adjustments.
|
||||
self.cat_expr_unadjusted(expr)
|
||||
}
|
||||
|
||||
ty::ty_uniq(*) | ty::ty_box(*) | ty::ty_rptr(*) => {
|
||||
let cmt = self.cat_expr(expr);
|
||||
self.cat_deref(expr, cmt, 0u, true).get()
|
||||
}
|
||||
|
||||
/*
|
||||
ty::ty_fn({proto, _}) {
|
||||
self.cat_call(expr, expr, proto)
|
||||
}
|
||||
*/
|
||||
|
||||
_ => {
|
||||
self.cat_rvalue(expr, expr_ty)
|
||||
}
|
||||
Some(adjustment) => {
|
||||
match adjustment.autoref {
|
||||
Some(_) => {
|
||||
// Equivalent to &*expr or something similar.
|
||||
// This is an rvalue, effectively.
|
||||
let expr_ty = ty::expr_ty(self.tcx, expr);
|
||||
self.cat_rvalue(expr, expr_ty)
|
||||
}
|
||||
None => {
|
||||
// Equivalent to *expr or something similar.
|
||||
self.cat_expr_autoderefd(expr, adjustment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_expr(expr: @ast::expr) -> cmt {
|
||||
fn cat_expr_autoderefd(expr: @ast::expr,
|
||||
adjustment: &ty::AutoAdjustment) -> cmt {
|
||||
let mut cmt = self.cat_expr_unadjusted(expr);
|
||||
for uint::range(1, adjustment.autoderefs+1) |deref| {
|
||||
cmt = self.cat_deref(expr, cmt, deref);
|
||||
}
|
||||
return cmt;
|
||||
}
|
||||
|
||||
fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
|
||||
debug!("cat_expr: id=%d expr=%s",
|
||||
expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
|
||||
|
||||
@ -459,15 +455,7 @@ impl &mem_categorization_ctxt {
|
||||
}
|
||||
|
||||
let base_cmt = self.cat_expr(e_base);
|
||||
match self.cat_deref(expr, base_cmt, 0u, true) {
|
||||
Some(cmt) => return cmt,
|
||||
None => {
|
||||
tcx.sess.span_bug(
|
||||
e_base.span,
|
||||
fmt!("Explicit deref of non-derefable type `%s`",
|
||||
ty_to_str(tcx, tcx.ty(e_base))));
|
||||
}
|
||||
}
|
||||
self.cat_deref(expr, base_cmt, 0)
|
||||
}
|
||||
|
||||
ast::expr_field(base, f_name, _) => {
|
||||
@ -475,7 +463,7 @@ impl &mem_categorization_ctxt {
|
||||
return self.cat_method_ref(expr, expr_ty);
|
||||
}
|
||||
|
||||
let base_cmt = self.cat_autoderef(base);
|
||||
let base_cmt = self.cat_expr(base);
|
||||
self.cat_field(expr, base_cmt, f_name)
|
||||
}
|
||||
|
||||
@ -484,7 +472,8 @@ impl &mem_categorization_ctxt {
|
||||
return self.cat_rvalue(expr, expr_ty);
|
||||
}
|
||||
|
||||
self.cat_index(expr, base)
|
||||
let base_cmt = self.cat_expr(base);
|
||||
self.cat_index(expr, base_cmt)
|
||||
}
|
||||
|
||||
ast::expr_path(_) => {
|
||||
@ -666,11 +655,21 @@ impl &mem_categorization_ctxt {
|
||||
mutbl: m, ty: self.tcx.ty(node)}
|
||||
}
|
||||
|
||||
fn cat_deref<N:ast_node>(node: N, base_cmt: cmt, derefs: uint,
|
||||
expl: bool) -> Option<cmt> {
|
||||
do ty::deref(self.tcx, base_cmt.ty, expl).map |mt| {
|
||||
match deref_kind(self.tcx, base_cmt.ty) {
|
||||
deref_ptr(ptr) => {
|
||||
fn cat_deref<N:ast_node>(node: N,
|
||||
base_cmt: cmt,
|
||||
deref_cnt: uint) -> cmt {
|
||||
let mt = match ty::deref(self.tcx, base_cmt.ty, true) {
|
||||
Some(mt) => mt,
|
||||
None => {
|
||||
self.tcx.sess.span_bug(
|
||||
node.span(),
|
||||
fmt!("Explicit deref of non-derefable type: %s",
|
||||
ty_to_str(self.tcx, base_cmt.ty)));
|
||||
}
|
||||
};
|
||||
|
||||
match deref_kind(self.tcx, base_cmt.ty) {
|
||||
deref_ptr(ptr) => {
|
||||
let lp = do base_cmt.lp.chain |l| {
|
||||
// Given that the ptr itself is loanable, we can
|
||||
// loan out deref'd uniq ptrs as the data they are
|
||||
@ -678,41 +677,38 @@ impl &mem_categorization_ctxt {
|
||||
// Other ptr types admit aliases and are therefore
|
||||
// not loanable.
|
||||
match ptr {
|
||||
uniq_ptr => {Some(@lp_deref(l, ptr))}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {None}
|
||||
uniq_ptr => {Some(@lp_deref(l, ptr))}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {None}
|
||||
}
|
||||
};
|
||||
|
||||
// for unique ptrs, we inherit mutability from the
|
||||
// owning reference.
|
||||
let m = match ptr {
|
||||
uniq_ptr => {
|
||||
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
|
||||
}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {
|
||||
mt.mutbl
|
||||
}
|
||||
uniq_ptr => {
|
||||
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
|
||||
}
|
||||
gc_ptr | region_ptr(_) | unsafe_ptr => {
|
||||
mt.mutbl
|
||||
}
|
||||
};
|
||||
|
||||
@{id:node.id(), span:node.span(),
|
||||
cat:cat_deref(base_cmt, derefs, ptr), lp:lp,
|
||||
cat:cat_deref(base_cmt, deref_cnt, ptr), lp:lp,
|
||||
mutbl:m, ty:mt.ty}
|
||||
}
|
||||
}
|
||||
|
||||
deref_comp(comp) => {
|
||||
deref_comp(comp) => {
|
||||
let lp = base_cmt.lp.map(|l| @lp_comp(l, comp) );
|
||||
let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
|
||||
@{id:node.id(), span:node.span(),
|
||||
cat:cat_comp(base_cmt, comp), lp:lp,
|
||||
mutbl:m, ty:mt.ty}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_index(expr: @ast::expr, base: @ast::expr) -> cmt {
|
||||
let base_cmt = self.cat_autoderef(base);
|
||||
|
||||
fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt {
|
||||
let mt = match ty::index(self.tcx, base_cmt.ty) {
|
||||
Some(mt) => mt,
|
||||
None => {
|
||||
@ -781,25 +777,6 @@ impl &mem_categorization_ctxt {
|
||||
mutbl:m_imm, ty:expr_ty}
|
||||
}
|
||||
|
||||
fn cat_autoderef(base: @ast::expr) -> cmt {
|
||||
// Creates a string of implicit derefences so long as base is
|
||||
// dereferencable. n.b., it is important that these dereferences are
|
||||
// associated with the field/index that caused the autoderef (expr).
|
||||
// This is used later to adjust ref counts and so forth in trans.
|
||||
|
||||
// Given something like base.f where base has type @m1 @m2 T, we want
|
||||
// to yield the equivalent categories to (**base).f.
|
||||
let mut cmt = self.cat_expr(base);
|
||||
let mut ctr = 0u;
|
||||
loop {
|
||||
ctr += 1u;
|
||||
match self.cat_deref(base, cmt, ctr, false) {
|
||||
None => return cmt,
|
||||
Some(cmt1) => cmt = cmt1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
|
||||
|
||||
op(cmt, pat);
|
||||
@ -900,15 +877,9 @@ impl &mem_categorization_ctxt {
|
||||
|
||||
ast::pat_box(subpat) | ast::pat_uniq(subpat) |
|
||||
ast::pat_region(subpat) => {
|
||||
// @p1, ~p1, &p1
|
||||
match self.cat_deref(subpat, cmt, 0u, true) {
|
||||
Some(subcmt) => {
|
||||
self.cat_pattern(subcmt, subpat, op);
|
||||
}
|
||||
None => {
|
||||
tcx.sess.span_bug(pat.span, ~"Non derefable type");
|
||||
}
|
||||
}
|
||||
// @p1, ~p1
|
||||
let subcmt = self.cat_deref(subpat, cmt, 0);
|
||||
self.cat_pattern(subcmt, subpat, op);
|
||||
}
|
||||
|
||||
ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ }
|
||||
|
@ -500,7 +500,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
|
||||
|
||||
let Result {bcx: guard_cx, val} = {
|
||||
do with_scope_result(bcx, e.info(), ~"guard") |bcx| {
|
||||
expr::trans_to_appropriate_llval(bcx, e)
|
||||
expr::trans_to_datum(bcx, e).to_result()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -453,8 +453,7 @@ fn trans_args(cx: block, llenv: ValueRef, args: CallArgs, fn_ty: ty::t,
|
||||
do vec::iteri(arg_exprs) |i, arg_expr| {
|
||||
let arg_val = unpack_result!(bcx, {
|
||||
trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups,
|
||||
if i == last { ret_flag } else { None },
|
||||
0u)
|
||||
if i == last { ret_flag } else { None })
|
||||
});
|
||||
vec::push(llargs, arg_val);
|
||||
}
|
||||
@ -480,18 +479,17 @@ fn trans_arg_expr(bcx: block,
|
||||
formal_ty: ty::arg,
|
||||
arg_expr: @ast::expr,
|
||||
temp_cleanups: &mut ~[ValueRef],
|
||||
ret_flag: Option<ValueRef>,
|
||||
derefs: uint)
|
||||
ret_flag: Option<ValueRef>)
|
||||
-> Result
|
||||
{
|
||||
let _icx = bcx.insn_ctxt("trans_arg_expr");
|
||||
let ccx = bcx.ccx();
|
||||
|
||||
debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \
|
||||
ret_flag=%?, derefs=%?)",
|
||||
ret_flag=%?)",
|
||||
formal_ty.mode, bcx.ty_to_str(formal_ty.ty),
|
||||
bcx.expr_to_str(arg_expr),
|
||||
ret_flag.map(|v| bcx.val_str(v)), derefs);
|
||||
ret_flag.map(|v| bcx.val_str(v)));
|
||||
let _indenter = indenter();
|
||||
|
||||
// translate the arg expr to a datum
|
||||
@ -528,20 +526,7 @@ fn trans_arg_expr(bcx: block,
|
||||
let mut arg_datum = arg_datumblock.datum;
|
||||
let mut bcx = arg_datumblock.bcx;
|
||||
|
||||
debug!(" initial value: %s", arg_datum.to_str(bcx.ccx()));
|
||||
|
||||
// auto-deref value as required (this only applies to method
|
||||
// call receivers) of method
|
||||
if derefs != 0 {
|
||||
arg_datum = arg_datum.autoderef(bcx, arg_expr.id, derefs);
|
||||
debug!(" deref'd value: %s", arg_datum.to_str(bcx.ccx()));
|
||||
};
|
||||
|
||||
// borrow value (convert from @T to &T and so forth)
|
||||
let arg_datum = unpack_datum!(bcx, {
|
||||
adapt_borrowed_value(bcx, arg_datum, arg_expr)
|
||||
});
|
||||
debug!(" borrowed value: %s", arg_datum.to_str(bcx.ccx()));
|
||||
debug!(" arg datum: %s", arg_datum.to_str(bcx.ccx()));
|
||||
|
||||
// finally, deal with the various modes
|
||||
let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode);
|
||||
@ -601,74 +586,3 @@ fn trans_arg_expr(bcx: block,
|
||||
return rslt(bcx, val);
|
||||
}
|
||||
|
||||
// when invoking a method, an argument of type @T or ~T can be implicltly
|
||||
// converted to an argument of type &T. Similarly, ~[T] can be converted to
|
||||
// &[T] and so on. If such a conversion (called borrowing) is necessary,
|
||||
// then the borrowings table will have an appropriate entry inserted. This
|
||||
// routine consults this table and performs these adaptations. It returns a
|
||||
// new location for the borrowed result as well as a new type for the argument
|
||||
// that reflects the borrowed value and not the original.
|
||||
fn adapt_borrowed_value(bcx: block,
|
||||
datum: Datum,
|
||||
expr: @ast::expr) -> DatumBlock
|
||||
{
|
||||
if !expr_is_borrowed(bcx, expr) {
|
||||
return DatumBlock {bcx: bcx, datum: datum};
|
||||
}
|
||||
|
||||
debug!("adapt_borrowed_value(datum=%s, expr=%s)",
|
||||
datum.to_str(bcx.ccx()),
|
||||
bcx.expr_to_str(expr));
|
||||
|
||||
match ty::get(datum.ty).sty {
|
||||
ty::ty_uniq(_) | ty::ty_box(_) => {
|
||||
let body_datum = datum.box_body(bcx);
|
||||
let rptr_datum = body_datum.to_rptr(bcx);
|
||||
return DatumBlock {bcx: bcx, datum: rptr_datum};
|
||||
}
|
||||
|
||||
ty::ty_estr(_) | ty::ty_evec(_, _) => {
|
||||
let ccx = bcx.ccx();
|
||||
let val = datum.to_appropriate_llval(bcx);
|
||||
|
||||
let unit_ty = ty::sequence_element_type(ccx.tcx, datum.ty);
|
||||
let llunit_ty = type_of::type_of(ccx, unit_ty);
|
||||
let (base, len) = datum.get_base_and_len(bcx);
|
||||
let p = alloca(bcx, T_struct(~[T_ptr(llunit_ty), ccx.int_type]));
|
||||
|
||||
debug!("adapt_borrowed_value: adapting %s to %s",
|
||||
val_str(bcx.ccx().tn, val),
|
||||
val_str(bcx.ccx().tn, p));
|
||||
|
||||
Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base]));
|
||||
Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
|
||||
|
||||
// this isn't necessarily the type that rust would assign
|
||||
// but it's close enough for trans purposes, as it will
|
||||
// have the same runtime representation
|
||||
let slice_ty = ty::mk_evec(bcx.tcx(),
|
||||
{ty: unit_ty, mutbl: ast::m_imm},
|
||||
ty::vstore_slice(ty::re_static));
|
||||
|
||||
return DatumBlock {bcx: bcx,
|
||||
datum: Datum {val: p,
|
||||
mode: ByRef,
|
||||
ty: slice_ty,
|
||||
source: FromRvalue}};
|
||||
}
|
||||
|
||||
_ => {
|
||||
// Just take a reference. This is basically like trans_addr_of.
|
||||
//
|
||||
// NDM---this code is almost certainly wrong. I presume its
|
||||
// purpose is auto-ref? What if an @T is autoref'd? No good.
|
||||
let rptr_datum = datum.to_rptr(bcx);
|
||||
return DatumBlock {bcx: bcx, datum: rptr_datum};
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool {
|
||||
bcx.tcx().borrowings.contains_key(e.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,7 +604,7 @@ impl block {
|
||||
}
|
||||
|
||||
fn expr_to_str(e: @ast::expr) -> ~str {
|
||||
fmt!("expr(%d: %s)", e.id, expr_to_str(e, self.sess().intr()))
|
||||
util::ppaux::expr_repr(self.tcx(), e)
|
||||
}
|
||||
|
||||
fn expr_is_lval(e: @ast::expr) -> bool {
|
||||
@ -1180,13 +1180,17 @@ fn path_str(sess: session::session, p: path) -> ~str {
|
||||
r
|
||||
}
|
||||
|
||||
fn monomorphize_type(bcx: block, t: ty::t) -> ty::t {
|
||||
match bcx.fcx.param_substs {
|
||||
Some(substs) => ty::subst_tps(bcx.tcx(), substs.tys, t),
|
||||
_ => { assert !ty::type_has_params(t); t }
|
||||
}
|
||||
}
|
||||
|
||||
fn node_id_type(bcx: block, id: ast::node_id) -> ty::t {
|
||||
let tcx = bcx.tcx();
|
||||
let t = ty::node_id_to_type(tcx, id);
|
||||
match bcx.fcx.param_substs {
|
||||
Some(substs) => ty::subst_tps(tcx, substs.tys, t),
|
||||
_ => { assert !ty::type_has_params(t); t }
|
||||
}
|
||||
monomorphize_type(bcx, t)
|
||||
}
|
||||
|
||||
fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {
|
||||
|
@ -312,10 +312,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
|
||||
let (v, _, _) = const_vec(cx, e, es);
|
||||
v
|
||||
}
|
||||
ast::expr_vstore(e, ast::vstore_fixed(_)) => {
|
||||
ast::expr_vstore(e, ast::expr_vstore_fixed(_)) => {
|
||||
const_expr(cx, e)
|
||||
}
|
||||
ast::expr_vstore(sub, ast::vstore_slice(_)) => {
|
||||
ast::expr_vstore(sub, ast::expr_vstore_slice) => {
|
||||
match sub.node {
|
||||
ast::expr_lit(lit) => {
|
||||
match lit.node {
|
||||
|
@ -41,7 +41,7 @@ fn trans_if(bcx: block,
|
||||
|
||||
let _icx = bcx.insn_ctxt("trans_if");
|
||||
let Result {bcx, val: cond_val} =
|
||||
expr::trans_to_appropriate_llval(bcx, cond);
|
||||
expr::trans_to_datum(bcx, cond).to_result();
|
||||
|
||||
let then_bcx_in = scope_block(bcx, thn.info(), ~"then");
|
||||
let else_bcx_in = scope_block(bcx, els.info(), ~"else");
|
||||
@ -121,7 +121,7 @@ fn trans_while(bcx: block, cond: @ast::expr, body: ast::blk)
|
||||
|
||||
// compile the condition
|
||||
let Result {bcx: cond_bcx_out, val: cond_val} =
|
||||
expr::trans_to_appropriate_llval(cond_bcx_in, cond);
|
||||
expr::trans_to_datum(cond_bcx_in, cond).to_result();
|
||||
let cond_bcx_out =
|
||||
trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
|
||||
CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
|
||||
@ -179,7 +179,7 @@ fn trans_log(log_ex: @ast::expr,
|
||||
let current_level = Load(bcx, global);
|
||||
let level = unpack_result!(bcx, {
|
||||
do with_scope_result(bcx, lvl.info(), ~"level") |bcx| {
|
||||
expr::trans_to_appropriate_llval(bcx, lvl)
|
||||
expr::trans_to_datum(bcx, lvl).to_result()
|
||||
}
|
||||
});
|
||||
|
||||
@ -278,7 +278,7 @@ fn trans_check_expr(bcx: block, chk_expr: @ast::expr,
|
||||
+ ~" failed";
|
||||
let Result {bcx, val} = {
|
||||
do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| {
|
||||
expr::trans_to_appropriate_llval(bcx, pred_expr)
|
||||
expr::trans_to_datum(bcx, pred_expr).to_result()
|
||||
}
|
||||
};
|
||||
do with_cond(bcx, Not(bcx, val)) |bcx| {
|
||||
|
@ -174,8 +174,12 @@ fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
|
||||
/*!
|
||||
*
|
||||
* Allocates temporary space on the stack using alloca() and
|
||||
* returns a by-ref Datum pointing to it. You must arrange
|
||||
* any cleanups etc yourself! */
|
||||
* returns a by-ref Datum pointing to it. If `zero` is true, the
|
||||
* space will be zeroed when it is allocated; this is normally not
|
||||
* necessary, but in the case of automatic rooting in match
|
||||
* statements it is possible to have temporaries that may not get
|
||||
* initialized if a certain arm is not taken, so we must zero
|
||||
* them. You must arrange any cleanups etc yourself! */
|
||||
|
||||
let llty = type_of::type_of(bcx.ccx(), ty);
|
||||
let scratch = alloca_maybe_zeroed(bcx, llty, zero);
|
||||
|
@ -35,9 +35,6 @@ The two functions above are the most general and can handle any
|
||||
situation, but there are a few other functions that are useful
|
||||
in specific scenarios:
|
||||
|
||||
- `trans_to_appropriate_llval()` can be used when you just want
|
||||
an LLVM ValueRef. It will return by value if the value in
|
||||
question is immediate, or by ref otherwise.
|
||||
- `trans_lvalue()` is exactly like `trans_to_datum()` but it only
|
||||
works on lvalues. This is mostly used as an assertion for those
|
||||
places where only an lvalue is expected. It also guarantees that
|
||||
@ -57,10 +54,10 @@ If you invoke `trans_into()`, no cleanup is scheduled for you. The
|
||||
value is written into the given destination and is assumed to be owned
|
||||
by that destination.
|
||||
|
||||
When you invoke `trans_to_datum()` or `trans_to_appropriate_llval()`
|
||||
on an rvalue, the resulting datum/value will have an appropriate
|
||||
cleanup scheduled for the innermost cleanup scope. If you later use
|
||||
`move_to()` or `drop_val()`, this cleanup will be canceled.
|
||||
When you invoke `trans_to_datum()` on an rvalue, the resulting
|
||||
datum/value will have an appropriate cleanup scheduled for the
|
||||
innermost cleanup scope. If you later use `move_to()` or
|
||||
`drop_val()`, this cleanup will be canceled.
|
||||
|
||||
During the evaluation of an expression, temporary cleanups are created
|
||||
and later canceled. These represent intermediate or partial results
|
||||
@ -112,21 +109,22 @@ use base::*;
|
||||
use syntax::print::pprust::{expr_to_str};
|
||||
use util::ppaux::ty_to_str;
|
||||
use util::common::indenter;
|
||||
use ty::{AutoPtr, AutoSlice};
|
||||
|
||||
// The primary two functions for translating expressions:
|
||||
export trans_to_datum, trans_into;
|
||||
|
||||
// More specific variants than trans_to_datum/trans_into that are useful
|
||||
// in some scenarios:
|
||||
export trans_local_var;
|
||||
|
||||
// Other helpers, types, and so forth:
|
||||
export with_field_tys;
|
||||
export Dest, SaveIn, Ignore;
|
||||
export cast_type_kind;
|
||||
export cast_kind, cast_pointer, cast_integral, cast_float;
|
||||
export cast_enum, cast_other;
|
||||
|
||||
// More specific variants than trans_to_datum/trans_into that are useful
|
||||
// in some scenarios:
|
||||
export trans_to_appropriate_llval, trans_lvalue, trans_local_var;
|
||||
|
||||
// Other helpers:
|
||||
export with_field_tys;
|
||||
|
||||
// Destinations
|
||||
|
||||
// These are passed around by the code generating functions to track the
|
||||
@ -160,15 +158,98 @@ impl Dest : cmp::Eq {
|
||||
pure fn ne(&&other: Dest) -> bool { !self.eq(other) }
|
||||
}
|
||||
|
||||
fn trans_to_appropriate_llval(bcx: block,
|
||||
expr: @ast::expr) -> common::Result {
|
||||
let mut bcx = bcx;
|
||||
let datum = unpack_datum!(bcx, trans_to_datum(bcx, expr));
|
||||
debug!("trans_to_appropriate_llval(): datum=%s", datum.to_str(bcx.ccx()));
|
||||
rslt(bcx, datum.to_appropriate_llval(bcx))
|
||||
fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
|
||||
|
||||
return match bcx.tcx().adjustments.find(expr.id) {
|
||||
None => {
|
||||
trans_to_datum_unadjusted(bcx, expr)
|
||||
}
|
||||
Some(adj) => {
|
||||
let mut bcx = bcx;
|
||||
let mut datum = unpack_datum!(bcx, {
|
||||
trans_to_datum_unadjusted(bcx, expr)
|
||||
});
|
||||
|
||||
if adj.autoderefs > 0 {
|
||||
datum = datum.autoderef(bcx, expr.id, adj.autoderefs);
|
||||
}
|
||||
|
||||
datum = match adj.autoref {
|
||||
None => datum,
|
||||
Some(ref autoref) => {
|
||||
match autoref.kind {
|
||||
AutoPtr => {
|
||||
unpack_datum!(bcx, auto_ref(bcx, datum))
|
||||
}
|
||||
AutoSlice => {
|
||||
unpack_datum!(bcx, auto_slice(bcx, datum))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
debug!("after adjustments, datum=%s", datum.to_str(bcx.ccx()));
|
||||
|
||||
return DatumBlock {bcx: bcx, datum: datum};
|
||||
}
|
||||
};
|
||||
|
||||
fn auto_ref(bcx: block, datum: Datum) -> DatumBlock {
|
||||
DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)}
|
||||
}
|
||||
|
||||
fn auto_slice(bcx: block, datum: Datum) -> DatumBlock {
|
||||
// This is not the most efficient thing possible; since slices
|
||||
// are two words it'd be better if this were compiled in
|
||||
// 'dest' mode, but I can't find a nice way to structure the
|
||||
// code and keep it DRY that accommodates that use case at the
|
||||
// moment.
|
||||
|
||||
let tcx = bcx.tcx();
|
||||
let unit_ty = ty::sequence_element_type(tcx, datum.ty);
|
||||
let (base, len) = datum.get_base_and_len(bcx);
|
||||
|
||||
// this type may have a different region/mutability than the
|
||||
// real one, but it will have the same runtime representation
|
||||
let slice_ty = ty::mk_evec(tcx, {ty: unit_ty, mutbl: ast::m_imm},
|
||||
ty::vstore_slice(ty::re_static));
|
||||
|
||||
let scratch = scratch_datum(bcx, slice_ty, false);
|
||||
Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
|
||||
Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
|
||||
DatumBlock {bcx: bcx, datum: scratch}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
return match bcx.tcx().adjustments.find(expr.id) {
|
||||
None => trans_into_unadjusted(bcx, expr, dest),
|
||||
Some(_) => {
|
||||
// use trans_to_datum, which is mildly less efficient but
|
||||
// which will perform the adjustments:
|
||||
let datumblock = trans_to_datum(bcx, expr);
|
||||
match dest {
|
||||
Ignore => datumblock.bcx,
|
||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
return match bcx.tcx().adjustments.find(expr.id) {
|
||||
None => trans_lvalue_unadjusted(bcx, expr),
|
||||
Some(_) => {
|
||||
bcx.sess().span_bug(
|
||||
expr.span,
|
||||
fmt!("trans_lvalue() called on an expression \
|
||||
with adjustments"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
/*!
|
||||
*
|
||||
* Translates an expression into a datum. If this expression
|
||||
@ -178,35 +259,38 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
|
||||
let mut bcx = bcx;
|
||||
|
||||
debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
|
||||
debug!("trans_to_datum_unadjusted(expr=%s)", bcx.expr_to_str(expr));
|
||||
let _indenter = indenter();
|
||||
|
||||
debuginfo::update_source_pos(bcx, expr.span);
|
||||
|
||||
match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
|
||||
ty::LvalueExpr => {
|
||||
return trans_lvalue(bcx, expr);
|
||||
return trans_lvalue_unadjusted(bcx, expr);
|
||||
}
|
||||
|
||||
ty::RvalueDatumExpr => {
|
||||
let datum = unpack_datum!(bcx, trans_rvalue_datum(bcx, expr));
|
||||
let datum = unpack_datum!(bcx, {
|
||||
trans_rvalue_datum_unadjusted(bcx, expr)
|
||||
});
|
||||
datum.add_clean(bcx);
|
||||
return DatumBlock {bcx: bcx, datum: datum};
|
||||
}
|
||||
|
||||
ty::RvalueStmtExpr => {
|
||||
bcx = trans_rvalue_stmt(bcx, expr);
|
||||
bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
|
||||
return nil(bcx, expr_ty(bcx, expr));
|
||||
}
|
||||
|
||||
ty::RvalueDpsExpr => {
|
||||
let ty = expr_ty(bcx, expr);
|
||||
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
|
||||
bcx = trans_rvalue_dps(bcx, expr, Ignore);
|
||||
bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
|
||||
return nil(bcx, ty);
|
||||
} else {
|
||||
let scratch = scratch_datum(bcx, ty, false);
|
||||
bcx = trans_rvalue_dps(bcx, expr, SaveIn(scratch.val));
|
||||
bcx = trans_rvalue_dps_unadjusted(
|
||||
bcx, expr, SaveIn(scratch.val));
|
||||
|
||||
// Note: this is not obviously a good idea. It causes
|
||||
// immediate values to be loaded immediately after a
|
||||
@ -231,10 +315,10 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
fn trans_into_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
let ty = expr_ty(bcx, expr);
|
||||
|
||||
debug!("trans_into(expr=%s, dest=%s)",
|
||||
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
|
||||
bcx.expr_to_str(expr),
|
||||
dest.to_str(bcx.ccx()));
|
||||
let _indenter = indenter();
|
||||
@ -253,39 +337,39 @@ fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
debug!("expr kind = %?", kind);
|
||||
match kind {
|
||||
ty::LvalueExpr => {
|
||||
let datumblock = trans_lvalue(bcx, expr);
|
||||
let datumblock = trans_lvalue_unadjusted(bcx, expr);
|
||||
match dest {
|
||||
Ignore => datumblock.bcx,
|
||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
||||
}
|
||||
}
|
||||
ty::RvalueDatumExpr => {
|
||||
let datumblock = trans_rvalue_datum(bcx, expr);
|
||||
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
|
||||
match dest {
|
||||
Ignore => datumblock.drop_val(),
|
||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
||||
}
|
||||
}
|
||||
ty::RvalueDpsExpr => {
|
||||
return trans_rvalue_dps(bcx, expr, dest);
|
||||
return trans_rvalue_dps_unadjusted(bcx, expr, dest);
|
||||
}
|
||||
ty::RvalueStmtExpr => {
|
||||
return trans_rvalue_stmt(bcx, expr);
|
||||
return trans_rvalue_stmt_unadjusted(bcx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
let _icx = bcx.insn_ctxt("trans_rvalue_datum");
|
||||
fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
|
||||
|
||||
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
|
||||
|
||||
match expr.node {
|
||||
ast::expr_vstore(contents, ast::vstore_box) => {
|
||||
ast::expr_vstore(contents, ast::expr_vstore_box) => {
|
||||
return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared,
|
||||
expr, contents);
|
||||
}
|
||||
ast::expr_vstore(contents, ast::vstore_uniq) => {
|
||||
ast::expr_vstore(contents, ast::expr_vstore_uniq) => {
|
||||
return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
|
||||
expr, contents);
|
||||
}
|
||||
@ -310,13 +394,14 @@ fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
_ => {
|
||||
bcx.tcx().sess.span_bug(
|
||||
expr.span,
|
||||
fmt!("trans_rvalue_datum reached fall-through case: %?",
|
||||
fmt!("trans_rvalue_datum_unadjusted reached \
|
||||
fall-through case: %?",
|
||||
expr.node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
|
||||
fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
|
||||
let mut bcx = bcx;
|
||||
let _icx = bcx.insn_ctxt("trans_rvalue_stmt");
|
||||
|
||||
@ -378,22 +463,25 @@ fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
|
||||
_ => {
|
||||
bcx.tcx().sess.span_bug(
|
||||
expr.span,
|
||||
fmt!("trans_rvalue_stmt reached fall-through case: %?",
|
||||
fmt!("trans_rvalue_stmt_unadjusted reached \
|
||||
fall-through case: %?",
|
||||
expr.node));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||
dest: Dest) -> block {
|
||||
let mut bcx = bcx;
|
||||
let _icx = bcx.insn_ctxt("trans_rvalue_dps");
|
||||
let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted");
|
||||
let tcx = bcx.tcx();
|
||||
|
||||
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
|
||||
|
||||
match expr.node {
|
||||
ast::expr_path(_) => {
|
||||
return trans_def_dps(bcx, expr, bcx.def(expr.id), dest);
|
||||
return trans_def_dps_unadjusted(bcx, expr,
|
||||
bcx.def(expr.id), dest);
|
||||
}
|
||||
ast::expr_if(cond, thn, els) => {
|
||||
return controlflow::trans_if(bcx, cond, thn, els, dest);
|
||||
@ -416,10 +504,10 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
ast::expr_lit(@{node: ast::lit_str(s), _}) => {
|
||||
return tvec::trans_lit_str(bcx, expr, s, dest);
|
||||
}
|
||||
ast::expr_vstore(contents, ast::vstore_slice(_)) => {
|
||||
ast::expr_vstore(contents, ast::expr_vstore_slice) => {
|
||||
return tvec::trans_slice_vstore(bcx, expr, contents, dest);
|
||||
}
|
||||
ast::expr_vstore(contents, ast::vstore_fixed(_)) => {
|
||||
ast::expr_vstore(contents, ast::expr_vstore_fixed(_)) => {
|
||||
return tvec::trans_fixed_vstore(bcx, expr, contents, dest);
|
||||
}
|
||||
ast::expr_vec(*) | ast::expr_repeat(*) => {
|
||||
@ -527,15 +615,16 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||
_ => {
|
||||
bcx.tcx().sess.span_bug(
|
||||
expr.span,
|
||||
fmt!("trans_rvalue_dps reached fall-through case: %?",
|
||||
fmt!("trans_rvalue_dps_unadjusted reached \
|
||||
fall-through case: %?",
|
||||
expr.node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
|
||||
def: ast::def, dest: Dest) -> block {
|
||||
let _icx = bcx.insn_ctxt("trans_def_dps");
|
||||
fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
|
||||
def: ast::def, dest: Dest) -> block {
|
||||
let _icx = bcx.insn_ctxt("trans_def_dps_unadjusted");
|
||||
let ccx = bcx.ccx();
|
||||
|
||||
let lldest = match dest {
|
||||
@ -575,13 +664,13 @@ fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
//!
|
||||
//
|
||||
// Translates an lvalue expression, always yielding a by-ref
|
||||
// datum. Generally speaking you should call trans_to_datum()
|
||||
// instead, but sometimes we call trans_lvalue() directly as a
|
||||
// means of asserting that a particular expression is an lvalue.
|
||||
fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||
/*!
|
||||
*
|
||||
* Translates an lvalue expression, always yielding a by-ref
|
||||
* datum. Generally speaking you should call trans_to_datum()
|
||||
* instead, but sometimes we call trans_lvalue() directly as a
|
||||
* means of asserting that a particular expression is an lvalue. */
|
||||
|
||||
let _icx = bcx.insn_ctxt("trans_lval");
|
||||
let mut bcx = bcx;
|
||||
@ -798,12 +887,7 @@ fn trans_rec_field(bcx: block,
|
||||
let mut bcx = bcx;
|
||||
let _icx = bcx.insn_ctxt("trans_rec_field");
|
||||
|
||||
// Translate and autoderef the base expression. We should have a
|
||||
// record or a struct when we're done, both of which are currently
|
||||
// non-immediate and hence always tracked by reference.
|
||||
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
||||
let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
|
||||
|
||||
do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| {
|
||||
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
|
||||
DatumBlock {
|
||||
@ -822,14 +906,11 @@ fn trans_index(bcx: block,
|
||||
let base_ty = expr_ty(bcx, base);
|
||||
let mut bcx = bcx;
|
||||
|
||||
// Translate and autoderef the base expression. We should have some sort
|
||||
// of vector (@[], &[], ~[], []/_, etc) when we're done.
|
||||
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
||||
let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
|
||||
|
||||
// Translate index expression and cast to a suitable LLVM integer.
|
||||
// Rust is less strict than LLVM in this regard.
|
||||
let Result {bcx, val: ix_val} = trans_to_appropriate_llval(bcx, idx);
|
||||
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
|
||||
let ix_size = shape::llsize_of_real(bcx.ccx(), val_ty(ix_val));
|
||||
let int_size = shape::llsize_of_real(bcx.ccx(), ccx.int_type);
|
||||
let ix_val = {
|
||||
@ -989,11 +1070,11 @@ fn trans_unary_datum(bcx: block,
|
||||
|
||||
return match op {
|
||||
ast::not => {
|
||||
let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
|
||||
let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
|
||||
immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty)
|
||||
}
|
||||
ast::neg => {
|
||||
let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
|
||||
let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
|
||||
let llneg = {
|
||||
if ty::type_is_fp(un_ty) {
|
||||
FNeg(bcx, val)
|
||||
@ -1153,7 +1234,7 @@ fn trans_lazy_binop(bcx: block,
|
||||
|
||||
let Result {bcx: past_lhs, val: lhs} = {
|
||||
do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
|
||||
trans_to_appropriate_llval(bcx, a)
|
||||
trans_to_datum(bcx, a).to_result()
|
||||
}
|
||||
};
|
||||
|
||||
@ -1170,7 +1251,7 @@ fn trans_lazy_binop(bcx: block,
|
||||
}
|
||||
let Result {bcx: past_rhs, val: rhs} = {
|
||||
do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| {
|
||||
trans_to_appropriate_llval(bcx, b)
|
||||
trans_to_datum(bcx, b).to_result()
|
||||
}
|
||||
};
|
||||
|
||||
@ -1299,7 +1380,7 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
|
||||
let t_out = node_id_type(bcx, id);
|
||||
|
||||
let mut bcx = bcx;
|
||||
let llexpr = unpack_result!(bcx, trans_to_appropriate_llval(bcx, expr));
|
||||
let llexpr = unpack_result!(bcx, trans_to_datum(bcx, expr).to_result());
|
||||
let ll_t_in = val_ty(llexpr);
|
||||
let t_in = expr_ty(bcx, expr);
|
||||
let ll_t_out = type_of::type_of(ccx, t_out);
|
||||
|
@ -95,18 +95,17 @@ fn trans_method(ccx: @crate_ctxt,
|
||||
fn trans_self_arg(bcx: block, base: @ast::expr,
|
||||
mentry: typeck::method_map_entry) -> Result {
|
||||
let _icx = bcx.insn_ctxt("impl::trans_self_arg");
|
||||
let basety = expr_ty(bcx, base);
|
||||
let mode = ast::expl(mentry.self_mode);
|
||||
let mut temp_cleanups = ~[];
|
||||
let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base,
|
||||
&mut temp_cleanups, None, mentry.derefs);
|
||||
let self_arg = {mode: mentry.self_arg.mode,
|
||||
ty: monomorphize_type(bcx, mentry.self_arg.ty)};
|
||||
let result = trans_arg_expr(bcx, self_arg, base,
|
||||
&mut temp_cleanups, None);
|
||||
|
||||
// by-ref self argument should not require cleanup in the case of
|
||||
// other arguments failing:
|
||||
//assert temp_cleanups == ~[];
|
||||
//do vec::iter(temp_cleanups) |c| {
|
||||
// revoke_clean(bcx, c)
|
||||
//}
|
||||
// FIXME(#3446)---this is wrong, actually. The temp_cleanups
|
||||
// should be revoked only after all arguments have been passed.
|
||||
for temp_cleanups.each |c| {
|
||||
revoke_clean(bcx, c)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -120,14 +119,14 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
|
||||
typeck::method_static(did) => {
|
||||
let callee_fn = callee::trans_fn_ref(bcx, did, callee_id);
|
||||
let Result {bcx, val} = trans_self_arg(bcx, self, mentry);
|
||||
|
||||
let tcx = bcx.tcx();
|
||||
Callee {
|
||||
bcx: bcx,
|
||||
data: Method(MethodData {
|
||||
llfn: callee_fn.llfn,
|
||||
llself: val,
|
||||
self_ty: node_id_type(bcx, self.id),
|
||||
self_mode: mentry.self_mode
|
||||
self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -144,7 +143,7 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
|
||||
}
|
||||
}
|
||||
typeck::method_trait(_, off) => {
|
||||
trans_trait_callee(bcx, callee_id, off, self, mentry.derefs)
|
||||
trans_trait_callee(bcx, callee_id, off, self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,7 +175,9 @@ fn trans_static_method_callee(bcx: block,
|
||||
let vtbls = resolve_vtables_in_fn_ctxt(
|
||||
bcx.fcx, ccx.maps.vtable_map.get(callee_id));
|
||||
|
||||
match vtbls[0] { // is index 0 always the one we want?
|
||||
// FIXME(#3446) -- I am pretty sure index 0 is not the right one,
|
||||
// if the static method is implemented on a generic type. (NDM)
|
||||
match vtbls[0] {
|
||||
typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
|
||||
|
||||
let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
|
||||
@ -276,18 +277,19 @@ fn trans_monomorphized_callee(bcx: block,
|
||||
let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty);
|
||||
|
||||
// combine the self environment with the rest
|
||||
let tcx = bcx.tcx();
|
||||
Callee {
|
||||
bcx: bcx,
|
||||
data: Method(MethodData {
|
||||
llfn: llfn_val,
|
||||
llself: llself_val,
|
||||
self_ty: node_id_type(bcx, base.id),
|
||||
self_mode: mentry.self_mode
|
||||
self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
|
||||
})
|
||||
}
|
||||
}
|
||||
typeck::vtable_trait(*) => {
|
||||
trans_trait_callee(bcx, callee_id, n_method, base, mentry.derefs)
|
||||
trans_trait_callee(bcx, callee_id, n_method, base)
|
||||
}
|
||||
typeck::vtable_param(*) => {
|
||||
fail ~"vtable_param left in monomorphized function's vtable substs";
|
||||
@ -388,8 +390,7 @@ fn combine_impl_and_methods_origins(bcx: block,
|
||||
fn trans_trait_callee(bcx: block,
|
||||
callee_id: ast::node_id,
|
||||
n_method: uint,
|
||||
self_expr: @ast::expr,
|
||||
autoderefs: uint)
|
||||
self_expr: @ast::expr)
|
||||
-> Callee
|
||||
{
|
||||
//!
|
||||
@ -404,7 +405,6 @@ fn trans_trait_callee(bcx: block,
|
||||
let _icx = bcx.insn_ctxt("impl::trans_trait_callee");
|
||||
let mut bcx = bcx;
|
||||
let self_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr));
|
||||
let self_datum = self_datum.autoderef(bcx, self_expr.id, autoderefs);
|
||||
let llpair = self_datum.to_ref_llval(bcx);
|
||||
let callee_ty = node_id_type(bcx, callee_id);
|
||||
trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair)
|
||||
|
@ -19,7 +19,9 @@ use syntax::ast::*;
|
||||
use syntax::print::pprust::*;
|
||||
use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
|
||||
use std::serialization::{serialize_Option,
|
||||
deserialize_Option};
|
||||
deserialize_Option,
|
||||
serialize_uint,
|
||||
deserialize_uint};
|
||||
|
||||
export TyVid, IntVid, FnVid, RegionVid, vid;
|
||||
export br_hashmap;
|
||||
@ -155,6 +157,7 @@ export closure_kind;
|
||||
export ck_block;
|
||||
export ck_box;
|
||||
export ck_uniq;
|
||||
export param_ty;
|
||||
export param_bound, param_bounds, bound_copy, bound_owned;
|
||||
export param_bounds_to_str, param_bound_to_str;
|
||||
export bound_send, bound_trait;
|
||||
@ -192,6 +195,8 @@ export opt_region_variance;
|
||||
export serialize_opt_region_variance, deserialize_opt_region_variance;
|
||||
export determine_inherited_purity;
|
||||
export provided_trait_methods;
|
||||
export AutoAdjustment, serialize_AutoAdjustment, deserialize_AutoAdjustment;
|
||||
export AutoRef, AutoRefKind, AutoSlice, AutoPtr;
|
||||
|
||||
// Data types
|
||||
|
||||
@ -287,19 +292,26 @@ impl region_variance: cmp::Eq {
|
||||
pure fn ne(&&other: region_variance) -> bool { !self.eq(other) }
|
||||
}
|
||||
|
||||
// N.B.: Borrows from inlined content are not accurately deserialized. This
|
||||
// is because we don't need the details in trans, we only care if there is an
|
||||
// entry in the table or not.
|
||||
type borrow = {
|
||||
region: ty::region,
|
||||
#[auto_serialize]
|
||||
type AutoAdjustment = {
|
||||
autoderefs: uint,
|
||||
autoref: Option<AutoRef>
|
||||
};
|
||||
|
||||
#[auto_serialize]
|
||||
type AutoRef = {
|
||||
kind: AutoRefKind,
|
||||
region: region,
|
||||
mutbl: ast::mutability
|
||||
};
|
||||
|
||||
impl borrow : cmp::Eq {
|
||||
pure fn eq(&&other: borrow) -> bool {
|
||||
self.region == other.region && self.mutbl == other.mutbl
|
||||
}
|
||||
pure fn ne(&&other: borrow) -> bool { !self.eq(other) }
|
||||
#[auto_serialize]
|
||||
enum AutoRefKind {
|
||||
/// Convert from @[]/~[] to &[] (or str)
|
||||
AutoSlice,
|
||||
|
||||
/// Convert from T to &T
|
||||
AutoPtr
|
||||
}
|
||||
|
||||
type ctxt =
|
||||
@ -340,8 +352,7 @@ type ctxt =
|
||||
trait_method_cache: HashMap<def_id, @~[method]>,
|
||||
ty_param_bounds: HashMap<ast::node_id, param_bounds>,
|
||||
inferred_modes: HashMap<ast::node_id, ast::mode>,
|
||||
// maps the id of borrowed expr to scope of borrowed ptr
|
||||
borrowings: HashMap<ast::node_id, borrow>,
|
||||
adjustments: HashMap<ast::node_id, @AutoAdjustment>,
|
||||
normalized_cache: HashMap<t, t>,
|
||||
lang_items: middle::lang_items::LanguageItems};
|
||||
|
||||
@ -496,6 +507,7 @@ impl param_ty : to_bytes::IterBytes {
|
||||
|
||||
|
||||
/// Representation of regions:
|
||||
#[auto_serialize]
|
||||
enum region {
|
||||
/// Bound regions are found (primarily) in function types. They indicate
|
||||
/// region parameters that have yet to be replaced with actual regions
|
||||
@ -523,6 +535,7 @@ enum region {
|
||||
re_var(RegionVid)
|
||||
}
|
||||
|
||||
#[auto_serialize]
|
||||
enum bound_region {
|
||||
/// The self region for classes, impls (&T in a type defn or &self/T)
|
||||
br_self,
|
||||
@ -533,35 +546,37 @@ enum bound_region {
|
||||
/// Named region parameters for functions (a in &a/T)
|
||||
br_named(ast::ident),
|
||||
|
||||
/// Handles capture-avoiding substitution in a rather subtle case. If you
|
||||
/// have a closure whose argument types are being inferred based on the
|
||||
/// expected type, and the expected type includes bound regions, then we
|
||||
/// will wrap those bound regions in a br_cap_avoid() with the id of the
|
||||
/// fn expression. This ensures that the names are not "captured" by the
|
||||
/// enclosing scope, which may define the same names. For an example of
|
||||
/// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs
|
||||
/// and regions-ret-borrowed-1.rs.
|
||||
/**
|
||||
* Handles capture-avoiding substitution in a rather subtle case. If you
|
||||
* have a closure whose argument types are being inferred based on the
|
||||
* expected type, and the expected type includes bound regions, then we
|
||||
* will wrap those bound regions in a br_cap_avoid() with the id of the
|
||||
* fn expression. This ensures that the names are not "captured" by the
|
||||
* enclosing scope, which may define the same names. For an example of
|
||||
* where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs
|
||||
* and regions-ret-borrowed-1.rs. */
|
||||
br_cap_avoid(ast::node_id, @bound_region),
|
||||
}
|
||||
|
||||
type opt_region = Option<region>;
|
||||
|
||||
/// The type substs represents the kinds of things that can be substituted to
|
||||
/// convert a polytype into a monotype. Note however that substituting bound
|
||||
/// regions other than `self` is done through a different mechanism.
|
||||
///
|
||||
/// `tps` represents the type parameters in scope. They are indexed according
|
||||
/// to the order in which they were declared.
|
||||
///
|
||||
/// `self_r` indicates the region parameter `self` that is present on nominal
|
||||
/// types (enums, classes) declared as having a region parameter. `self_r`
|
||||
/// should always be none for types that are not region-parameterized and
|
||||
/// Some(_) for types that are. The only bound region parameter that should
|
||||
/// appear within a region-parameterized type is `self`.
|
||||
///
|
||||
/// `self_ty` is the type to which `self` should be remapped, if any. The
|
||||
/// `self` type is rather funny in that it can only appear on traits and
|
||||
/// is always substituted away to the implementing type for a trait.
|
||||
/**
|
||||
* The type substs represents the kinds of things that can be substituted to
|
||||
* convert a polytype into a monotype. Note however that substituting bound
|
||||
* regions other than `self` is done through a different mechanism:
|
||||
*
|
||||
* - `tps` represents the type parameters in scope. They are indexed
|
||||
* according to the order in which they were declared.
|
||||
*
|
||||
* - `self_r` indicates the region parameter `self` that is present on nominal
|
||||
* types (enums, classes) declared as having a region parameter. `self_r`
|
||||
* should always be none for types that are not region-parameterized and
|
||||
* Some(_) for types that are. The only bound region parameter that should
|
||||
* appear within a region-parameterized type is `self`.
|
||||
*
|
||||
* - `self_ty` is the type to which `self` should be remapped, if any. The
|
||||
* `self` type is rather funny in that it can only appear on traits and is
|
||||
* always substituted away to the implementing type for a trait. */
|
||||
type substs = {
|
||||
self_r: opt_region,
|
||||
self_ty: Option<ty::t>,
|
||||
@ -650,6 +665,7 @@ enum param_bound {
|
||||
enum TyVid = uint;
|
||||
enum IntVid = uint;
|
||||
enum FnVid = uint;
|
||||
#[auto_serialize]
|
||||
enum RegionVid = uint;
|
||||
|
||||
enum InferTy {
|
||||
@ -842,7 +858,7 @@ fn mk_ctxt(s: session::session,
|
||||
trait_method_cache: new_def_hash(),
|
||||
ty_param_bounds: map::int_hash(),
|
||||
inferred_modes: map::int_hash(),
|
||||
borrowings: map::int_hash(),
|
||||
adjustments: map::int_hash(),
|
||||
normalized_cache: new_ty_hash(),
|
||||
lang_items: move lang_items}
|
||||
}
|
||||
@ -1339,7 +1355,7 @@ fn substs_to_str(cx: ctxt, substs: &substs) -> ~str {
|
||||
fmt!("substs(self_r=%s, self_ty=%s, tps=%?)",
|
||||
substs.self_r.map_default(~"none", |r| region_to_str(cx, r)),
|
||||
substs.self_ty.map_default(~"none", |t| ty_to_str(cx, t)),
|
||||
substs.tps.map(|t| ty_to_str(cx, t)))
|
||||
tys_to_str(cx, substs.tps))
|
||||
}
|
||||
|
||||
fn param_bound_to_str(cx: ctxt, pb: ¶m_bound) -> ~str {
|
||||
@ -2939,8 +2955,8 @@ fn expr_kind(tcx: ctxt,
|
||||
ast::expr_unary_move(*) |
|
||||
ast::expr_repeat(*) |
|
||||
ast::expr_lit(@{node: lit_str(_), _}) |
|
||||
ast::expr_vstore(_, ast::vstore_slice(_)) |
|
||||
ast::expr_vstore(_, ast::vstore_fixed(_)) |
|
||||
ast::expr_vstore(_, ast::expr_vstore_slice) |
|
||||
ast::expr_vstore(_, ast::expr_vstore_fixed(_)) |
|
||||
ast::expr_vec(*) => {
|
||||
RvalueDpsExpr
|
||||
}
|
||||
@ -2990,8 +3006,8 @@ fn expr_kind(tcx: ctxt,
|
||||
ast::expr_unary(*) |
|
||||
ast::expr_addr_of(*) |
|
||||
ast::expr_binary(*) |
|
||||
ast::expr_vstore(_, ast::vstore_box) |
|
||||
ast::expr_vstore(_, ast::vstore_uniq) => {
|
||||
ast::expr_vstore(_, ast::expr_vstore_box) |
|
||||
ast::expr_vstore(_, ast::expr_vstore_uniq) => {
|
||||
RvalueDatumExpr
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ use std::serialization::{serialize_uint, deserialize_uint};
|
||||
use vec::each;
|
||||
use syntax::print::pprust::*;
|
||||
use util::ppaux::{ty_to_str, tys_to_str, region_to_str,
|
||||
bound_region_to_str, vstore_to_str};
|
||||
bound_region_to_str, vstore_to_str, expr_repr};
|
||||
use util::common::{indent, indenter};
|
||||
use std::list;
|
||||
use list::{List, Nil, Cons};
|
||||
@ -86,7 +86,7 @@ enum method_origin {
|
||||
// method invoked on a type parameter with a bounded trait
|
||||
method_param(method_param),
|
||||
|
||||
// method invoked on a boxed trait
|
||||
// method invoked on a trait instance
|
||||
method_trait(ast::def_id, uint),
|
||||
}
|
||||
|
||||
@ -108,13 +108,10 @@ type method_param = {
|
||||
bound_num: uint
|
||||
};
|
||||
|
||||
#[auto_serialize]
|
||||
type method_map_entry = {
|
||||
// number of derefs that are required on the receiver
|
||||
derefs: uint,
|
||||
|
||||
// the mode by which the self parameter needs to be passed
|
||||
self_mode: ast::rmode,
|
||||
// the type and mode of the self parameter, which is not reflected
|
||||
// in the fn type (FIXME #3446)
|
||||
self_arg: ty::arg,
|
||||
|
||||
// method details being invoked
|
||||
origin: method_origin
|
||||
|
@ -102,7 +102,7 @@ struct inherited {
|
||||
locals: HashMap<ast::node_id, TyVid>,
|
||||
node_types: HashMap<ast::node_id, ty::t>,
|
||||
node_type_substs: HashMap<ast::node_id, ty::substs>,
|
||||
borrowings: HashMap<ast::node_id, ty::borrow>,
|
||||
adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
|
||||
}
|
||||
|
||||
struct fn_ctxt {
|
||||
@ -143,7 +143,7 @@ fn blank_inherited(ccx: @crate_ctxt) -> @inherited {
|
||||
locals: int_hash(),
|
||||
node_types: map::int_hash(),
|
||||
node_type_substs: map::int_hash(),
|
||||
borrowings: map::int_hash()
|
||||
adjustments: map::int_hash()
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,6 +604,7 @@ impl @fn_ctxt {
|
||||
node_id, ty_to_str(self.tcx(), ty), self.tag());
|
||||
self.inh.node_types.insert(node_id, ty);
|
||||
}
|
||||
|
||||
fn write_substs(node_id: ast::node_id, +substs: ty::substs) {
|
||||
if !ty::substs_is_noop(&substs) {
|
||||
debug!("write_substs(%d, %s) in fcx %s",
|
||||
@ -613,12 +614,24 @@ impl @fn_ctxt {
|
||||
self.inh.node_type_substs.insert(node_id, substs);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_ty_substs(node_id: ast::node_id, ty: ty::t,
|
||||
+substs: ty::substs) {
|
||||
let ty = ty::subst(self.tcx(), &substs, ty);
|
||||
self.write_ty(node_id, ty);
|
||||
self.write_substs(node_id, substs);
|
||||
}
|
||||
|
||||
fn write_autoderef_adjustment(node_id: ast::node_id, derefs: uint) {
|
||||
if derefs == 0 { return; }
|
||||
self.write_adjustment(node_id, @{autoderefs: derefs, autoref: None});
|
||||
}
|
||||
|
||||
fn write_adjustment(node_id: ast::node_id, adj: @ty::AutoAdjustment) {
|
||||
debug!("write_adjustment(node_id=%?, adj=%?)", node_id, adj);
|
||||
self.inh.adjustments.insert(node_id, adj);
|
||||
}
|
||||
|
||||
fn write_nil(node_id: ast::node_id) {
|
||||
self.write_ty(node_id, ty::mk_nil(self.tcx()));
|
||||
}
|
||||
@ -630,14 +643,17 @@ impl @fn_ctxt {
|
||||
ast_ty_to_ty(self, self, ast_t)
|
||||
}
|
||||
|
||||
fn expr_to_str(expr: @ast::expr) -> ~str {
|
||||
expr_repr(self.tcx(), expr)
|
||||
}
|
||||
|
||||
fn expr_ty(ex: @ast::expr) -> ty::t {
|
||||
match self.inh.node_types.find(ex.id) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
self.tcx().sess.bug(
|
||||
fmt!("no type for expr %d (%s) in fcx %s",
|
||||
ex.id, expr_to_str(ex, self.ccx.tcx.sess.intr()),
|
||||
self.tag()));
|
||||
fmt!("no type for %s in fcx %s",
|
||||
self.expr_to_str(ex), self.tag()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -691,22 +707,15 @@ impl @fn_ctxt {
|
||||
infer::can_mk_subty(self.infcx(), sub, sup)
|
||||
}
|
||||
|
||||
fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
|
||||
sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> {
|
||||
fn mk_assignty(expr: @ast::expr, sub: ty::t, sup: ty::t)
|
||||
-> Result<(), ty::type_err>
|
||||
{
|
||||
match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) {
|
||||
Ok(None) => result::Ok(()),
|
||||
Err(e) => result::Err(e),
|
||||
Ok(Some(borrow)) => {
|
||||
match self.mk_subr(true, expr.span,
|
||||
ty::re_scope(borrow_lb), borrow.region) {
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => {
|
||||
debug!("inserting borrowing of expr %?: %?",
|
||||
expr.id, borrow);
|
||||
self.inh.borrowings.insert(expr.id, borrow);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Ok(Some(adjustment)) => {
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -753,9 +762,18 @@ impl @fn_ctxt {
|
||||
}
|
||||
}
|
||||
|
||||
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
|
||||
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> (ty::t, uint) {
|
||||
/*!
|
||||
*
|
||||
* Autoderefs the type `t` as many times as possible, returning
|
||||
* a new type and a counter for how many times the type was
|
||||
* deref'd. If the counter is non-zero, the receiver is responsible
|
||||
* for inserting an AutoAdjustment record into `tcx.adjustments`
|
||||
* so that trans/borrowck/etc know about this autoderef. */
|
||||
|
||||
let mut t1 = t;
|
||||
let mut enum_dids = ~[];
|
||||
let mut autoderefs = 0;
|
||||
loop {
|
||||
let sty = structure_of(fcx, sp, t1);
|
||||
|
||||
@ -773,13 +791,14 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
|
||||
}
|
||||
ty::ty_enum(did, _) => {
|
||||
// Watch out for a type like `enum t = @t`. Such a
|
||||
// type would otherwise infinitely auto-deref. This
|
||||
// is the only autoderef loop that needs to be
|
||||
// type would otherwise infinitely auto-deref. Only
|
||||
// autoderef loops during typeck (basically, this one
|
||||
// and the loops in typeck::check::method) need to be
|
||||
// concerned with this, as an error will be reported
|
||||
// on the enum definition as well because the enum is
|
||||
// not instantiable.
|
||||
if vec::contains(enum_dids, did) {
|
||||
return t1;
|
||||
return (t1, autoderefs);
|
||||
}
|
||||
vec::push(enum_dids, did);
|
||||
}
|
||||
@ -788,8 +807,13 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
|
||||
|
||||
// Otherwise, deref if type is derefable:
|
||||
match ty::deref_sty(fcx.ccx.tcx, &sty, false) {
|
||||
None => return t1,
|
||||
Some(mt) => t1 = mt.ty
|
||||
None => {
|
||||
return (t1, autoderefs);
|
||||
}
|
||||
Some(mt) => {
|
||||
autoderefs += 1;
|
||||
t1 = mt.ty
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -904,16 +928,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
expected: Option<ty::t>,
|
||||
unifier: fn()) -> bool {
|
||||
|
||||
debug!(
|
||||
">> typechecking expr %d (%s)",
|
||||
expr.id, syntax::print::pprust::expr_to_str(expr,
|
||||
fcx.ccx.tcx.sess.intr()));
|
||||
debug!(">> typechecking %s", fcx.expr_to_str(expr));
|
||||
|
||||
// A generic function to factor out common logic from call and
|
||||
// overloaded operations
|
||||
fn check_call_inner(
|
||||
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
|
||||
fcx: @fn_ctxt,
|
||||
sp: span,
|
||||
call_expr_id: ast::node_id,
|
||||
in_fty: ty::t,
|
||||
callee_expr: @ast::expr,
|
||||
check_args: bool,
|
||||
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
|
||||
|
||||
let mut bot = false;
|
||||
@ -956,7 +981,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// Grab the argument types, supplying fresh type variables
|
||||
// if the wrong number of arguments were supplied
|
||||
let expected_arg_count = vec::len(fn_ty.sig.inputs);
|
||||
let arg_tys = if expected_arg_count == supplied_arg_count {
|
||||
let formal_tys = if expected_arg_count == supplied_arg_count {
|
||||
fn_ty.sig.inputs.map(|a| a.ty)
|
||||
} else {
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
@ -983,6 +1008,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// of arguments when we typecheck the functions. This isn't really the
|
||||
// right way to do this.
|
||||
for [false, true]/_.each |check_blocks| {
|
||||
debug!("check_blocks=%b", check_blocks);
|
||||
|
||||
// More awful hacks: before we check the blocks, try to do
|
||||
// an "opportunistic" vtable resolution of any trait
|
||||
// bounds on the call.
|
||||
@ -990,19 +1017,26 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
vtable::early_resolve_expr(callee_expr, fcx, true);
|
||||
}
|
||||
|
||||
for args.eachi |i, a| {
|
||||
let is_block = match a.node {
|
||||
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
|
||||
ast::expr_do_body(*) => true,
|
||||
_ => false
|
||||
for args.eachi |i, arg| {
|
||||
let is_block = match arg.node {
|
||||
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
|
||||
ast::expr_do_body(*) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
if is_block == check_blocks {
|
||||
let arg_ty = arg_tys[i];
|
||||
bot |= check_expr_with_unifier(
|
||||
fcx, a, Some(arg_ty),
|
||||
|| demand::assign(fcx, a.span, call_expr_id,
|
||||
arg_ty, a)
|
||||
debug!("checking the argument");
|
||||
let formal_ty = formal_tys[i];
|
||||
|
||||
if check_args {
|
||||
bot |= check_expr_with_unifier(
|
||||
fcx, arg, Some(formal_ty),
|
||||
|| demand::assign(fcx, arg.span, formal_ty, arg)
|
||||
);
|
||||
} else {
|
||||
demand::assign(fcx, arg.span, formal_ty, arg);
|
||||
bot |= ty::type_is_bot(fcx.expr_ty(arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1036,7 +1070,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// Call the generic checker.
|
||||
let fty = {
|
||||
let r = check_call_inner(fcx, sp, call_expr_id,
|
||||
fn_ty, f, args);
|
||||
fn_ty, f, true, args);
|
||||
bot |= r.bot;
|
||||
r.fty
|
||||
};
|
||||
@ -1092,16 +1126,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
|
||||
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
|
||||
self_ex: @ast::expr, self_t: ty::t,
|
||||
opname: ast::ident, args: ~[@ast::expr])
|
||||
-> Option<(ty::t, bool)> {
|
||||
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
|
||||
op_ex.callee_id, opname, self_t, ~[], false);
|
||||
match lkup.method() {
|
||||
opname: ast::ident, check_args: bool,
|
||||
args: ~[@ast::expr])
|
||||
-> Option<(ty::t, bool)>
|
||||
{
|
||||
match method::lookup(fcx, op_ex, self_ex,
|
||||
op_ex.callee_id, opname, self_t, ~[]) {
|
||||
Some(origin) => {
|
||||
let {fty: method_ty, bot: bot} = {
|
||||
let method_ty = fcx.node_ty(op_ex.callee_id);
|
||||
check_call_inner(fcx, op_ex.span, op_ex.id,
|
||||
method_ty, op_ex, args)
|
||||
method_ty, op_ex, check_args, args)
|
||||
};
|
||||
fcx.ccx.method_map.insert(op_ex.id, origin);
|
||||
Some((ty::ty_fn_ret(method_ty), bot))
|
||||
@ -1109,71 +1144,95 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_rel_op(fcx: @fn_ctxt,
|
||||
expr: @ast::expr,
|
||||
op: ast::binop,
|
||||
lhs: @ast::expr,
|
||||
rhs: @ast::expr) -> bool
|
||||
{
|
||||
// We know that only things of equal type can be compared, so
|
||||
// go ahead and unify the two types before we do anything else
|
||||
// (with other operators, we must be much more careful not to
|
||||
// make assumptions, due to the possibility of operator
|
||||
// overloading; but overloaded == still enforces the
|
||||
// requirement that only equal types are compared).
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let lhs_bot = check_expr(fcx, lhs, None);
|
||||
let lhs_t = fcx.expr_ty(lhs);
|
||||
let rhs_bot = check_expr_with(fcx, rhs, lhs_t);
|
||||
|
||||
let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
|
||||
if ty::is_binopable(tcx, lhs_t, op) {
|
||||
let result_t = ty::mk_bool(tcx);
|
||||
fcx.write_ty(expr.id, result_t);
|
||||
return lhs_bot | rhs_bot;
|
||||
}
|
||||
|
||||
let (result, rhs_bot) =
|
||||
check_user_binop(fcx, expr, lhs, lhs_t, op, false, rhs);
|
||||
fcx.write_ty(expr.id, result);
|
||||
return lhs_bot | rhs_bot;
|
||||
}
|
||||
|
||||
// could be either a expr_binop or an expr_assign_binop
|
||||
fn check_binop(fcx: @fn_ctxt, expr: @ast::expr,
|
||||
op: ast::binop,
|
||||
lhs: @ast::expr,
|
||||
rhs: @ast::expr) -> bool {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let lhs_bot = check_expr(fcx, lhs, None);
|
||||
let lhs_t = fcx.expr_ty(lhs);
|
||||
|
||||
// Hack: Unify the two sides if this is a relational operator.
|
||||
// Relational operators are different for type inferencing
|
||||
// reasons.
|
||||
match op {
|
||||
ast::eq | ast::ne | ast::lt | ast::le | ast::ge | ast::gt => {
|
||||
check_expr_with(fcx, rhs, lhs_t);
|
||||
return check_rel_op(fcx, expr, op, lhs, rhs);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let lhs_bot = check_expr(fcx, lhs, None);
|
||||
let lhs_t = fcx.expr_ty(lhs);
|
||||
let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
|
||||
return match (op, ty::get(lhs_t).sty) {
|
||||
(_, _) if ty::type_is_integral(lhs_t) &&
|
||||
ast_util::is_shift_binop(op) => {
|
||||
|
||||
if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
|
||||
// Shift is a special case: rhs can be any integral type
|
||||
let rhs_bot = check_expr(fcx, rhs, None);
|
||||
let rhs_t = fcx.expr_ty(rhs);
|
||||
require_integral(fcx, rhs.span, rhs_t);
|
||||
fcx.write_ty(expr.id, lhs_t);
|
||||
lhs_bot | rhs_bot
|
||||
}
|
||||
return lhs_bot | rhs_bot;
|
||||
}
|
||||
|
||||
(_, _) if ty::is_binopable(tcx, lhs_t, op) => {
|
||||
if ty::is_binopable(tcx, lhs_t, op) {
|
||||
let tvar = fcx.infcx().next_ty_var();
|
||||
demand::suptype(fcx, expr.span, tvar, lhs_t);
|
||||
let rhs_bot = check_expr_with(fcx, rhs, tvar);
|
||||
let result_t = match op {
|
||||
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
|
||||
ast::gt => {
|
||||
if !ty::type_is_scalar(lhs_t) {
|
||||
fcx.ccx.tcx.sess.span_bug(expr.span,
|
||||
~"non-scalar compare");
|
||||
}
|
||||
ty::mk_bool(fcx.ccx.tcx)
|
||||
}
|
||||
_ => lhs_t
|
||||
};
|
||||
let result_t = lhs_t;
|
||||
fcx.write_ty(expr.id, result_t);
|
||||
if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
|
||||
else { lhs_bot }
|
||||
}
|
||||
return {
|
||||
if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
|
||||
else { lhs_bot }
|
||||
};
|
||||
}
|
||||
|
||||
(_, _) => {
|
||||
let (result, rhs_bot) =
|
||||
check_user_binop(fcx, expr, lhs, lhs_t, op, rhs);
|
||||
fcx.write_ty(expr.id, result);
|
||||
lhs_bot | rhs_bot
|
||||
}
|
||||
};
|
||||
let (result, rhs_bot) =
|
||||
check_user_binop(fcx, expr, lhs, lhs_t, op, true, rhs);
|
||||
fcx.write_ty(expr.id, result);
|
||||
return lhs_bot | rhs_bot;
|
||||
}
|
||||
|
||||
fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr,
|
||||
lhs_expr: @ast::expr, lhs_resolved_t: ty::t,
|
||||
op: ast::binop, rhs: @ast::expr) -> (ty::t, bool) {
|
||||
op: ast::binop, check_rhs: bool,
|
||||
rhs: @ast::expr) -> (ty::t, bool)
|
||||
{
|
||||
let tcx = fcx.ccx.tcx;
|
||||
match ast_util::binop_to_method_name(op) {
|
||||
Some(name) => {
|
||||
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
|
||||
fcx.tcx().sess.ident_of(name), ~[rhs]) {
|
||||
fcx.tcx().sess.ident_of(name),
|
||||
check_rhs, ~[rhs]) {
|
||||
Some(pair) => return pair,
|
||||
_ => ()
|
||||
}
|
||||
@ -1202,11 +1261,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
|
||||
(lhs_resolved_t, false)
|
||||
}
|
||||
|
||||
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
|
||||
ex: @ast::expr,
|
||||
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
|
||||
match lookup_op_method(fcx, ex, rhs_expr, rhs_t,
|
||||
fcx.tcx().sess.ident_of(mname), ~[]) {
|
||||
fcx.tcx().sess.ident_of(mname), true, ~[]) {
|
||||
Some((ret_ty, _)) => ret_ty,
|
||||
_ => {
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
@ -1308,87 +1368,84 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
// Check field access expressions
|
||||
fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool,
|
||||
base: @ast::expr, field: ast::ident, tys: ~[@ast::ty])
|
||||
-> bool {
|
||||
-> bool
|
||||
{
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let bot = check_expr(fcx, base, None);
|
||||
let expr_t = structurally_resolved_type(fcx, expr.span,
|
||||
fcx.expr_ty(base));
|
||||
let base_t = do_autoderef(fcx, expr.span, expr_t);
|
||||
let mut handled = false;
|
||||
let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
|
||||
let n_tys = vec::len(tys);
|
||||
match structure_of(fcx, expr.span, base_t) {
|
||||
ty::ty_rec(fields) => {
|
||||
match ty::field_idx(field, fields) {
|
||||
Some(ix) => {
|
||||
if n_tys > 0u {
|
||||
tcx.sess.span_err(expr.span,
|
||||
~"can't provide type parameters \
|
||||
to a field access");
|
||||
}
|
||||
fcx.write_ty(expr.id, fields[ix].mt.ty);
|
||||
handled = true;
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
ty::ty_class(base_id, substs) => {
|
||||
// This is just for fields -- the same code handles
|
||||
// methods in both classes and traits
|
||||
|
||||
// (1) verify that the class id actually has a field called
|
||||
// field
|
||||
debug!("class named %s", ty_to_str(tcx, base_t));
|
||||
let cls_items = ty::lookup_class_fields(tcx, base_id);
|
||||
match lookup_field_ty(tcx, base_id, cls_items, field, &substs) {
|
||||
Some(field_ty) => {
|
||||
// (2) look up what field's type is, and return it
|
||||
fcx.write_ty(expr.id, field_ty);
|
||||
handled = true;
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
if !handled {
|
||||
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
|
||||
let is_self_ref = self_ref(fcx, base.id);
|
||||
|
||||
// this will be the call or block that immediately
|
||||
// encloses the method call
|
||||
let borrow_lb = fcx.tcx().region_map.get(expr.id);
|
||||
|
||||
let lkup = method::lookup(fcx, expr, base, borrow_lb,
|
||||
expr.id, field, expr_t, tps,
|
||||
is_self_ref);
|
||||
match lkup.method() {
|
||||
Some(entry) => {
|
||||
fcx.ccx.method_map.insert(expr.id, entry);
|
||||
|
||||
// If we have resolved to a method but this is not in
|
||||
// a callee position, error
|
||||
if !is_callee {
|
||||
tcx.sess.span_err(
|
||||
expr.span,
|
||||
~"attempted to take value of method \
|
||||
(try writing an anonymous function)");
|
||||
ty::ty_rec(fields) => {
|
||||
match ty::field_idx(field, fields) {
|
||||
Some(ix) => {
|
||||
if n_tys > 0u {
|
||||
tcx.sess.span_err(
|
||||
expr.span,
|
||||
~"can't provide type parameters \
|
||||
to a field access");
|
||||
}
|
||||
fcx.write_ty(expr.id, fields[ix].mt.ty);
|
||||
fcx.write_autoderef_adjustment(base.id, derefs);
|
||||
return bot;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let t_err =
|
||||
fcx.infcx().resolve_type_vars_if_possible(expr_t);
|
||||
let msg =
|
||||
fmt!(
|
||||
"attempted access of field `%s` on type `%s`, \
|
||||
but no field or method with that name was found",
|
||||
tcx.sess.str_of(field),
|
||||
fcx.infcx().ty_to_str(t_err));
|
||||
tcx.sess.span_err(expr.span, msg);
|
||||
// NB: Add bogus type to allow typechecking to continue
|
||||
fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
ty::ty_class(base_id, substs) => {
|
||||
// This is just for fields -- the same code handles
|
||||
// methods in both classes and traits
|
||||
|
||||
// (1) verify that the class id actually has a field called
|
||||
// field
|
||||
debug!("class named %s", ty_to_str(tcx, base_t));
|
||||
let cls_items = ty::lookup_class_fields(tcx, base_id);
|
||||
match lookup_field_ty(tcx, base_id, cls_items,
|
||||
field, &substs) {
|
||||
Some(field_ty) => {
|
||||
// (2) look up what field's type is, and return it
|
||||
fcx.write_ty(expr.id, field_ty);
|
||||
fcx.write_autoderef_adjustment(base.id, derefs);
|
||||
return bot;
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
|
||||
|
||||
match method::lookup(fcx, expr, base, expr.id,
|
||||
field, expr_t, tps) {
|
||||
Some(entry) => {
|
||||
fcx.ccx.method_map.insert(expr.id, entry);
|
||||
|
||||
// If we have resolved to a method but this is not in
|
||||
// a callee position, error
|
||||
if !is_callee {
|
||||
tcx.sess.span_err(
|
||||
expr.span,
|
||||
~"attempted to take value of method \
|
||||
(try writing an anonymous function)");
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let t_err =
|
||||
fcx.infcx().resolve_type_vars_if_possible(expr_t);
|
||||
let msg =
|
||||
fmt!(
|
||||
"attempted access of field `%s` on type `%s`, \
|
||||
but no field or method with that name was found",
|
||||
tcx.sess.str_of(field),
|
||||
fcx.infcx().ty_to_str(t_err));
|
||||
tcx.sess.span_err(expr.span, msg);
|
||||
// NB: Add bogus type to allow typechecking to continue
|
||||
fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
|
||||
}
|
||||
}
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
@ -2000,32 +2057,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
bot = check_field(fcx, expr, false, base, field, tys);
|
||||
}
|
||||
ast::expr_index(base, idx) => {
|
||||
bot |= check_expr(fcx, base, None);
|
||||
let raw_base_t = fcx.expr_ty(base);
|
||||
let base_t = do_autoderef(fcx, expr.span, raw_base_t);
|
||||
bot |= check_expr(fcx, idx, None);
|
||||
let idx_t = fcx.expr_ty(idx);
|
||||
let base_sty = structure_of(fcx, expr.span, base_t);
|
||||
match ty::index_sty(tcx, &base_sty) {
|
||||
Some(mt) => {
|
||||
require_integral(fcx, idx.span, idx_t);
|
||||
fcx.write_ty(id, mt.ty);
|
||||
}
|
||||
None => {
|
||||
let resolved = structurally_resolved_type(fcx, expr.span,
|
||||
raw_base_t);
|
||||
match lookup_op_method(fcx, expr, base, resolved,
|
||||
tcx.sess.ident_of(~"index"),
|
||||
~[idx]) {
|
||||
Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
|
||||
_ => {
|
||||
tcx.sess.span_fatal(
|
||||
expr.span, ~"cannot index a value of type `" +
|
||||
fcx.infcx().ty_to_str(base_t) + ~"`");
|
||||
bot |= check_expr(fcx, base, None);
|
||||
let raw_base_t = fcx.expr_ty(base);
|
||||
let (base_t, derefs) = do_autoderef(fcx, expr.span, raw_base_t);
|
||||
bot |= check_expr(fcx, idx, None);
|
||||
let idx_t = fcx.expr_ty(idx);
|
||||
let base_sty = structure_of(fcx, expr.span, base_t);
|
||||
match ty::index_sty(tcx, &base_sty) {
|
||||
Some(mt) => {
|
||||
require_integral(fcx, idx.span, idx_t);
|
||||
fcx.write_ty(id, mt.ty);
|
||||
fcx.write_autoderef_adjustment(base.id, derefs);
|
||||
}
|
||||
None => {
|
||||
let resolved = structurally_resolved_type(fcx, expr.span,
|
||||
raw_base_t);
|
||||
match lookup_op_method(fcx, expr, base, resolved,
|
||||
tcx.sess.ident_of(~"index"), true,
|
||||
~[idx]) {
|
||||
Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
|
||||
_ => {
|
||||
tcx.sess.span_fatal(
|
||||
expr.span, ~"cannot index a value of type `" +
|
||||
fcx.infcx().ty_to_str(base_t) + ~"`");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if bot { fcx.write_bot(expr.id); }
|
||||
@ -2304,16 +2362,6 @@ fn check_enum_variants(ccx: @crate_ctxt,
|
||||
check_instantiable(ccx.tcx, sp, id);
|
||||
}
|
||||
|
||||
// Determines whether the given node ID is a use of the def of
|
||||
// the self ID for the current method, if there is one
|
||||
// self IDs in an outer scope count. so that means that you can
|
||||
// call your own private methods from nested functions inside
|
||||
// class methods
|
||||
fn self_ref(fcx: @fn_ctxt, id: ast::node_id) -> bool {
|
||||
option::map_default(fcx.ccx.tcx.def_map.find(id), false,
|
||||
ast_util::is_self)
|
||||
}
|
||||
|
||||
fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid {
|
||||
match fcx.inh.locals.find(id) {
|
||||
Some(x) => x,
|
||||
@ -2480,19 +2528,19 @@ fn type_is_c_like_enum(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
|
||||
}
|
||||
|
||||
fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
|
||||
v: ast::vstore) -> ty::vstore {
|
||||
v: ast::expr_vstore) -> ty::vstore {
|
||||
match v {
|
||||
ast::vstore_fixed(None) => ty::vstore_fixed(n),
|
||||
ast::vstore_fixed(Some(u)) => {
|
||||
ast::expr_vstore_fixed(None) => ty::vstore_fixed(n),
|
||||
ast::expr_vstore_fixed(Some(u)) => {
|
||||
if n != u {
|
||||
let s = fmt!("fixed-size sequence mismatch: %u vs. %u",u, n);
|
||||
fcx.ccx.tcx.sess.span_err(e.span,s);
|
||||
}
|
||||
ty::vstore_fixed(u)
|
||||
}
|
||||
ast::vstore_uniq => ty::vstore_uniq,
|
||||
ast::vstore_box => ty::vstore_box,
|
||||
ast::vstore_slice(_) => {
|
||||
ast::expr_vstore_uniq => ty::vstore_uniq,
|
||||
ast::expr_vstore_box => ty::vstore_box,
|
||||
ast::expr_vstore_slice => {
|
||||
let r = fcx.infcx().next_region_var(e.span, e.id);
|
||||
ty::vstore_slice(r)
|
||||
}
|
||||
|
@ -27,10 +27,9 @@ fn eqtype(fcx: @fn_ctxt, sp: span,
|
||||
}
|
||||
|
||||
// Checks that the type `actual` can be assigned to `expected`.
|
||||
fn assign(fcx: @fn_ctxt, sp: span, borrow_lb: ast::node_id,
|
||||
expected: ty::t, expr: @ast::expr) {
|
||||
fn assign(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) {
|
||||
let expr_ty = fcx.expr_ty(expr);
|
||||
match fcx.mk_assignty(expr, borrow_lb, expr_ty, expected) {
|
||||
match fcx.mk_assignty(expr, expr_ty, expected) {
|
||||
result::Ok(()) => { /* ok */ }
|
||||
result::Err(ref err) => {
|
||||
fcx.report_mismatched_types(sp, expected, expr_ty, err);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -155,116 +155,157 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
|
||||
visit::visit_block(b, rcx, v);
|
||||
}
|
||||
|
||||
fn visit_expr(e: @ast::expr, &&rcx: @rcx, v: rvt) {
|
||||
fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
|
||||
debug!("visit_expr(e=%s)",
|
||||
pprust::expr_to_str(e, rcx.fcx.tcx().sess.intr()));
|
||||
pprust::expr_to_str(expr, rcx.fcx.tcx().sess.intr()));
|
||||
|
||||
match e.node {
|
||||
ast::expr_path(*) => {
|
||||
// Avoid checking the use of local variables, as we already
|
||||
// check their definitions. The def'n always encloses the
|
||||
// use. So if the def'n is enclosed by the region, then the
|
||||
// uses will also be enclosed (and otherwise, an error will
|
||||
// have been reported at the def'n site).
|
||||
match lookup_def(rcx.fcx, e.span, e.id) {
|
||||
ast::def_local(*) | ast::def_arg(*) | ast::def_upvar(*) => return,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
// constrain_auto_ref(rcx, expr);
|
||||
|
||||
ast::expr_cast(source, _) => {
|
||||
// Determine if we are casting `source` to an trait instance.
|
||||
// If so, we have to be sure that the type of the source obeys
|
||||
// the trait's region bound.
|
||||
//
|
||||
// Note: there is a subtle point here concerning type
|
||||
// parameters. It is possible that the type of `source`
|
||||
// contains type parameters, which in turn may contain regions
|
||||
// that are not visible to us (only the caller knows about
|
||||
// them). The kind checker is ultimately responsible for
|
||||
// guaranteeing region safety in that particular case. There
|
||||
// is an extensive comment on the function
|
||||
// check_cast_for_escaping_regions() in kind.rs explaining how
|
||||
// it goes about doing that.
|
||||
match rcx.resolve_node_type(e.id) {
|
||||
result::Err(_) => { return; /* typeck will fail anyhow */ }
|
||||
result::Ok(target_ty) => {
|
||||
match ty::get(target_ty).sty {
|
||||
ty::ty_trait(_, substs, _) => {
|
||||
let trait_region = match substs.self_r {
|
||||
Some(r) => {r}
|
||||
None => {ty::re_static}
|
||||
};
|
||||
let source_ty = rcx.fcx.expr_ty(source);
|
||||
constrain_regions_in_type(rcx, trait_region,
|
||||
e.span, source_ty);
|
||||
}
|
||||
_ => ()
|
||||
match expr.node {
|
||||
ast::expr_path(*) => {
|
||||
// Avoid checking the use of local variables, as we
|
||||
// already check their definitions. The def'n always
|
||||
// encloses the use. So if the def'n is enclosed by the
|
||||
// region, then the uses will also be enclosed (and
|
||||
// otherwise, an error will have been reported at the
|
||||
// def'n site).
|
||||
match lookup_def(rcx.fcx, expr.span, expr.id) {
|
||||
ast::def_local(*) | ast::def_arg(*) |
|
||||
ast::def_upvar(*) => return,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ast::expr_addr_of(*) => {
|
||||
// FIXME(#3148) -- in some cases, we need to capture a dependency
|
||||
// between the regions found in operand the resulting region type.
|
||||
// See #3148 for more details.
|
||||
}
|
||||
|
||||
ast::expr_fn(*) | ast::expr_fn_block(*) => {
|
||||
match rcx.resolve_node_type(e.id) {
|
||||
result::Err(_) => return, // Typechecking will fail anyhow.
|
||||
result::Ok(function_type) => {
|
||||
match ty::get(function_type).sty {
|
||||
ty::ty_fn(ref fn_ty) => {
|
||||
match fn_ty.meta.proto {
|
||||
proto_vstore(vstore_slice(region)) => {
|
||||
constrain_free_variables(rcx, region, e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ()
|
||||
ast::expr_cast(source, _) => {
|
||||
// Determine if we are casting `source` to an trait
|
||||
// instance. If so, we have to be sure that the type of
|
||||
// the source obeys the trait's region bound.
|
||||
//
|
||||
// Note: there is a subtle point here concerning type
|
||||
// parameters. It is possible that the type of `source`
|
||||
// contains type parameters, which in turn may contain
|
||||
// regions that are not visible to us (only the caller
|
||||
// knows about them). The kind checker is ultimately
|
||||
// responsible for guaranteeing region safety in that
|
||||
// particular case. There is an extensive comment on the
|
||||
// function check_cast_for_escaping_regions() in kind.rs
|
||||
// explaining how it goes about doing that.
|
||||
match rcx.resolve_node_type(expr.id) {
|
||||
result::Err(_) => { return; /*typeck will fail anyhow*/ }
|
||||
result::Ok(target_ty) => {
|
||||
match ty::get(target_ty).sty {
|
||||
ty::ty_trait(_, substs, _) => {
|
||||
let trait_region = match substs.self_r {
|
||||
Some(r) => {r}
|
||||
None => {ty::re_static}
|
||||
};
|
||||
let source_ty = rcx.fcx.expr_ty(source);
|
||||
constrain_regions_in_type(rcx, trait_region,
|
||||
expr.span, source_ty);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ast::expr_addr_of(*) => {
|
||||
// FIXME(#3148) -- in some cases, we need to capture a
|
||||
// dependency between the regions found in operand the
|
||||
// resulting region type. See #3148 for more details.
|
||||
}
|
||||
|
||||
ast::expr_fn(*) | ast::expr_fn_block(*) => {
|
||||
match rcx.resolve_node_type(expr.id) {
|
||||
result::Err(_) => return, // Typechecking will fail anyhow.
|
||||
result::Ok(function_type) => {
|
||||
match ty::get(function_type).sty {
|
||||
ty::ty_fn(ref fn_ty) => {
|
||||
match fn_ty.meta.proto {
|
||||
proto_vstore(vstore_slice(region)) => {
|
||||
constrain_free_variables(rcx, region,
|
||||
expr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ()
|
||||
}
|
||||
|
||||
if !visit_node(e.id, e.span, rcx) { return; }
|
||||
visit::visit_expr(e, rcx, v);
|
||||
if !visit_node(expr.id, expr.span, rcx) { return; }
|
||||
visit::visit_expr(expr, rcx, v);
|
||||
}
|
||||
|
||||
fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
|
||||
visit::visit_stmt(s, rcx, v);
|
||||
}
|
||||
|
||||
// checks the type of the node `id` and reports an error if it
|
||||
// references a region that is not in scope for that node. Returns
|
||||
// false if an error is reported; this is used to cause us to cut off
|
||||
// region checking for that subtree to avoid reporting tons of errors.
|
||||
fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
|
||||
let fcx = rcx.fcx;
|
||||
/*!
|
||||
*
|
||||
* checks the type of the node `id` and reports an error if it
|
||||
* references a region that is not in scope for that node.
|
||||
* Returns false if an error is reported; this is used to cause us
|
||||
* to cut off region checking for that subtree to avoid reporting
|
||||
* tons of errors. */
|
||||
|
||||
// Try to resolve the type. If we encounter an error, then typeck
|
||||
// is going to fail anyway, so just stop here and let typeck
|
||||
// report errors later on in the writeback phase.
|
||||
let ty = match rcx.resolve_node_type(id) {
|
||||
result::Err(_) => return true,
|
||||
result::Ok(ty) => ty
|
||||
};
|
||||
let fcx = rcx.fcx;
|
||||
|
||||
// find the region where this expr evaluation is taking place
|
||||
let tcx = fcx.ccx.tcx;
|
||||
let encl_region = ty::encl_region(tcx, id);
|
||||
|
||||
debug!("visit_node(ty=%s, id=%d, encl_region=%?)",
|
||||
ty_to_str(tcx, ty), id, encl_region);
|
||||
|
||||
// Otherwise, look at the type and see if it is a region pointer.
|
||||
return constrain_regions_in_type(rcx, encl_region, span, ty);
|
||||
constrain_regions_in_type_of_node(rcx, id, encl_region, span)
|
||||
}
|
||||
|
||||
fn constrain_auto_ref(
|
||||
rcx: @rcx,
|
||||
expr: @ast::expr)
|
||||
{
|
||||
/*!
|
||||
*
|
||||
* If `expr` is auto-ref'd (e.g., as part of a borrow), then this
|
||||
* function ensures that the lifetime of the resulting borrowed
|
||||
* ptr includes at least the expression `expr`. */
|
||||
|
||||
let adjustment = rcx.fcx.inh.adjustments.find(expr.id);
|
||||
let region = match adjustment {
|
||||
Some(@{autoref: Some(ref auto_ref), _}) => auto_ref.region,
|
||||
_ => { return; }
|
||||
};
|
||||
|
||||
let tcx = rcx.fcx.tcx();
|
||||
let expr_region = ty::re_scope(expr.id);
|
||||
match rcx.fcx.mk_subr(true, expr.span, expr_region, region) {
|
||||
result::Ok(()) => {}
|
||||
result::Err(_) => {
|
||||
// In practice, this cannot happen: `region` is always a
|
||||
// region variable, and constraints on region variables
|
||||
// are collected and then resolved later. However, I
|
||||
// included the span_err() here (rather than, say,
|
||||
// span_bug()) because it seemed more future-proof: if,
|
||||
// for some reason, the code were to change so that in
|
||||
// some cases `region` is not a region variable, then
|
||||
// reporting an error would be the correct path.
|
||||
tcx.sess.span_err(
|
||||
expr.span,
|
||||
~"lifetime of borrowed pointer does not include \
|
||||
the expression being borrowed");
|
||||
note_and_explain_region(
|
||||
tcx,
|
||||
~"lifetime of the borrowed pointer is",
|
||||
region,
|
||||
~"");
|
||||
rcx.errors_reported += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn constrain_free_variables(
|
||||
@ -272,9 +313,11 @@ fn constrain_free_variables(
|
||||
region: ty::region,
|
||||
expr: @ast::expr)
|
||||
{
|
||||
// Make sure that all regions referenced by the free
|
||||
// variables inside the closure outlive the closure
|
||||
// itself.
|
||||
/*!
|
||||
*
|
||||
* Make sure that all free variables referenced inside the closure
|
||||
* outlive the closure itself. */
|
||||
|
||||
let tcx = rcx.fcx.ccx.tcx;
|
||||
for get_freevars(tcx, expr.id).each |freevar| {
|
||||
debug!("freevar def is %?", freevar.def);
|
||||
@ -302,11 +345,44 @@ fn constrain_free_variables(
|
||||
}
|
||||
}
|
||||
|
||||
fn constrain_regions_in_type_of_node(
|
||||
rcx: @rcx,
|
||||
id: ast::node_id,
|
||||
encl_region: ty::region,
|
||||
span: span) -> bool
|
||||
{
|
||||
let tcx = rcx.fcx.tcx();
|
||||
|
||||
// Try to resolve the type. If we encounter an error, then typeck
|
||||
// is going to fail anyway, so just stop here and let typeck
|
||||
// report errors later on in the writeback phase.
|
||||
let ty = match rcx.resolve_node_type(id) {
|
||||
result::Err(_) => return true,
|
||||
result::Ok(ty) => ty
|
||||
};
|
||||
|
||||
debug!("constrain_regions_in_type_of_node(\
|
||||
ty=%s, id=%d, encl_region=%?)",
|
||||
ty_to_str(tcx, ty), id, encl_region);
|
||||
|
||||
constrain_regions_in_type(rcx, encl_region, span, ty)
|
||||
}
|
||||
|
||||
fn constrain_regions_in_type(
|
||||
rcx: @rcx,
|
||||
encl_region: ty::region,
|
||||
span: span,
|
||||
ty: ty::t) -> bool {
|
||||
ty: ty::t) -> bool
|
||||
{
|
||||
/*!
|
||||
*
|
||||
* Requires that any regions which appear in `ty` must be
|
||||
* superregions of `encl_region`. This prevents regions from
|
||||
* being used outside of the block in which they are valid.
|
||||
* Recall that regions represent blocks of code or expressions:
|
||||
* this requirement basically says "any place that uses or may use
|
||||
* a region R must be within the block of code that R corresponds
|
||||
* to." */
|
||||
|
||||
let e = rcx.errors_reported;
|
||||
ty::walk_regions_and_ty(
|
||||
|
@ -4,6 +4,7 @@ use infer::{resolve_type, resolve_and_force_all_but_regions,
|
||||
use ast_util::new_def_hash;
|
||||
use syntax::print::pprust;
|
||||
use result::{Result, Ok, Err};
|
||||
use util::common::indenter;
|
||||
|
||||
// vtable resolution looks for places where trait bounds are
|
||||
// subsituted in and figures out which vtable is used. There is some
|
||||
@ -410,6 +411,8 @@ fn insert_vtables(ccx: @crate_ctxt, callee_id: ast::node_id,
|
||||
fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||
debug!("vtable: early_resolve_expr() ex with id %? (early: %b): %s",
|
||||
ex.id, is_early, expr_to_str(ex, fcx.tcx().sess.intr()));
|
||||
let _indent = indenter();
|
||||
|
||||
let cx = fcx.ccx;
|
||||
match ex.node {
|
||||
ast::expr_path(*) => {
|
||||
|
@ -27,30 +27,53 @@ fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_method_map_entry(fcx: @fn_ctxt, sp: span, id: ast::node_id)
|
||||
{
|
||||
// Resolve any method map entry
|
||||
match fcx.ccx.method_map.find(id) {
|
||||
None => {}
|
||||
Some(ref mme) => {
|
||||
for resolve_type_vars_in_type(fcx, sp, mme.self_arg.ty).each |t| {
|
||||
fcx.ccx.method_map.insert(
|
||||
id,
|
||||
{self_arg: {mode: mme.self_arg.mode, ty: t},
|
||||
..*mme});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Resolve any borrowings for the node with id `id`
|
||||
match fcx.inh.borrowings.find(id) {
|
||||
match fcx.inh.adjustments.find(id) {
|
||||
None => (),
|
||||
Some(borrow) => {
|
||||
match resolve_region(fcx.infcx(), borrow.region,
|
||||
resolve_all | force_all) {
|
||||
Err(e) => {
|
||||
// This should not, I think, happen.
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
sp, fmt!("cannot resolve scope of borrow: %s",
|
||||
infer::fixup_err_to_str(e)));
|
||||
Some(adj) => {
|
||||
let resolved_autoref = match adj.autoref {
|
||||
Some(ref autoref) => {
|
||||
match resolve_region(fcx.infcx(), autoref.region,
|
||||
resolve_all | force_all) {
|
||||
Err(e) => {
|
||||
// This should not, I think, happen.
|
||||
fcx.ccx.tcx.sess.span_err(
|
||||
sp, fmt!("cannot resolve scope of borrow: %s",
|
||||
infer::fixup_err_to_str(e)));
|
||||
Some(*autoref)
|
||||
}
|
||||
Ok(r) => {
|
||||
Some({region: r, ..*autoref})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(r) => {
|
||||
debug!("Borrowing node %d -> region %?, mutbl %?",
|
||||
id, r, borrow.mutbl);
|
||||
fcx.tcx().borrowings.insert(id, {region: r,
|
||||
mutbl: borrow.mutbl});
|
||||
}
|
||||
}
|
||||
None => None
|
||||
};
|
||||
|
||||
let resolved_adj = @{autoref: resolved_autoref, ..*adj};
|
||||
debug!("Adjustments for node %d: %?", id, resolved_adj);
|
||||
fcx.tcx().adjustments.insert(id, resolved_adj);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +132,8 @@ fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) {
|
||||
fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
|
||||
if !wbcx.success { return; }
|
||||
resolve_type_vars_for_node(wbcx, e.span, e.id);
|
||||
resolve_method_map_entry(wbcx.fcx, e.span, e.id);
|
||||
resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id);
|
||||
match e.node {
|
||||
ast::expr_fn(_, decl, _, _) |
|
||||
ast::expr_fn_block(decl, _, _) => {
|
||||
|
@ -275,6 +275,7 @@ use unify::{vals_and_bindings, root};
|
||||
use integral::{int_ty_set, int_ty_set_all};
|
||||
use combine::{combine_fields, eq_tys};
|
||||
use assignment::Assign;
|
||||
use to_str::to_str;
|
||||
|
||||
use sub::Sub;
|
||||
use lub::Lub;
|
||||
@ -304,7 +305,7 @@ type bounds<T:Copy> = {lb: bound<T>, ub: bound<T>};
|
||||
type cres<T> = Result<T,ty::type_err>; // "combine result"
|
||||
type ures = cres<()>; // "unify result"
|
||||
type fres<T> = Result<T, fixup_err>; // "fixup result"
|
||||
type ares = cres<Option<ty::borrow>>; // "assignment result"
|
||||
type ares = cres<Option<@ty::AutoAdjustment>>; // "assignment result"
|
||||
|
||||
enum infer_ctxt = @{
|
||||
tcx: ty::ctxt,
|
||||
@ -469,19 +470,24 @@ impl ures: then {
|
||||
}
|
||||
}
|
||||
|
||||
trait cres_helpers<T> {
|
||||
trait ToUres {
|
||||
fn to_ures() -> ures;
|
||||
fn compare(t: T, f: fn() -> ty::type_err) -> cres<T>;
|
||||
}
|
||||
|
||||
impl<T:Copy Eq> cres<T>: cres_helpers<T> {
|
||||
impl<T> cres<T>: ToUres {
|
||||
fn to_ures() -> ures {
|
||||
match self {
|
||||
Ok(_v) => Ok(()),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait CresCompare<T> {
|
||||
fn compare(t: T, f: fn() -> ty::type_err) -> cres<T>;
|
||||
}
|
||||
|
||||
impl<T:Copy Eq> cres<T>: CresCompare<T> {
|
||||
fn compare(t: T, f: fn() -> ty::type_err) -> cres<T> {
|
||||
do self.chain |s| {
|
||||
if s == t {
|
||||
|
@ -134,18 +134,24 @@ priv impl Assign {
|
||||
(ty::ty_box(_), ty::ty_rptr(r_b, mt_b)) => {
|
||||
let nr_b = ty::mk_box(self.infcx.tcx,
|
||||
{ty: mt_b.ty, mutbl: m_const});
|
||||
self.try_assign(a, nr_b, mt_b.mutbl, r_b)
|
||||
self.try_assign(1, ty::AutoPtr,
|
||||
a, nr_b,
|
||||
mt_b.mutbl, r_b)
|
||||
}
|
||||
(ty::ty_uniq(_), ty::ty_rptr(r_b, mt_b)) => {
|
||||
let nr_b = ty::mk_uniq(self.infcx.tcx,
|
||||
{ty: mt_b.ty, mutbl: m_const});
|
||||
self.try_assign(a, nr_b, mt_b.mutbl, r_b)
|
||||
self.try_assign(1, ty::AutoPtr,
|
||||
a, nr_b,
|
||||
mt_b.mutbl, r_b)
|
||||
}
|
||||
(ty::ty_estr(vs_a),
|
||||
ty::ty_estr(ty::vstore_slice(r_b)))
|
||||
if is_borrowable(vs_a) => {
|
||||
let nr_b = ty::mk_estr(self.infcx.tcx, vs_a);
|
||||
self.try_assign(a, nr_b, m_imm, r_b)
|
||||
self.try_assign(0, ty::AutoSlice,
|
||||
a, nr_b,
|
||||
m_imm, r_b)
|
||||
}
|
||||
|
||||
(ty::ty_evec(_, vs_a),
|
||||
@ -154,7 +160,9 @@ priv impl Assign {
|
||||
let nr_b = ty::mk_evec(self.infcx.tcx,
|
||||
{ty: mt_b.ty, mutbl: m_const},
|
||||
vs_a);
|
||||
self.try_assign(a, nr_b, mt_b.mutbl, r_b)
|
||||
self.try_assign(0, ty::AutoSlice,
|
||||
a, nr_b,
|
||||
mt_b.mutbl, r_b)
|
||||
}
|
||||
|
||||
_ => {
|
||||
@ -177,7 +185,9 @@ priv impl Assign {
|
||||
/// variable `r_a >= r_b` and returns a corresponding assignment
|
||||
/// record. See the discussion at the top of this file for more
|
||||
/// details.
|
||||
fn try_assign(a: ty::t,
|
||||
fn try_assign(autoderefs: uint,
|
||||
kind: ty::AutoRefKind,
|
||||
a: ty::t,
|
||||
nr_b: ty::t,
|
||||
m: ast::mutability,
|
||||
r_b: ty::region) -> ares {
|
||||
@ -193,7 +203,14 @@ priv impl Assign {
|
||||
do sub.tys(a, nr_b).chain |_t| {
|
||||
let r_a = self.infcx.next_region_var_nb(self.span);
|
||||
do sub.contraregions(r_a, r_b).chain |_r| {
|
||||
Ok(Some({region: r_a, mutbl: m}))
|
||||
Ok(Some(@{
|
||||
autoderefs: autoderefs,
|
||||
autoref: Some({
|
||||
kind: kind,
|
||||
region: r_a,
|
||||
mutbl: m
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use syntax::codemap;
|
||||
use syntax::codemap::span;
|
||||
use syntax::print::pprust;
|
||||
use syntax::print::pprust::{path_to_str, proto_to_str,
|
||||
mode_to_str, purity_to_str};
|
||||
mode_to_str, purity_to_str};
|
||||
use syntax::{ast, ast_util};
|
||||
use syntax::ast_map;
|
||||
use driver::session::session;
|
||||
@ -229,10 +229,15 @@ fn proto_ty_to_str(cx: ctxt, proto: ty::fn_proto) -> ~str {
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str {
|
||||
fmt!("expr(%d: %s)",
|
||||
expr.id,
|
||||
pprust::expr_to_str(expr, cx.sess.intr()))
|
||||
}
|
||||
|
||||
fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str {
|
||||
let mut rs = ~"";
|
||||
for ts.each |t| { rs += ty_to_str(cx, t); }
|
||||
rs
|
||||
let tstrs = ts.map(|t| ty_to_str(cx, t));
|
||||
fmt!("[%s]", str::connect(tstrs, ", "))
|
||||
}
|
||||
|
||||
fn bound_to_str(cx: ctxt, b: param_bound) -> ~str {
|
||||
|
@ -19,4 +19,5 @@ fn cat(in_x : uint, in_y : int) -> cat {
|
||||
fn main() {
|
||||
let nyan : cat = cat(52u, 99);
|
||||
nyan.speak = fn@() { debug!("meow"); }; //~ ERROR attempted to take value of method
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
19
src/test/compile-fail/borrowck-autoref-3261.rs
Normal file
19
src/test/compile-fail/borrowck-autoref-3261.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use either::*;
|
||||
enum X = Either<(uint,uint),fn()>;
|
||||
impl &X {
|
||||
fn with(blk: fn(x: &Either<(uint,uint),fn()>)) {
|
||||
blk(&**self)
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
let mut x = X(Right(main));
|
||||
do (&mut x).with |opt| { //~ ERROR illegal borrow
|
||||
match *opt {
|
||||
Right(f) => {
|
||||
x = X(Left((0,0)));
|
||||
f()
|
||||
},
|
||||
_ => fail
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
struct Foo {
|
||||
x: int,
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn f(&self) {}
|
||||
fn g(&const self) {}
|
||||
fn h(&mut self) {}
|
||||
}
|
||||
|
||||
fn a(x: &mut Foo) {
|
||||
x.f(); //~ ERROR illegal borrow unless pure
|
||||
x.g();
|
||||
x.h();
|
||||
}
|
||||
|
||||
fn b(x: &Foo) {
|
||||
x.f();
|
||||
x.g();
|
||||
x.h(); //~ ERROR illegal borrow
|
||||
}
|
||||
|
||||
fn c(x: &const Foo) {
|
||||
x.f(); //~ ERROR illegal borrow unless pure
|
||||
x.g();
|
||||
x.h(); //~ ERROR illegal borrow
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ fn borrow_from_arg_imm_ref(&&v: ~int) {
|
||||
}
|
||||
|
||||
fn borrow_from_arg_mut_ref(&v: ~int) {
|
||||
borrow(v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
borrow(v); //~ ERROR illegal borrow unless pure
|
||||
//~^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fn c() {
|
||||
|
||||
|
||||
// ...but not impure fns
|
||||
(*q).times(3); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
(*q).times(3); //~ ERROR illegal borrow unless pure
|
||||
//~^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ fn c() {
|
||||
(*q).purem();
|
||||
|
||||
// ...but not impure fns
|
||||
(*q).impurem(); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
(*q).impurem(); //~ ERROR illegal borrow unless pure
|
||||
//~^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let x: int = 3;
|
||||
let y: &mut int = &mut x; //~ ERROR taking mut reference to immutable local variable
|
||||
let y: &mut int = &mut x; //~ ERROR illegal borrow
|
||||
*y = 5;
|
||||
log (debug, *y);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
enum foo = ~int;
|
||||
|
||||
fn borrow(x: @mut foo) {
|
||||
let _y = &***x; //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
let _y = &***x; //~ ERROR illegal borrow unless pure
|
||||
*x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
|
||||
}
|
||||
|
||||
|
8
src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
Normal file
8
src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
Normal file
@ -0,0 +1,8 @@
|
||||
fn write(v: &[mut int]) {
|
||||
v[0] += 1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let v = ~[1, 2, 3];
|
||||
write(v); //~ ERROR illegal borrow
|
||||
}
|
@ -5,7 +5,7 @@ fn want_slice(v: &[int]) -> int {
|
||||
}
|
||||
|
||||
fn has_mut_vec(+v: @~[mut int]) -> int {
|
||||
want_slice(*v) //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
want_slice(*v) //~ ERROR illegal borrow unless pure
|
||||
//~^ NOTE impure due to access to impure function
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ fn test1(x: @mut ~int) {
|
||||
// Here, evaluating the second argument actually invalidates the
|
||||
// first borrow, even though it occurs outside of the scope of the
|
||||
// borrow!
|
||||
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure
|
||||
//~^ NOTE impure due to assigning to dereference of mutable @ pointer
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,19 @@
|
||||
fn borrow(_v: &int) {}
|
||||
|
||||
fn box_mut(v: @mut ~int) {
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_rec_mut(v: @{mut f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_mut_rec(v: @mut {f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_imm(v: @~int) {
|
||||
@ -33,28 +29,23 @@ fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
|
||||
}
|
||||
|
||||
fn box_const(v: @const ~int) {
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_rec_const(v: @{const f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_recs_const(v: @{f: {g: {const h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_const_rec(v: @const {f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_const_recs(v: @const {f: {g: {h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,23 +1,19 @@
|
||||
fn borrow(_v: &int) {}
|
||||
|
||||
fn box_mut(v: &mut ~int) {
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_rec_mut(v: &{mut f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_mut_rec(v: &mut {f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_imm(v: &~int) {
|
||||
@ -33,28 +29,23 @@ fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
|
||||
}
|
||||
|
||||
fn box_const(v: &const ~int) {
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(*v); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_rec_const(v: &{const f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_recs_const(v: &{f: {g: {const h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_const_rec(v: &const {f: ~int}) {
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn box_const_recs(v: &const {f: {g: {h: ~int}}}) {
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
|
||||
//~^ NOTE impure due to access to impure function
|
||||
borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -7,6 +7,5 @@ fn f<T:Eq>(&o: Option<T>) {
|
||||
|
||||
fn main() {
|
||||
f::<int>(option::None);
|
||||
//~^ ERROR taking mut reference to static item
|
||||
//~^^ ERROR illegal borrow: creating mutable alias to aliasable, immutable memory
|
||||
//~^ ERROR illegal borrow: creating mutable alias to static item
|
||||
}
|
||||
|
14
src/test/run-pass/auto-ref-newtype.rs
Normal file
14
src/test/run-pass/auto-ref-newtype.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// Check that we can define inherent methods on newtype enums that use
|
||||
// an auto-ref'd receiver.
|
||||
|
||||
enum Foo = uint;
|
||||
|
||||
impl Foo {
|
||||
fn len(&self) -> uint { **self }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let m = Foo(3);
|
||||
assert m.len() == 3;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ struct Foo {
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn f(&self) {}
|
||||
fn f(&const self) {}
|
||||
}
|
||||
|
||||
fn g(x: &mut Foo) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user