1
Fork 0

Introduce auto adjustment table to subsume autoderef/autoref/borrowings.

Fixes #3261
Fixes #3443
This commit is contained in:
Niko Matsakis 2012-09-11 21:25:01 -07:00
parent 02b41097e4
commit 8a8f200d10
59 changed files with 2098 additions and 1608 deletions

View file

@ -1177,7 +1177,7 @@ type SharedChan<T: Send> = unsafe::Exclusive<Chan<T>>;
impl<T: Send> SharedChan<T>: Channel<T> { impl<T: Send> SharedChan<T>: Channel<T> {
fn send(+x: T) { fn send(+x: T) {
let mut xx = Some(move x); let mut xx = Some(move x);
do self.with |chan| { do self.with_imm |chan| {
let mut x = None; let mut x = None;
x <-> xx; x <-> xx;
chan.send(option::unwrap(move x)) chan.send(option::unwrap(move x))
@ -1186,7 +1186,7 @@ impl<T: Send> SharedChan<T>: Channel<T> {
fn try_send(+x: T) -> bool { fn try_send(+x: T) -> bool {
let mut xx = Some(move x); let mut xx = Some(move x);
do self.with |chan| { do self.with_imm |chan| {
let mut x = None; let mut x = None;
x <-> xx; x <-> xx;
chan.try_send(option::unwrap(move x)) chan.try_send(option::unwrap(move x))

View file

@ -354,6 +354,13 @@ impl<T: Send> Exclusive<T> {
move result 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 // FIXME(#2585) make this a by-move method on the exclusive

View file

@ -314,6 +314,8 @@ enum EbmlSerializerTag {
EsEnum, EsEnumVid, EsEnumBody, EsEnum, EsEnumVid, EsEnumBody,
EsVec, EsVecLen, EsVecElt, EsVec, EsVecLen, EsVecElt,
EsOpaque,
EsLabel // Used only when debugging 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 { impl ebml::Writer: serialization::Serializer {
fn emit_nil() {} fn emit_nil() {}
@ -397,7 +407,7 @@ impl ebml::Writer: serialization::Serializer {
} }
type EbmlDeserializer_ = {mut parent: ebml::Doc, type EbmlDeserializer_ = {mut parent: ebml::Doc,
mut pos: uint}; mut pos: uint};
enum EbmlDeserializer { enum EbmlDeserializer {
EbmlDeserializer_(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 { impl EbmlDeserializer: serialization::Deserializer {
fn read_nil() -> () { () } fn read_nil() -> () { () }

View file

@ -794,7 +794,7 @@ impl TcpSocketBuf: io::Reader {
count count
} }
fn read_byte() -> int { fn read_byte() -> int {
let bytes = ~[0]; let mut bytes = ~[0];
if self.read(bytes, 1u) == 0 { fail } else { bytes[0] as int } if self.read(bytes, 1u) == 0 { fail } else { bytes[0] as int }
} }
fn unread_byte(amt: int) { fn unread_byte(amt: int) {

View file

@ -775,7 +775,7 @@ mod tests {
let (c,p) = pipes::stream(); let (c,p) = pipes::stream();
let m = ~Mutex(); let m = ~Mutex();
let m2 = ~m.clone(); let m2 = ~m.clone();
let sharedstate = ~0; let mut sharedstate = ~0;
let ptr = ptr::addr_of(*sharedstate); let ptr = ptr::addr_of(*sharedstate);
do task::spawn { do task::spawn {
let sharedstate: &mut int = let sharedstate: &mut int =
@ -1047,7 +1047,7 @@ mod tests {
// mutex mutual exclusion test, a ways above. // mutex mutual exclusion test, a ways above.
let (c,p) = pipes::stream(); let (c,p) = pipes::stream();
let x2 = ~x.clone(); let x2 = ~x.clone();
let sharedstate = ~0; let mut sharedstate = ~0;
let ptr = ptr::addr_of(*sharedstate); let ptr = ptr::addr_of(*sharedstate);
do task::spawn { do task::spawn {
let sharedstate: &mut int = let sharedstate: &mut int =

View file

@ -421,6 +421,15 @@ enum vstore {
vstore_slice(@region) // &[1,2,3,4](foo)? 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 { pure fn is_blockish(p: ast::proto) -> bool {
match p { match p {
proto_block => true, proto_block => true,
@ -662,7 +671,7 @@ enum alt_mode { alt_check, alt_exhaustive, }
#[auto_serialize] #[auto_serialize]
enum expr_ { enum expr_ {
expr_vstore(@expr, vstore), expr_vstore(@expr, expr_vstore),
expr_vec(~[@expr], mutability), expr_vec(~[@expr], mutability),
expr_rec(~[field], Option<@expr>), expr_rec(~[field], Option<@expr>),
expr_call(@expr, ~[@expr], bool), // True iff last argument is a block expr_call(@expr, ~[@expr], bool), // True iff last argument is a block

View file

@ -260,7 +260,7 @@ impl ext_ctxt: ext_ctxt_helpers {
ast::expr_lit( ast::expr_lit(
@{node: ast::lit_str(s), @{node: ast::lit_str(s),
span: span})), span: span})),
ast::vstore_uniq)) ast::expr_vstore_uniq))
} }
fn lit_uint(span: span, i: uint) -> @ast::expr { fn lit_uint(span: span, i: uint) -> @ast::expr {

View file

@ -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); let vecexpr = ast::expr_vec(exprs, ast::m_imm);
mk_expr(cx, sp, vecexpr) 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 { @ast::expr {
mk_expr(cx, sp, ast::expr_vstore(expr, vst)) mk_expr(cx, sp, ast::expr_vstore(expr, vst))
} }
fn mk_uniq_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) -> fn mk_uniq_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
@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]) -> fn mk_fixed_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
@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 { fn mk_base_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr {
let lit = ast::lit_str(@s); let lit = ast::lit_str(@s);
return mk_lit(cx, sp, lit); return mk_lit(cx, sp, lit);
} }
fn mk_uniq_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr { 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, fn mk_rec_e(cx: ext_ctxt, sp: span,

View file

@ -64,7 +64,9 @@ use ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
variant, view_item, view_item_, view_item_export, variant, view_item, view_item_, view_item_export,
view_item_import, view_item_use, view_path, view_path_glob, view_item_import, view_item_use, view_path, view_path_glob,
view_path_list, view_path_simple, visibility, vstore, vstore_box, 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 file_type;
export parser; export parser;
@ -1071,7 +1073,8 @@ impl parser {
None => (), None => (),
Some(v) => { Some(v) => {
hi = self.span.hi; 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 { ex = match e.node {
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _}) expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
if m == m_imm => { 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) _ => expr_addr_of(m, e)
}; };
@ -1386,7 +1389,7 @@ impl parser {
// HACK: turn @[...] into a @-evec // HACK: turn @[...] into a @-evec
ex = match e.node { ex = match e.node {
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _}) 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) _ => expr_unary(box(m), e)
}; };
} }
@ -1398,7 +1401,7 @@ impl parser {
// HACK: turn ~[...] into a ~-evec // HACK: turn ~[...] into a ~-evec
ex = match e.node { ex = match e.node {
expr_vec(*) | expr_lit(@{node: lit_str(_), span: _}) 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) _ => expr_unary(uniq(m), e)
}; };
} }
@ -1849,7 +1852,7 @@ impl parser {
node: expr_lit(@{node: lit_str(_), span: _}), _ node: expr_lit(@{node: lit_str(_), span: _}), _
}) => { }) => {
let vst = @{id: self.get_id(), callee_id: self.get_id(), 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)}; span: mk_sp(lo, hi)};
pat_lit(vst) pat_lit(vst)
} }
@ -1866,7 +1869,7 @@ impl parser {
node: expr_lit(@{node: lit_str(_), span: _}), _ node: expr_lit(@{node: lit_str(_), span: _}), _
}) => { }) => {
let vst = @{id: self.get_id(), callee_id: self.get_id(), 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)}; span: mk_sp(lo, hi)};
pat_lit(vst) pat_lit(vst)
} }
@ -1884,10 +1887,12 @@ impl parser {
pat_lit(e@@{ pat_lit(e@@{
node: expr_lit(@{node: lit_str(_), span: _}), _ node: expr_lit(@{node: lit_str(_), span: _}), _
}) => { }) => {
let vst = @{id: self.get_id(), callee_id: self.get_id(), let vst = @{
node: expr_vstore(e, id: self.get_id(),
vstore_slice(self.region_from_name(None))), callee_id: self.get_id(),
span: mk_sp(lo, hi)}; node: expr_vstore(e, expr_vstore_slice),
span: mk_sp(lo, hi)
};
pat_lit(vst) pat_lit(vst)
} }
_ => pat_region(sub) _ => pat_region(sub)

View file

@ -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_expr(s: ps, &&expr: @ast::expr) {
fn print_field(s: ps, field: ast::field) { fn print_field(s: ps, field: ast::field) {
ibox(s, indent_unit); ibox(s, indent_unit);
@ -992,17 +1002,17 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
let ann_node = node_expr(s, expr); let ann_node = node_expr(s, expr);
s.ann.pre(ann_node); s.ann.pre(ann_node);
match expr.node { match expr.node {
ast::expr_vstore(e, v) => match v { ast::expr_vstore(e, v) => match v {
ast::vstore_fixed(_) => { ast::expr_vstore_fixed(_) => {
print_expr(s, e); print_expr(s, e);
word(s.s, ~"/"); word(s.s, ~"/");
print_vstore(s, v); print_expr_vstore(s, v);
} }
_ => { _ => {
print_vstore(s, v); print_expr_vstore(s, v);
print_expr(s, e); print_expr(s, e);
} }
}, },
ast::expr_vec(exprs, mutbl) => { ast::expr_vec(exprs, mutbl) => {
ibox(s, indent_unit); ibox(s, indent_unit);
word(s.s, ~"["); word(s.s, ~"[");

View file

@ -294,7 +294,7 @@ fn mk_test_desc_vec(cx: test_ctxt) -> @ast::expr {
span: dummy_sp()}; span: dummy_sp()};
return @{id: cx.sess.next_node_id(), return @{id: cx.sess.next_node_id(),
callee_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()}; 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(), let name_expr = {id: cx.sess.next_node_id(),
callee_id: cx.sess.next_node_id(), callee_id: cx.sess.next_node_id(),
node: ast::expr_vstore(name_expr_inner, node: ast::expr_vstore(name_expr_inner,
ast::vstore_uniq), ast::expr_vstore_uniq),
span: dummy_sp()}; span: dummy_sp()};

View file

@ -120,7 +120,7 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
tag_table_spill = 0x5f, tag_table_spill = 0x5f,
tag_table_method_map = 0x60, tag_table_method_map = 0x60,
tag_table_vtable_map = 0x61, tag_table_vtable_map = 0x61,
tag_table_borrowings = 0x62 tag_table_adjustments = 0x62
} }
// djb's cdb hashes. // djb's cdb hashes.

View file

@ -11,8 +11,10 @@ use middle::ty;
use std::map::HashMap; use std::map::HashMap;
use ty::{FnTyBase, FnMeta, FnSig}; 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 parse_bounds_data;
export pstate;
// Compact string representation for ty::t values. API ty_str & // Compact string representation for ty::t values. API ty_str &
// parse_from_str. Extra parameters are for converting to/from def_ids in the // 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); 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, fn parse_ty_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
conv: conv_did) -> ty::t { 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) 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) { fn parse_ret_ty(st: @pstate, conv: conv_did) -> (ast::ret_style, ty::t) {
match peek(st) { match peek(st) {
'!' => { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) } '!' => { 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 { fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
let proto = parse_proto(st); let proto = parse_proto(st);
let purity = parse_purity(next(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) == '['); assert (next(st) == '[');
let mut inputs: ~[ty::arg] = ~[]; let mut inputs: ~[ty::arg] = ~[];
while peek(st) != ']' { while peek(st) != ']' {
let mode = match peek(st) { let mode = parse_mode(st);
'&' => ast::by_mutbl_ref, vec::push(inputs, {mode: mode, ty: parse_ty(st, conv)});
'-' => 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)});
} }
st.pos += 1u; // eat the ']' st.pos += 1u; // eat the ']'
let (ret_style, ret_ty) = parse_ret_ty(st, conv); 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, fn parse_bounds_data(data: @~[u8], start: uint,
crate_num: int, tcx: ty::ctxt, conv: conv_did) crate_num: int, tcx: ty::ctxt, conv: conv_did)
-> @~[ty::param_bound] { -> @~[ty::param_bound]
let st = @{data: data, crate: crate_num, mut pos: start, tcx: tcx}; {
let st = parse_state_from_data(data, crate_num, start, tcx);
parse_bounds(st, conv) parse_bounds(st, conv)
} }

View file

@ -15,6 +15,7 @@ export ac_use_abbrevs;
export enc_ty; export enc_ty;
export enc_bounds; export enc_bounds;
export enc_mode; export enc_mode;
export enc_arg;
type ctxt = { type ctxt = {
diag: span_handler, 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) { fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
match ty::resolved_mode(cx.tcx, m) { match ty::resolved_mode(cx.tcx, m) {
by_mutbl_ref => w.write_char('&'), 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); enc_bounds(w, cx, ft.meta.bounds);
w.write_char('['); w.write_char('[');
for ft.sig.inputs.each |arg| { for ft.sig.inputs.each |arg| {
enc_mode(w, cx, arg.mode); enc_arg(w, cx, arg);
enc_ty(w, cx, arg.ty);
} }
w.write_char(']'); w.write_char(']');
match ft.meta.ret_style { match ft.meta.ret_style {

View file

@ -18,10 +18,8 @@ use std::serialization::DeserializerHelpers;
use std::prettyprint::Serializer; use std::prettyprint::Serializer;
use middle::{ty, typeck}; use middle::{ty, typeck};
use middle::typeck::{method_origin, method_map_entry, use middle::typeck::{method_origin, method_map_entry,
serialize_method_map_entry, vtable_res,
deserialize_method_map_entry, vtable_origin};
vtable_res,
vtable_origin};
use driver::session::session; use driver::session::session;
use middle::freevars::{freevar_entry, use middle::freevars::{freevar_entry,
serialize_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 // 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 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 { impl ebml::EbmlDeserializer: read_method_map_entry_helper {
fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry { fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry {
let mme = deserialize_method_map_entry(self); do self.read_rec {
{derefs: mme.derefs, {self_arg:
self_mode: mme.self_mode, self.read_rec_field(~"self_arg", 0u, || {
origin: mme.origin.tr(xcx)} 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 // Encoding and decoding vtable_res
fn encode_vtable_res(ecx: @e::encode_ctxt, fn encode_vtable_res(ecx: @e::encode_ctxt,
ebml_w: ebml::Writer, ebml_w: ebml::Writer,
dr: typeck::vtable_res) { dr: typeck::vtable_res) {
// can't autogenerate this code because automatic serialization of // can't autogenerate this code because automatic serialization of
// ty::t doesn't work, and there is no way (atm) to have // ty::t doesn't work, and there is no way (atm) to have
// hand-written serialization routines combine with auto-generated // hand-written serialization routines combine with auto-generated
@ -573,6 +629,7 @@ impl @e::encode_ctxt: get_ty_str_ctxt {
} }
trait ebml_writer_helpers { 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_ty(ecx: @e::encode_ctxt, ty: ty::t);
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]); fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]);
fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds); 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 { impl ebml::Writer: ebml_writer_helpers {
fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) { fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) {
e::write_type(ecx, self, ty) do self.emit_opaque {
}
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
do self.emit_from_vec(tys) |ty| {
e::write_type(ecx, self, ty) 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) { 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) { 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) { do ebml_w.tag(c::tag_table_node_type) {
ebml_w.id(id); ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) { 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) { do ebml_w.tag(c::tag_table_method_map) {
ebml_w.id(id); ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) { 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 option::iter(tcx.adjustments.find(id)) |adj| {
do ebml_w.tag(c::tag_table_borrowings) { do ebml_w.tag(c::tag_table_adjustments) {
ebml_w.id(id); ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) { do ebml_w.tag(c::tag_table_val) {
// N.B. We don't actually serialize borrows as, in ty::serialize_AutoAdjustment(ebml_w, *adj)
// trans, we only care whether a value is borrowed or
// not.
} }
} }
} }
@ -782,6 +847,7 @@ impl ebml::Doc: doc_decoder_helpers {
} }
trait ebml_deserializer_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_ty(xcx: extended_decode_ctxt) -> ty::t;
fn read_tys(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]; 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 { 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 { fn read_ty(xcx: extended_decode_ctxt) -> ty::t {
// Note: regions types embed local node ids. In principle, we // Note: regions types embed local node ids. In principle, we
// should translate these node ids into the new decode // should translate these node ids into the new decode
// context. However, we do not bother, because region types // context. However, we do not bother, because region types
// are not used during trans. // are not used during trans.
tydecode::parse_ty_data( do self.read_opaque |doc| {
self.parent.data, xcx.dcx.cdata.cnum, self.pos, xcx.dcx.tcx, tydecode::parse_ty_data(
|a| xcx.tr_def_id(a) ) 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] { 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] { fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound] {
tydecode::parse_bounds_data( do self.read_opaque |doc| {
self.parent.data, self.pos, xcx.dcx.cdata.cnum, xcx.dcx.tcx, tydecode::parse_bounds_data(
|a| xcx.tr_def_id(a) ) 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) 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 { do self.read_rec {
{ {
bounds: self.read_rec_field(~"bounds", 0u, || { 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) { } else if tag == (c::tag_table_vtable_map as uint) {
dcx.maps.vtable_map.insert(id, dcx.maps.vtable_map.insert(id,
val_dsr.read_vtable_res(xcx)); val_dsr.read_vtable_res(xcx));
} else if tag == (c::tag_table_borrowings as uint) { } else if tag == (c::tag_table_adjustments as uint) {
// N.B.: we don't actually *serialize* borrows because, in let adj = @ty::deserialize_AutoAdjustment(val_dsr).tr(xcx);
// trans, the only thing we care about is whether a value was dcx.tcx.adjustments.insert(id, adj);
// borrowed or not.
let borrow = {region: ty::re_static, mutbl: ast::m_imm};
dcx.tcx.borrowings.insert(id, borrow);
} else { } else {
xcx.dcx.tcx.sess.bug( xcx.dcx.tcx.sess.bug(
fmt!("unknown tag found in side tables: %x", tag)); fmt!("unknown tag found in side tables: %x", tag));

View file

@ -221,7 +221,7 @@ use syntax::ast_util;
use syntax::ast_map; use syntax::ast_map;
use syntax::codemap::span; use syntax::codemap::span;
use util::ppaux::{ty_to_str, region_to_str, explain_region, 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::map::{int_hash, HashMap, Set};
use std::list; use std::list;
use std::list::{List, Cons, Nil}; use std::list::{List, Cons, Nil};
@ -318,7 +318,7 @@ enum bckerr_code {
err_mut_uniq, err_mut_uniq,
err_mut_variant, err_mut_variant,
err_root_not_permitted, 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_root_scope(ty::region, ty::region), // superscope, subscope
err_out_of_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 _ => false
} }
} }
err_mutbl(e0a, e1a) => { err_mutbl(e0a) => {
match other { match other {
err_mutbl(e0b, e1b) => e0a == e0b && e1a == e1b, err_mutbl(e0b) => e0a == e0b,
_ => false _ => false
} }
} }
@ -444,10 +444,6 @@ impl borrowck_ctxt {
cat_expr(self.tcx, self.method_map, expr) 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, fn cat_def(id: ast::node_id,
span: span, span: span,
ty: ty::t, ty: ty::t,
@ -482,8 +478,8 @@ impl borrowck_ctxt {
self.span_err( self.span_err(
err.cmt.span, err.cmt.span,
fmt!("illegal borrow: %s", fmt!("illegal borrow: %s",
self.bckerr_code_to_str(err.code))); self.bckerr_to_str(err)));
self.note_and_explain_bckerr(err.code); self.note_and_explain_bckerr(err);
} }
fn span_err(s: span, m: ~str) { fn span_err(s: span, m: ~str) {
@ -506,11 +502,12 @@ impl borrowck_ctxt {
} }
} }
fn bckerr_code_to_str(code: bckerr_code) -> ~str { fn bckerr_to_str(err: bckerr) -> ~str {
match code { match err.code {
err_mutbl(req, act) => { err_mutbl(req) => {
fmt!("creating %s alias to aliasable, %s memory", fmt!("creating %s alias to %s",
self.mut_to_str(req), self.mut_to_str(act)) self.mut_to_str(req),
self.cmt_to_str(err.cmt))
} }
err_mut_uniq => { err_mut_uniq => {
~"unique value in aliasable, mutable location" ~"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 { match code {
err_mutbl(*) | err_mut_uniq | err_mut_variant | err_mutbl(*) | err_mut_uniq | err_mut_variant |
err_root_not_permitted => {} err_root_not_permitted => {}

View file

@ -75,8 +75,7 @@ fn check_loans(bccx: borrowck_ctxt,
enum assignment_type { enum assignment_type {
at_straight_up, at_straight_up,
at_swap, at_swap
at_mutbl_ref,
} }
impl assignment_type : cmp::Eq { impl assignment_type : cmp::Eq {
@ -92,15 +91,13 @@ impl assignment_type {
// are only assigned once; but it doesn't consider &mut // are only assigned once; but it doesn't consider &mut
match self { match self {
at_straight_up => true, at_straight_up => true,
at_swap => true, at_swap => true
at_mutbl_ref => false
} }
} }
fn ing_form(desc: ~str) -> ~str { fn ing_form(desc: ~str) -> ~str {
match self { match self {
at_straight_up => ~"assigning to " + desc, at_straight_up => ~"assigning to " + desc,
at_swap => ~"swapping to and from " + desc, at_swap => ~"swapping to and from " + desc
at_mutbl_ref => ~"taking mut reference to " + desc
} }
} }
} }
@ -369,11 +366,9 @@ impl check_loan_ctxt {
// taking a mutable ref. that will create a loan of its own // taking a mutable ref. that will create a loan of its own
// which will be checked for compat separately in // which will be checked for compat separately in
// check_for_conflicting_loans() // check_for_conflicting_loans()
if at != at_mutbl_ref { for cmt.lp.each |lp| {
for cmt.lp.each |lp| { self.check_for_loan_conflicting_with_assignment(
self.check_for_loan_conflicting_with_assignment( at, ex, cmt, lp);
at, ex, cmt, lp);
}
} }
self.bccx.add_to_mutbl_map(cmt); self.bccx.add_to_mutbl_map(cmt);
@ -430,8 +425,8 @@ impl check_loan_ctxt {
self.tcx().sess.span_err( self.tcx().sess.span_err(
e.cmt.span, e.cmt.span,
fmt!("illegal borrow unless pure: %s", fmt!("illegal borrow unless pure: %s",
self.bccx.bckerr_code_to_str(e.code))); self.bccx.bckerr_to_str(e)));
self.bccx.note_and_explain_bckerr(e.code); self.bccx.note_and_explain_bckerr(e);
self.tcx().sess.span_note( self.tcx().sess.span_note(
sp, sp,
fmt!("impure due to %s", msg)); fmt!("impure due to %s", msg));
@ -531,14 +526,12 @@ impl check_loan_ctxt {
ty::node_id_to_type(self.tcx(), callee_id)); ty::node_id_to_type(self.tcx(), callee_id));
do vec::iter2(args, arg_tys) |arg, arg_ty| { do vec::iter2(args, arg_tys) |arg, arg_ty| {
match ty::resolved_mode(self.tcx(), arg_ty.mode) { match ty::resolved_mode(self.tcx(), arg_ty.mode) {
ast::by_move => { ast::by_move => {
self.check_move_out(arg); self.check_move_out(arg);
} }
ast::by_mutbl_ref => { ast::by_mutbl_ref | ast::by_ref |
self.check_assignment(at_mutbl_ref, arg); ast::by_copy | ast::by_val => {
} }
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, _) => { ast::expr_call(f, args, _) => {
self.check_call(expr, Some(f), f.id, f.span, args); self.check_call(expr, Some(f), f.id, f.span, args);
} }

View file

@ -6,9 +6,9 @@
// their associated scopes. In phase two, checking loans, we will then make // their associated scopes. In phase two, checking loans, we will then make
// sure that all of these loans are honored. // 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 preserve::{preserve_condition, pc_ok, pc_if_pure};
use ty::ty_region; use ty::{ty_region};
export gather_loans; 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())); ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
// If this expression is borrowed, have to ensure it remains valid: // If this expression is borrowed, have to ensure it remains valid:
for tcx.borrowings.find(ex.id).each |borrow| { for tcx.adjustments.find(ex.id).each |adjustments| {
let cmt = self.bccx.cat_borrow_of_expr(ex); self.guarantee_adjustments(ex, adjustments);
self.guarantee_valid(cmt, borrow.mutbl, borrow.region);
} }
// Special checks for various kinds of expressions: // 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); self.guarantee_valid(arg_cmt, m_imm, scope_r);
} }
ast::by_val => { ast::by_val => {
// Rust's by-val does not actually give ownership to // FIXME (#2493): safety checks would be required here,
// the callee. This means that if a pointer type is // but the correct set is really hard to get right,
// passed, it is effectively a borrow, and so the // and modes are going away anyhow.
// 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);
}
}
} }
ast::by_move | ast::by_copy => {} ast::by_move | ast::by_copy => {}
} }
@ -261,6 +224,42 @@ fn req_loans_in_expr(ex: @ast::expr,
impl gather_loan_ctxt { impl gather_loan_ctxt {
fn tcx() -> ty::ctxt { self.bccx.tcx } 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 // guarantees that addr_of(cmt) will be valid for the duration of
// `static_scope_r`, or reports an error. This may entail taking // `static_scope_r`, or reports an error. This may entail taking
// out loans, which will be added to the `req_loan_map`. This can // out loans, which will be added to the `req_loan_map`. This can
@ -387,25 +386,17 @@ impl gather_loan_ctxt {
// mutable memory. // mutable memory.
fn check_mutbl(req_mutbl: ast::mutability, fn check_mutbl(req_mutbl: ast::mutability,
cmt: cmt) -> bckres<preserve_condition> { cmt: cmt) -> bckres<preserve_condition> {
match (req_mutbl, cmt.mutbl) { if req_mutbl == m_const || req_mutbl == cmt.mutbl {
(m_const, _) |
(m_imm, m_imm) |
(m_mutbl, m_mutbl) => {
Ok(pc_ok) Ok(pc_ok)
} } else {
(_, m_const) |
(m_imm, m_mutbl) |
(m_mutbl, m_imm) => {
let e = {cmt: cmt, let e = {cmt: cmt,
code: err_mutbl(req_mutbl, cmt.mutbl)}; code: err_mutbl(req_mutbl)};
if req_mutbl == m_imm { if req_mutbl == m_imm {
// you can treat mutable things as imm if you are pure // you can treat mutable things as imm if you are pure
Ok(pc_if_pure(e)) Ok(pc_if_pure(e))
} else { } else {
Err(e) Err(e)
} }
}
} }
} }

View file

@ -36,23 +36,40 @@ enum loan_ctxt {
impl loan_ctxt { impl loan_ctxt {
fn tcx() -> ty::ctxt { self.bccx.tcx } fn tcx() -> ty::ctxt { self.bccx.tcx }
fn ok_with_loan_of(cmt: cmt, fn issue_loan(cmt: cmt,
scope_ub: ty::region, scope_ub: ty::region,
mutbl: ast::mutability) -> bckres<()> { req_mutbl: ast::mutability) -> bckres<()> {
if self.bccx.is_subregion_of(self.scope_region, scope_ub) { if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
// Note: all cmt's that we deal with will have a non-none match req_mutbl {
// lp, because the entry point into this routine, m_mutbl => {
// `borrowck_ctxt::loan()`, rejects any cmt with a // We do not allow non-mutable data to be loaned
// none-lp. // out as mutable under any circumstances.
(*self.loans).push({lp: option::get(cmt.lp), if cmt.mutbl != m_mutbl {
cmt: cmt, return Err({cmt:cmt,
mutbl: mutbl}); code:err_mutbl(req_mutbl)});
Ok(()) }
}
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 { } else {
// The loan being requested lives longer than the data // The loan being requested lives longer than the data
// being loaned out! // being loaned out!
Err({cmt:cmt, code:err_out_of_scope(scope_ub, return Err({cmt:cmt,
self.scope_region)}) 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) => { cat_local(local_id) | cat_arg(local_id) => {
let local_scope_id = self.tcx().region_map.get(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) => { cat_stack_upvar(cmt) => {
self.loan(cmt, req_mutbl) // NDM correct? self.loan(cmt, req_mutbl) // NDM correct?
@ -138,7 +155,7 @@ impl loan_ctxt {
do self.loan(cmt_base, base_mutbl).chain |_ok| { do self.loan(cmt_base, base_mutbl).chain |_ok| {
// can use static for the scope because the base // can use static for the scope because the base
// determines the lifetime, ultimately // 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. // could change.
do self.loan(cmt_base, m_imm).chain |_ok| { do self.loan(cmt_base, m_imm).chain |_ok| {
// can use static, as in loan_stable_comp() // 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)
} }
} }
} }

View file

@ -169,7 +169,7 @@ priv impl &preserve_ctxt {
} }
Err(e) => { Err(e) => {
debug!("must root @T, err: %s", 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) self.attempt_root(cmt, base, derefs)
} }
} }

View file

@ -40,7 +40,7 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
fn is_str(e: @expr) -> bool { fn is_str(e: @expr) -> bool {
match e.node { match e.node {
expr_vstore(@{node: expr_lit(@{node: lit_str(_), _}), _}, expr_vstore(@{node: expr_lit(@{node: lit_str(_), _}), _},
vstore_uniq) => true, expr_vstore_uniq) => true,
_ => false _ => false
} }
} }
@ -98,8 +98,8 @@ fn check_expr(sess: session, def_map: resolve::DefMap,
} }
} }
} }
expr_vstore(_, vstore_slice(_)) | expr_vstore(_, expr_vstore_slice) |
expr_vstore(_, vstore_fixed(_)) | expr_vstore(_, expr_vstore_fixed(_)) |
expr_vec(_, m_imm) | expr_vec(_, m_imm) |
expr_addr_of(m_imm, _) | expr_addr_of(m_imm, _) |
expr_field(*) | expr_field(*) |

View file

@ -87,12 +87,12 @@ fn classify(e: @expr,
} }
ast::expr_vstore(e, vstore) => { ast::expr_vstore(e, vstore) => {
match vstore { match vstore {
ast::vstore_fixed(_) | ast::expr_vstore_fixed(_) |
ast::vstore_slice(_) => classify(e, def_map, tcx), ast::expr_vstore_slice => classify(e, def_map, tcx),
ast::vstore_uniq | ast::expr_vstore_uniq |
ast::vstore_box => non_const ast::expr_vstore_box => non_const
} }
} }
ast::expr_struct(_, fs, None) | ast::expr_struct(_, fs, None) |

View file

@ -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 // If this is a method call with a by-val argument, we need
// to check the copy // to check the copy
match cx.method_map.find(e.id) { 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, _) => { 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) && !is_nullary_variant(cx, ex) &&
// borrowed unique value isn't really a copy // 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); let ty = ty::expr_ty(cx.tcx, ex);
check_copy(cx, ex.id, ty, ex.span, implicit_copy, why); 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) { fn check_imm_free_var(cx: ctx, def: def, sp: span) {

View file

@ -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( fn cat_expr(
tcx: ty::ctxt, tcx: ty::ctxt,
method_map: typeck::method_map, method_map: typeck::method_map,
@ -420,33 +409,40 @@ struct mem_categorization_ctxt {
} }
impl &mem_categorization_ctxt { impl &mem_categorization_ctxt {
fn cat_borrow_of_expr(expr: @ast::expr) -> cmt { fn cat_expr(expr: @ast::expr) -> cmt {
// Any expression can be borrowed (to account for auto-ref on method match self.tcx.adjustments.find(expr.id) {
// receivers), but @, ~, @vec, and ~vec are handled specially. None => {
let expr_ty = ty::expr_ty(self.tcx, expr); // No adjustments.
match ty::get(expr_ty).sty { self.cat_expr_unadjusted(expr)
ty::ty_evec(*) | ty::ty_estr(*) => { }
self.cat_index(expr, expr)
}
ty::ty_uniq(*) | ty::ty_box(*) | ty::ty_rptr(*) => { Some(adjustment) => {
let cmt = self.cat_expr(expr); match adjustment.autoref {
self.cat_deref(expr, cmt, 0u, true).get() Some(_) => {
} // Equivalent to &*expr or something similar.
// This is an rvalue, effectively.
/* let expr_ty = ty::expr_ty(self.tcx, expr);
ty::ty_fn({proto, _}) { self.cat_rvalue(expr, expr_ty)
self.cat_call(expr, expr, proto) }
} None => {
*/ // Equivalent to *expr or something similar.
self.cat_expr_autoderefd(expr, adjustment)
_ => { }
self.cat_rvalue(expr, expr_ty) }
} }
} }
} }
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", debug!("cat_expr: id=%d expr=%s",
expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); 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); let base_cmt = self.cat_expr(e_base);
match self.cat_deref(expr, base_cmt, 0u, true) { self.cat_deref(expr, base_cmt, 0)
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))));
}
}
} }
ast::expr_field(base, f_name, _) => { ast::expr_field(base, f_name, _) => {
@ -475,7 +463,7 @@ impl &mem_categorization_ctxt {
return self.cat_method_ref(expr, expr_ty); 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) self.cat_field(expr, base_cmt, f_name)
} }
@ -484,7 +472,8 @@ impl &mem_categorization_ctxt {
return self.cat_rvalue(expr, expr_ty); 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(_) => { ast::expr_path(_) => {
@ -666,11 +655,21 @@ impl &mem_categorization_ctxt {
mutbl: m, ty: self.tcx.ty(node)} mutbl: m, ty: self.tcx.ty(node)}
} }
fn cat_deref<N:ast_node>(node: N, base_cmt: cmt, derefs: uint, fn cat_deref<N:ast_node>(node: N,
expl: bool) -> Option<cmt> { base_cmt: cmt,
do ty::deref(self.tcx, base_cmt.ty, expl).map |mt| { deref_cnt: uint) -> cmt {
match deref_kind(self.tcx, base_cmt.ty) { let mt = match ty::deref(self.tcx, base_cmt.ty, true) {
deref_ptr(ptr) => { 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| { let lp = do base_cmt.lp.chain |l| {
// Given that the ptr itself is loanable, we can // Given that the ptr itself is loanable, we can
// loan out deref'd uniq ptrs as the data they are // 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 // Other ptr types admit aliases and are therefore
// not loanable. // not loanable.
match ptr { match ptr {
uniq_ptr => {Some(@lp_deref(l, ptr))} uniq_ptr => {Some(@lp_deref(l, ptr))}
gc_ptr | region_ptr(_) | unsafe_ptr => {None} gc_ptr | region_ptr(_) | unsafe_ptr => {None}
} }
}; };
// for unique ptrs, we inherit mutability from the // for unique ptrs, we inherit mutability from the
// owning reference. // owning reference.
let m = match ptr { let m = match ptr {
uniq_ptr => { uniq_ptr => {
self.inherited_mutability(base_cmt.mutbl, mt.mutbl) self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
} }
gc_ptr | region_ptr(_) | unsafe_ptr => { gc_ptr | region_ptr(_) | unsafe_ptr => {
mt.mutbl mt.mutbl
} }
}; };
@{id:node.id(), span:node.span(), @{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} mutbl:m, ty:mt.ty}
} }
deref_comp(comp) => { deref_comp(comp) => {
let lp = base_cmt.lp.map(|l| @lp_comp(l, comp) ); let lp = base_cmt.lp.map(|l| @lp_comp(l, comp) );
let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
@{id:node.id(), span:node.span(), @{id:node.id(), span:node.span(),
cat:cat_comp(base_cmt, comp), lp:lp, cat:cat_comp(base_cmt, comp), lp:lp,
mutbl:m, ty:mt.ty} mutbl:m, ty:mt.ty}
}
} }
} }
} }
fn cat_index(expr: @ast::expr, base: @ast::expr) -> cmt { fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt {
let base_cmt = self.cat_autoderef(base);
let mt = match ty::index(self.tcx, base_cmt.ty) { let mt = match ty::index(self.tcx, base_cmt.ty) {
Some(mt) => mt, Some(mt) => mt,
None => { None => {
@ -781,25 +777,6 @@ impl &mem_categorization_ctxt {
mutbl:m_imm, ty:expr_ty} 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)) { fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
op(cmt, pat); op(cmt, pat);
@ -900,15 +877,9 @@ impl &mem_categorization_ctxt {
ast::pat_box(subpat) | ast::pat_uniq(subpat) | ast::pat_box(subpat) | ast::pat_uniq(subpat) |
ast::pat_region(subpat) => { ast::pat_region(subpat) => {
// @p1, ~p1, &p1 // @p1, ~p1
match self.cat_deref(subpat, cmt, 0u, true) { let subcmt = self.cat_deref(subpat, cmt, 0);
Some(subcmt) => { self.cat_pattern(subcmt, subpat, op);
self.cat_pattern(subcmt, subpat, op);
}
None => {
tcx.sess.span_bug(pat.span, ~"Non derefable type");
}
}
} }
ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ } ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ }

View file

@ -500,7 +500,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
let Result {bcx: guard_cx, val} = { let Result {bcx: guard_cx, val} = {
do with_scope_result(bcx, e.info(), ~"guard") |bcx| { do with_scope_result(bcx, e.info(), ~"guard") |bcx| {
expr::trans_to_appropriate_llval(bcx, e) expr::trans_to_datum(bcx, e).to_result()
} }
}; };

View file

@ -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| { do vec::iteri(arg_exprs) |i, arg_expr| {
let arg_val = unpack_result!(bcx, { let arg_val = unpack_result!(bcx, {
trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups, trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups,
if i == last { ret_flag } else { None }, if i == last { ret_flag } else { None })
0u)
}); });
vec::push(llargs, arg_val); vec::push(llargs, arg_val);
} }
@ -480,18 +479,17 @@ fn trans_arg_expr(bcx: block,
formal_ty: ty::arg, formal_ty: ty::arg,
arg_expr: @ast::expr, arg_expr: @ast::expr,
temp_cleanups: &mut ~[ValueRef], temp_cleanups: &mut ~[ValueRef],
ret_flag: Option<ValueRef>, ret_flag: Option<ValueRef>)
derefs: uint)
-> Result -> Result
{ {
let _icx = bcx.insn_ctxt("trans_arg_expr"); let _icx = bcx.insn_ctxt("trans_arg_expr");
let ccx = bcx.ccx(); let ccx = bcx.ccx();
debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \ 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), formal_ty.mode, bcx.ty_to_str(formal_ty.ty),
bcx.expr_to_str(arg_expr), 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(); let _indenter = indenter();
// translate the arg expr to a datum // 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 arg_datum = arg_datumblock.datum;
let mut bcx = arg_datumblock.bcx; let mut bcx = arg_datumblock.bcx;
debug!(" initial value: %s", arg_datum.to_str(bcx.ccx())); debug!(" arg datum: %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()));
// finally, deal with the various modes // finally, deal with the various modes
let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode); 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); 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)
}
}

View file

@ -604,7 +604,7 @@ impl block {
} }
fn expr_to_str(e: @ast::expr) -> ~str { 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 { fn expr_is_lval(e: @ast::expr) -> bool {
@ -1180,13 +1180,17 @@ fn path_str(sess: session::session, p: path) -> ~str {
r 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 { fn node_id_type(bcx: block, id: ast::node_id) -> ty::t {
let tcx = bcx.tcx(); let tcx = bcx.tcx();
let t = ty::node_id_to_type(tcx, id); let t = ty::node_id_to_type(tcx, id);
match bcx.fcx.param_substs { monomorphize_type(bcx, t)
Some(substs) => ty::subst_tps(tcx, substs.tys, t),
_ => { assert !ty::type_has_params(t); t }
}
} }
fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t { fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {

View file

@ -312,10 +312,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
let (v, _, _) = const_vec(cx, e, es); let (v, _, _) = const_vec(cx, e, es);
v v
} }
ast::expr_vstore(e, ast::vstore_fixed(_)) => { ast::expr_vstore(e, ast::expr_vstore_fixed(_)) => {
const_expr(cx, e) const_expr(cx, e)
} }
ast::expr_vstore(sub, ast::vstore_slice(_)) => { ast::expr_vstore(sub, ast::expr_vstore_slice) => {
match sub.node { match sub.node {
ast::expr_lit(lit) => { ast::expr_lit(lit) => {
match lit.node { match lit.node {

View file

@ -41,7 +41,7 @@ fn trans_if(bcx: block,
let _icx = bcx.insn_ctxt("trans_if"); let _icx = bcx.insn_ctxt("trans_if");
let Result {bcx, val: cond_val} = 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 then_bcx_in = scope_block(bcx, thn.info(), ~"then");
let else_bcx_in = scope_block(bcx, els.info(), ~"else"); 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 // compile the condition
let Result {bcx: cond_bcx_out, val: cond_val} = 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 = let cond_bcx_out =
trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in)); trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb); 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 current_level = Load(bcx, global);
let level = unpack_result!(bcx, { let level = unpack_result!(bcx, {
do with_scope_result(bcx, lvl.info(), ~"level") |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"; + ~" failed";
let Result {bcx, val} = { let Result {bcx, val} = {
do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| { 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| { do with_cond(bcx, Not(bcx, val)) |bcx| {

View file

@ -174,8 +174,12 @@ fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
/*! /*!
* *
* Allocates temporary space on the stack using alloca() and * Allocates temporary space on the stack using alloca() and
* returns a by-ref Datum pointing to it. You must arrange * returns a by-ref Datum pointing to it. If `zero` is true, the
* any cleanups etc yourself! */ * 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 llty = type_of::type_of(bcx.ccx(), ty);
let scratch = alloca_maybe_zeroed(bcx, llty, zero); let scratch = alloca_maybe_zeroed(bcx, llty, zero);

View file

@ -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 situation, but there are a few other functions that are useful
in specific scenarios: 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 - `trans_lvalue()` is exactly like `trans_to_datum()` but it only
works on lvalues. This is mostly used as an assertion for those works on lvalues. This is mostly used as an assertion for those
places where only an lvalue is expected. It also guarantees that 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 value is written into the given destination and is assumed to be owned
by that destination. by that destination.
When you invoke `trans_to_datum()` or `trans_to_appropriate_llval()` When you invoke `trans_to_datum()` on an rvalue, the resulting
on an rvalue, the resulting datum/value will have an appropriate datum/value will have an appropriate cleanup scheduled for the
cleanup scheduled for the innermost cleanup scope. If you later use innermost cleanup scope. If you later use `move_to()` or
`move_to()` or `drop_val()`, this cleanup will be canceled. `drop_val()`, this cleanup will be canceled.
During the evaluation of an expression, temporary cleanups are created During the evaluation of an expression, temporary cleanups are created
and later canceled. These represent intermediate or partial results and later canceled. These represent intermediate or partial results
@ -112,21 +109,22 @@ use base::*;
use syntax::print::pprust::{expr_to_str}; use syntax::print::pprust::{expr_to_str};
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
use util::common::indenter; use util::common::indenter;
use ty::{AutoPtr, AutoSlice};
// The primary two functions for translating expressions: // The primary two functions for translating expressions:
export trans_to_datum, trans_into; 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 Dest, SaveIn, Ignore;
export cast_type_kind; export cast_type_kind;
export cast_kind, cast_pointer, cast_integral, cast_float; export cast_kind, cast_pointer, cast_integral, cast_float;
export cast_enum, cast_other; 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 // Destinations
// These are passed around by the code generating functions to track the // 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) } pure fn ne(&&other: Dest) -> bool { !self.eq(other) }
} }
fn trans_to_appropriate_llval(bcx: block, fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
expr: @ast::expr) -> common::Result { debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
let mut bcx = bcx;
let datum = unpack_datum!(bcx, trans_to_datum(bcx, expr)); return match bcx.tcx().adjustments.find(expr.id) {
debug!("trans_to_appropriate_llval(): datum=%s", datum.to_str(bcx.ccx())); None => {
rslt(bcx, datum.to_appropriate_llval(bcx)) 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 * 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; 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(); let _indenter = indenter();
debuginfo::update_source_pos(bcx, expr.span); debuginfo::update_source_pos(bcx, expr.span);
match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) { match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
ty::LvalueExpr => { ty::LvalueExpr => {
return trans_lvalue(bcx, expr); return trans_lvalue_unadjusted(bcx, expr);
} }
ty::RvalueDatumExpr => { 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); datum.add_clean(bcx);
return DatumBlock {bcx: bcx, datum: datum}; return DatumBlock {bcx: bcx, datum: datum};
} }
ty::RvalueStmtExpr => { ty::RvalueStmtExpr => {
bcx = trans_rvalue_stmt(bcx, expr); bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
return nil(bcx, expr_ty(bcx, expr)); return nil(bcx, expr_ty(bcx, expr));
} }
ty::RvalueDpsExpr => { ty::RvalueDpsExpr => {
let ty = expr_ty(bcx, expr); let ty = expr_ty(bcx, expr);
if ty::type_is_nil(ty) || ty::type_is_bot(ty) { 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); return nil(bcx, ty);
} else { } else {
let scratch = scratch_datum(bcx, ty, false); 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 // Note: this is not obviously a good idea. It causes
// immediate values to be loaded immediately after a // 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); 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), bcx.expr_to_str(expr),
dest.to_str(bcx.ccx())); dest.to_str(bcx.ccx()));
let _indenter = indenter(); let _indenter = indenter();
@ -253,39 +337,39 @@ fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
debug!("expr kind = %?", kind); debug!("expr kind = %?", kind);
match kind { match kind {
ty::LvalueExpr => { ty::LvalueExpr => {
let datumblock = trans_lvalue(bcx, expr); let datumblock = trans_lvalue_unadjusted(bcx, expr);
match dest { match dest {
Ignore => datumblock.bcx, Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(INIT, lldest) SaveIn(lldest) => datumblock.store_to(INIT, lldest)
} }
} }
ty::RvalueDatumExpr => { ty::RvalueDatumExpr => {
let datumblock = trans_rvalue_datum(bcx, expr); let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
match dest { match dest {
Ignore => datumblock.drop_val(), Ignore => datumblock.drop_val(),
SaveIn(lldest) => datumblock.store_to(INIT, lldest) SaveIn(lldest) => datumblock.store_to(INIT, lldest)
} }
} }
ty::RvalueDpsExpr => { ty::RvalueDpsExpr => {
return trans_rvalue_dps(bcx, expr, dest); return trans_rvalue_dps_unadjusted(bcx, expr, dest);
} }
ty::RvalueStmtExpr => { 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 { fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
let _icx = bcx.insn_ctxt("trans_rvalue_datum"); let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
match expr.node { 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, return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared,
expr, contents); 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, return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
expr, contents); expr, contents);
} }
@ -310,13 +394,14 @@ fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
_ => { _ => {
bcx.tcx().sess.span_bug( bcx.tcx().sess.span_bug(
expr.span, expr.span,
fmt!("trans_rvalue_datum reached fall-through case: %?", fmt!("trans_rvalue_datum_unadjusted reached \
fall-through case: %?",
expr.node)); 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 mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rvalue_stmt"); 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( bcx.tcx().sess.span_bug(
expr.span, expr.span,
fmt!("trans_rvalue_stmt reached fall-through case: %?", fmt!("trans_rvalue_stmt_unadjusted reached \
fall-through case: %?",
expr.node)); 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 mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rvalue_dps"); let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted");
let tcx = bcx.tcx(); let tcx = bcx.tcx();
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
match expr.node { match expr.node {
ast::expr_path(_) => { 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) => { ast::expr_if(cond, thn, els) => {
return controlflow::trans_if(bcx, cond, thn, els, dest); 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), _}) => { ast::expr_lit(@{node: ast::lit_str(s), _}) => {
return tvec::trans_lit_str(bcx, expr, s, dest); 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); 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); return tvec::trans_fixed_vstore(bcx, expr, contents, dest);
} }
ast::expr_vec(*) | ast::expr_repeat(*) => { 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( bcx.tcx().sess.span_bug(
expr.span, expr.span,
fmt!("trans_rvalue_dps reached fall-through case: %?", fmt!("trans_rvalue_dps_unadjusted reached \
fall-through case: %?",
expr.node)); expr.node));
} }
} }
} }
fn trans_def_dps(bcx: block, ref_expr: @ast::expr, fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
def: ast::def, dest: Dest) -> block { def: ast::def, dest: Dest) -> block {
let _icx = bcx.insn_ctxt("trans_def_dps"); let _icx = bcx.insn_ctxt("trans_def_dps_unadjusted");
let ccx = bcx.ccx(); let ccx = bcx.ccx();
let lldest = match dest { 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 { fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
//! /*!
// *
// Translates an lvalue expression, always yielding a by-ref * Translates an lvalue expression, always yielding a by-ref
// datum. Generally speaking you should call trans_to_datum() * datum. Generally speaking you should call trans_to_datum()
// instead, but sometimes we call trans_lvalue() directly as a * instead, but sometimes we call trans_lvalue() directly as a
// means of asserting that a particular expression is an lvalue. * means of asserting that a particular expression is an lvalue. */
let _icx = bcx.insn_ctxt("trans_lval"); let _icx = bcx.insn_ctxt("trans_lval");
let mut bcx = bcx; let mut bcx = bcx;
@ -798,12 +887,7 @@ fn trans_rec_field(bcx: block,
let mut bcx = bcx; let mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rec_field"); 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 = 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| { do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| {
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys); let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
DatumBlock { DatumBlock {
@ -822,14 +906,11 @@ fn trans_index(bcx: block,
let base_ty = expr_ty(bcx, base); let base_ty = expr_ty(bcx, base);
let mut bcx = bcx; 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 = 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. // Translate index expression and cast to a suitable LLVM integer.
// Rust is less strict than LLVM in this regard. // 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 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 int_size = shape::llsize_of_real(bcx.ccx(), ccx.int_type);
let ix_val = { let ix_val = {
@ -989,11 +1070,11 @@ fn trans_unary_datum(bcx: block,
return match op { return match op {
ast::not => { 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) immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty)
} }
ast::neg => { 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 = { let llneg = {
if ty::type_is_fp(un_ty) { if ty::type_is_fp(un_ty) {
FNeg(bcx, val) FNeg(bcx, val)
@ -1153,7 +1234,7 @@ fn trans_lazy_binop(bcx: block,
let Result {bcx: past_lhs, val: lhs} = { let Result {bcx: past_lhs, val: lhs} = {
do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| { 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} = { let Result {bcx: past_rhs, val: rhs} = {
do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| { 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 t_out = node_id_type(bcx, id);
let mut bcx = bcx; 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 ll_t_in = val_ty(llexpr);
let t_in = expr_ty(bcx, expr); let t_in = expr_ty(bcx, expr);
let ll_t_out = type_of::type_of(ccx, t_out); let ll_t_out = type_of::type_of(ccx, t_out);

View file

@ -95,18 +95,17 @@ fn trans_method(ccx: @crate_ctxt,
fn trans_self_arg(bcx: block, base: @ast::expr, fn trans_self_arg(bcx: block, base: @ast::expr,
mentry: typeck::method_map_entry) -> Result { mentry: typeck::method_map_entry) -> Result {
let _icx = bcx.insn_ctxt("impl::trans_self_arg"); 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 mut temp_cleanups = ~[];
let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base, let self_arg = {mode: mentry.self_arg.mode,
&mut temp_cleanups, None, mentry.derefs); 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 // FIXME(#3446)---this is wrong, actually. The temp_cleanups
// other arguments failing: // should be revoked only after all arguments have been passed.
//assert temp_cleanups == ~[]; for temp_cleanups.each |c| {
//do vec::iter(temp_cleanups) |c| { revoke_clean(bcx, c)
// revoke_clean(bcx, c) }
//}
return result; return result;
} }
@ -120,14 +119,14 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
typeck::method_static(did) => { typeck::method_static(did) => {
let callee_fn = callee::trans_fn_ref(bcx, did, callee_id); let callee_fn = callee::trans_fn_ref(bcx, did, callee_id);
let Result {bcx, val} = trans_self_arg(bcx, self, mentry); let Result {bcx, val} = trans_self_arg(bcx, self, mentry);
let tcx = bcx.tcx();
Callee { Callee {
bcx: bcx, bcx: bcx,
data: Method(MethodData { data: Method(MethodData {
llfn: callee_fn.llfn, llfn: callee_fn.llfn,
llself: val, llself: val,
self_ty: node_id_type(bcx, self.id), 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) => { 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( let vtbls = resolve_vtables_in_fn_ctxt(
bcx.fcx, ccx.maps.vtable_map.get(callee_id)); 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) => { typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
let mth_id = method_with_name(bcx.ccx(), impl_did, mname); 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); let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty);
// combine the self environment with the rest // combine the self environment with the rest
let tcx = bcx.tcx();
Callee { Callee {
bcx: bcx, bcx: bcx,
data: Method(MethodData { data: Method(MethodData {
llfn: llfn_val, llfn: llfn_val,
llself: llself_val, llself: llself_val,
self_ty: node_id_type(bcx, base.id), 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(*) => { 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(*) => { typeck::vtable_param(*) => {
fail ~"vtable_param left in monomorphized function's vtable substs"; 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, fn trans_trait_callee(bcx: block,
callee_id: ast::node_id, callee_id: ast::node_id,
n_method: uint, n_method: uint,
self_expr: @ast::expr, self_expr: @ast::expr)
autoderefs: uint)
-> Callee -> Callee
{ {
//! //!
@ -404,7 +405,6 @@ fn trans_trait_callee(bcx: block,
let _icx = bcx.insn_ctxt("impl::trans_trait_callee"); let _icx = bcx.insn_ctxt("impl::trans_trait_callee");
let mut bcx = bcx; let mut bcx = bcx;
let self_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr)); 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 llpair = self_datum.to_ref_llval(bcx);
let callee_ty = node_id_type(bcx, callee_id); let callee_ty = node_id_type(bcx, callee_id);
trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair) trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair)

View file

@ -19,7 +19,9 @@ use syntax::ast::*;
use syntax::print::pprust::*; use syntax::print::pprust::*;
use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str}; use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
use std::serialization::{serialize_Option, use std::serialization::{serialize_Option,
deserialize_Option}; deserialize_Option,
serialize_uint,
deserialize_uint};
export TyVid, IntVid, FnVid, RegionVid, vid; export TyVid, IntVid, FnVid, RegionVid, vid;
export br_hashmap; export br_hashmap;
@ -155,6 +157,7 @@ export closure_kind;
export ck_block; export ck_block;
export ck_box; export ck_box;
export ck_uniq; export ck_uniq;
export param_ty;
export param_bound, param_bounds, bound_copy, bound_owned; export param_bound, param_bounds, bound_copy, bound_owned;
export param_bounds_to_str, param_bound_to_str; export param_bounds_to_str, param_bound_to_str;
export bound_send, bound_trait; export bound_send, bound_trait;
@ -192,6 +195,8 @@ export opt_region_variance;
export serialize_opt_region_variance, deserialize_opt_region_variance; export serialize_opt_region_variance, deserialize_opt_region_variance;
export determine_inherited_purity; export determine_inherited_purity;
export provided_trait_methods; export provided_trait_methods;
export AutoAdjustment, serialize_AutoAdjustment, deserialize_AutoAdjustment;
export AutoRef, AutoRefKind, AutoSlice, AutoPtr;
// Data types // Data types
@ -287,19 +292,26 @@ impl region_variance: cmp::Eq {
pure fn ne(&&other: region_variance) -> bool { !self.eq(other) } pure fn ne(&&other: region_variance) -> bool { !self.eq(other) }
} }
// N.B.: Borrows from inlined content are not accurately deserialized. This #[auto_serialize]
// is because we don't need the details in trans, we only care if there is an type AutoAdjustment = {
// entry in the table or not. autoderefs: uint,
type borrow = { autoref: Option<AutoRef>
region: ty::region, };
#[auto_serialize]
type AutoRef = {
kind: AutoRefKind,
region: region,
mutbl: ast::mutability mutbl: ast::mutability
}; };
impl borrow : cmp::Eq { #[auto_serialize]
pure fn eq(&&other: borrow) -> bool { enum AutoRefKind {
self.region == other.region && self.mutbl == other.mutbl /// Convert from @[]/~[] to &[] (or str)
} AutoSlice,
pure fn ne(&&other: borrow) -> bool { !self.eq(other) }
/// Convert from T to &T
AutoPtr
} }
type ctxt = type ctxt =
@ -340,8 +352,7 @@ type ctxt =
trait_method_cache: HashMap<def_id, @~[method]>, trait_method_cache: HashMap<def_id, @~[method]>,
ty_param_bounds: HashMap<ast::node_id, param_bounds>, ty_param_bounds: HashMap<ast::node_id, param_bounds>,
inferred_modes: HashMap<ast::node_id, ast::mode>, inferred_modes: HashMap<ast::node_id, ast::mode>,
// maps the id of borrowed expr to scope of borrowed ptr adjustments: HashMap<ast::node_id, @AutoAdjustment>,
borrowings: HashMap<ast::node_id, borrow>,
normalized_cache: HashMap<t, t>, normalized_cache: HashMap<t, t>,
lang_items: middle::lang_items::LanguageItems}; lang_items: middle::lang_items::LanguageItems};
@ -496,6 +507,7 @@ impl param_ty : to_bytes::IterBytes {
/// Representation of regions: /// Representation of regions:
#[auto_serialize]
enum region { enum region {
/// Bound regions are found (primarily) in function types. They indicate /// Bound regions are found (primarily) in function types. They indicate
/// region parameters that have yet to be replaced with actual regions /// region parameters that have yet to be replaced with actual regions
@ -523,6 +535,7 @@ enum region {
re_var(RegionVid) re_var(RegionVid)
} }
#[auto_serialize]
enum bound_region { enum bound_region {
/// The self region for classes, impls (&T in a type defn or &self/T) /// The self region for classes, impls (&T in a type defn or &self/T)
br_self, br_self,
@ -533,35 +546,37 @@ enum bound_region {
/// Named region parameters for functions (a in &a/T) /// Named region parameters for functions (a in &a/T)
br_named(ast::ident), 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 * Handles capture-avoiding substitution in a rather subtle case. If you
/// expected type, and the expected type includes bound regions, then we * have a closure whose argument types are being inferred based on the
/// will wrap those bound regions in a br_cap_avoid() with the id of the * expected type, and the expected type includes bound regions, then we
/// fn expression. This ensures that the names are not "captured" by the * will wrap those bound regions in a br_cap_avoid() with the id of the
/// enclosing scope, which may define the same names. For an example of * fn expression. This ensures that the names are not "captured" by the
/// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs * enclosing scope, which may define the same names. For an example of
/// and regions-ret-borrowed-1.rs. * 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), br_cap_avoid(ast::node_id, @bound_region),
} }
type opt_region = Option<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 * The type substs represents the kinds of things that can be substituted to
/// regions other than `self` is done through a different mechanism. * 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. * - `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` * - `self_r` indicates the region parameter `self` that is present on nominal
/// should always be none for types that are not region-parameterized and * types (enums, classes) declared as having a region parameter. `self_r`
/// Some(_) for types that are. The only bound region parameter that should * should always be none for types that are not region-parameterized and
/// appear within a region-parameterized type is `self`. * 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 * - `self_ty` is the type to which `self` should be remapped, if any. The
/// is always substituted away to the implementing type for a trait. * `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 = { type substs = {
self_r: opt_region, self_r: opt_region,
self_ty: Option<ty::t>, self_ty: Option<ty::t>,
@ -650,6 +665,7 @@ enum param_bound {
enum TyVid = uint; enum TyVid = uint;
enum IntVid = uint; enum IntVid = uint;
enum FnVid = uint; enum FnVid = uint;
#[auto_serialize]
enum RegionVid = uint; enum RegionVid = uint;
enum InferTy { enum InferTy {
@ -842,7 +858,7 @@ fn mk_ctxt(s: session::session,
trait_method_cache: new_def_hash(), trait_method_cache: new_def_hash(),
ty_param_bounds: map::int_hash(), ty_param_bounds: map::int_hash(),
inferred_modes: map::int_hash(), inferred_modes: map::int_hash(),
borrowings: map::int_hash(), adjustments: map::int_hash(),
normalized_cache: new_ty_hash(), normalized_cache: new_ty_hash(),
lang_items: move lang_items} 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=%?)", fmt!("substs(self_r=%s, self_ty=%s, tps=%?)",
substs.self_r.map_default(~"none", |r| region_to_str(cx, r)), 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.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: &param_bound) -> ~str { fn param_bound_to_str(cx: ctxt, pb: &param_bound) -> ~str {
@ -2939,8 +2955,8 @@ fn expr_kind(tcx: ctxt,
ast::expr_unary_move(*) | ast::expr_unary_move(*) |
ast::expr_repeat(*) | ast::expr_repeat(*) |
ast::expr_lit(@{node: lit_str(_), _}) | ast::expr_lit(@{node: lit_str(_), _}) |
ast::expr_vstore(_, ast::vstore_slice(_)) | ast::expr_vstore(_, ast::expr_vstore_slice) |
ast::expr_vstore(_, ast::vstore_fixed(_)) | ast::expr_vstore(_, ast::expr_vstore_fixed(_)) |
ast::expr_vec(*) => { ast::expr_vec(*) => {
RvalueDpsExpr RvalueDpsExpr
} }
@ -2990,8 +3006,8 @@ fn expr_kind(tcx: ctxt,
ast::expr_unary(*) | ast::expr_unary(*) |
ast::expr_addr_of(*) | ast::expr_addr_of(*) |
ast::expr_binary(*) | ast::expr_binary(*) |
ast::expr_vstore(_, ast::vstore_box) | ast::expr_vstore(_, ast::expr_vstore_box) |
ast::expr_vstore(_, ast::vstore_uniq) => { ast::expr_vstore(_, ast::expr_vstore_uniq) => {
RvalueDatumExpr RvalueDatumExpr
} }

View file

@ -60,7 +60,7 @@ use std::serialization::{serialize_uint, deserialize_uint};
use vec::each; use vec::each;
use syntax::print::pprust::*; use syntax::print::pprust::*;
use util::ppaux::{ty_to_str, tys_to_str, region_to_str, 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 util::common::{indent, indenter};
use std::list; use std::list;
use list::{List, Nil, Cons}; use list::{List, Nil, Cons};
@ -86,7 +86,7 @@ enum method_origin {
// method invoked on a type parameter with a bounded trait // method invoked on a type parameter with a bounded trait
method_param(method_param), method_param(method_param),
// method invoked on a boxed trait // method invoked on a trait instance
method_trait(ast::def_id, uint), method_trait(ast::def_id, uint),
} }
@ -108,13 +108,10 @@ type method_param = {
bound_num: uint bound_num: uint
}; };
#[auto_serialize]
type method_map_entry = { type method_map_entry = {
// number of derefs that are required on the receiver // the type and mode of the self parameter, which is not reflected
derefs: uint, // in the fn type (FIXME #3446)
self_arg: ty::arg,
// the mode by which the self parameter needs to be passed
self_mode: ast::rmode,
// method details being invoked // method details being invoked
origin: method_origin origin: method_origin

View file

@ -102,7 +102,7 @@ struct inherited {
locals: HashMap<ast::node_id, TyVid>, locals: HashMap<ast::node_id, TyVid>,
node_types: HashMap<ast::node_id, ty::t>, node_types: HashMap<ast::node_id, ty::t>,
node_type_substs: HashMap<ast::node_id, ty::substs>, 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 { struct fn_ctxt {
@ -143,7 +143,7 @@ fn blank_inherited(ccx: @crate_ctxt) -> @inherited {
locals: int_hash(), locals: int_hash(),
node_types: map::int_hash(), node_types: map::int_hash(),
node_type_substs: 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()); node_id, ty_to_str(self.tcx(), ty), self.tag());
self.inh.node_types.insert(node_id, ty); self.inh.node_types.insert(node_id, ty);
} }
fn write_substs(node_id: ast::node_id, +substs: ty::substs) { fn write_substs(node_id: ast::node_id, +substs: ty::substs) {
if !ty::substs_is_noop(&substs) { if !ty::substs_is_noop(&substs) {
debug!("write_substs(%d, %s) in fcx %s", debug!("write_substs(%d, %s) in fcx %s",
@ -613,12 +614,24 @@ impl @fn_ctxt {
self.inh.node_type_substs.insert(node_id, substs); self.inh.node_type_substs.insert(node_id, substs);
} }
} }
fn write_ty_substs(node_id: ast::node_id, ty: ty::t, fn write_ty_substs(node_id: ast::node_id, ty: ty::t,
+substs: ty::substs) { +substs: ty::substs) {
let ty = ty::subst(self.tcx(), &substs, ty); let ty = ty::subst(self.tcx(), &substs, ty);
self.write_ty(node_id, ty); self.write_ty(node_id, ty);
self.write_substs(node_id, substs); 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) { fn write_nil(node_id: ast::node_id) {
self.write_ty(node_id, ty::mk_nil(self.tcx())); 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) 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 { fn expr_ty(ex: @ast::expr) -> ty::t {
match self.inh.node_types.find(ex.id) { match self.inh.node_types.find(ex.id) {
Some(t) => t, Some(t) => t,
None => { None => {
self.tcx().sess.bug( self.tcx().sess.bug(
fmt!("no type for expr %d (%s) in fcx %s", fmt!("no type for %s in fcx %s",
ex.id, expr_to_str(ex, self.ccx.tcx.sess.intr()), self.expr_to_str(ex), self.tag()));
self.tag()));
} }
} }
} }
@ -691,22 +707,15 @@ impl @fn_ctxt {
infer::can_mk_subty(self.infcx(), sub, sup) infer::can_mk_subty(self.infcx(), sub, sup)
} }
fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id, fn mk_assignty(expr: @ast::expr, sub: ty::t, sup: ty::t)
sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> { -> Result<(), ty::type_err>
{
match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) { match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) {
Ok(None) => result::Ok(()), Ok(None) => result::Ok(()),
Err(e) => result::Err(e), Err(e) => result::Err(e),
Ok(Some(borrow)) => { Ok(Some(adjustment)) => {
match self.mk_subr(true, expr.span, self.write_adjustment(expr.id, adjustment);
ty::re_scope(borrow_lb), borrow.region) { Ok(())
Err(e) => Err(e),
Ok(()) => {
debug!("inserting borrowing of expr %?: %?",
expr.id, borrow);
self.inh.borrowings.insert(expr.id, borrow);
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 t1 = t;
let mut enum_dids = ~[]; let mut enum_dids = ~[];
let mut autoderefs = 0;
loop { loop {
let sty = structure_of(fcx, sp, t1); 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, _) => { ty::ty_enum(did, _) => {
// Watch out for a type like `enum t = @t`. Such a // Watch out for a type like `enum t = @t`. Such a
// type would otherwise infinitely auto-deref. This // type would otherwise infinitely auto-deref. Only
// is the only autoderef loop that needs to be // 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 // concerned with this, as an error will be reported
// on the enum definition as well because the enum is // on the enum definition as well because the enum is
// not instantiable. // not instantiable.
if vec::contains(enum_dids, did) { if vec::contains(enum_dids, did) {
return t1; return (t1, autoderefs);
} }
vec::push(enum_dids, did); 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: // Otherwise, deref if type is derefable:
match ty::deref_sty(fcx.ccx.tcx, &sty, false) { match ty::deref_sty(fcx.ccx.tcx, &sty, false) {
None => return t1, None => {
Some(mt) => t1 = mt.ty 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>, expected: Option<ty::t>,
unifier: fn()) -> bool { unifier: fn()) -> bool {
debug!( debug!(">> typechecking %s", fcx.expr_to_str(expr));
">> typechecking expr %d (%s)",
expr.id, syntax::print::pprust::expr_to_str(expr,
fcx.ccx.tcx.sess.intr()));
// A generic function to factor out common logic from call and // A generic function to factor out common logic from call and
// overloaded operations // overloaded operations
fn check_call_inner( 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, callee_expr: @ast::expr,
check_args: bool,
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} { args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
let mut bot = false; let mut bot = false;
@ -956,7 +981,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Grab the argument types, supplying fresh type variables // Grab the argument types, supplying fresh type variables
// if the wrong number of arguments were supplied // if the wrong number of arguments were supplied
let expected_arg_count = vec::len(fn_ty.sig.inputs); 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) fn_ty.sig.inputs.map(|a| a.ty)
} else { } else {
fcx.ccx.tcx.sess.span_err( 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 // of arguments when we typecheck the functions. This isn't really the
// right way to do this. // right way to do this.
for [false, true]/_.each |check_blocks| { for [false, true]/_.each |check_blocks| {
debug!("check_blocks=%b", check_blocks);
// More awful hacks: before we check the blocks, try to do // More awful hacks: before we check the blocks, try to do
// an "opportunistic" vtable resolution of any trait // an "opportunistic" vtable resolution of any trait
// bounds on the call. // bounds on the call.
@ -990,19 +1017,26 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
vtable::early_resolve_expr(callee_expr, fcx, true); vtable::early_resolve_expr(callee_expr, fcx, true);
} }
for args.eachi |i, a| { for args.eachi |i, arg| {
let is_block = match a.node { let is_block = match arg.node {
ast::expr_fn_block(*) | ast::expr_loop_body(*) | ast::expr_fn_block(*) | ast::expr_loop_body(*) |
ast::expr_do_body(*) => true, ast::expr_do_body(*) => true,
_ => false _ => false
}; };
if is_block == check_blocks { if is_block == check_blocks {
let arg_ty = arg_tys[i]; debug!("checking the argument");
bot |= check_expr_with_unifier( let formal_ty = formal_tys[i];
fcx, a, Some(arg_ty),
|| demand::assign(fcx, a.span, call_expr_id, if check_args {
arg_ty, a) 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. // Call the generic checker.
let fty = { let fty = {
let r = check_call_inner(fcx, sp, call_expr_id, let r = check_call_inner(fcx, sp, call_expr_id,
fn_ty, f, args); fn_ty, f, true, args);
bot |= r.bot; bot |= r.bot;
r.fty 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, fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
self_ex: @ast::expr, self_t: ty::t, self_ex: @ast::expr, self_t: ty::t,
opname: ast::ident, args: ~[@ast::expr]) opname: ast::ident, check_args: bool,
-> Option<(ty::t, bool)> { args: ~[@ast::expr])
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id, -> Option<(ty::t, bool)>
op_ex.callee_id, opname, self_t, ~[], false); {
match lkup.method() { match method::lookup(fcx, op_ex, self_ex,
op_ex.callee_id, opname, self_t, ~[]) {
Some(origin) => { Some(origin) => {
let {fty: method_ty, bot: bot} = { let {fty: method_ty, bot: bot} = {
let method_ty = fcx.node_ty(op_ex.callee_id); let method_ty = fcx.node_ty(op_ex.callee_id);
check_call_inner(fcx, op_ex.span, op_ex.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); fcx.ccx.method_map.insert(op_ex.id, origin);
Some((ty::ty_fn_ret(method_ty), bot)) Some((ty::ty_fn_ret(method_ty), bot))
@ -1109,71 +1144,95 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
_ => None _ => 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 // could be either a expr_binop or an expr_assign_binop
fn check_binop(fcx: @fn_ctxt, expr: @ast::expr, fn check_binop(fcx: @fn_ctxt, expr: @ast::expr,
op: ast::binop, op: ast::binop,
lhs: @ast::expr, lhs: @ast::expr,
rhs: @ast::expr) -> bool { rhs: @ast::expr) -> bool {
let tcx = fcx.ccx.tcx; 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 { match op {
ast::eq | ast::ne | ast::lt | ast::le | ast::ge | ast::gt => { 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); 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) && if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
ast_util::is_shift_binop(op) => {
// Shift is a special case: rhs can be any integral type // Shift is a special case: rhs can be any integral type
let rhs_bot = check_expr(fcx, rhs, None); let rhs_bot = check_expr(fcx, rhs, None);
let rhs_t = fcx.expr_ty(rhs); let rhs_t = fcx.expr_ty(rhs);
require_integral(fcx, rhs.span, rhs_t); require_integral(fcx, rhs.span, rhs_t);
fcx.write_ty(expr.id, lhs_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(); let tvar = fcx.infcx().next_ty_var();
demand::suptype(fcx, expr.span, tvar, lhs_t); demand::suptype(fcx, expr.span, tvar, lhs_t);
let rhs_bot = check_expr_with(fcx, rhs, tvar); let rhs_bot = check_expr_with(fcx, rhs, tvar);
let result_t = match op { let result_t = lhs_t;
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
};
fcx.write_ty(expr.id, result_t); fcx.write_ty(expr.id, result_t);
if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot } return {
else { lhs_bot } if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
} else { lhs_bot }
};
}
(_, _) => { let (result, rhs_bot) =
let (result, rhs_bot) = check_user_binop(fcx, expr, lhs, lhs_t, op, true, rhs);
check_user_binop(fcx, expr, lhs, lhs_t, op, rhs); fcx.write_ty(expr.id, result);
fcx.write_ty(expr.id, result); return lhs_bot | rhs_bot;
lhs_bot | rhs_bot
}
};
} }
fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr, fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr,
lhs_expr: @ast::expr, lhs_resolved_t: ty::t, 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; let tcx = fcx.ccx.tcx;
match ast_util::binop_to_method_name(op) { match ast_util::binop_to_method_name(op) {
Some(name) => { Some(name) => {
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t, 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, Some(pair) => return pair,
_ => () _ => ()
} }
@ -1202,11 +1261,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
(lhs_resolved_t, false) (lhs_resolved_t, false)
} }
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str, fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
ex: @ast::expr, ex: @ast::expr,
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t { rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
match lookup_op_method(fcx, ex, rhs_expr, rhs_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, Some((ret_ty, _)) => ret_ty,
_ => { _ => {
fcx.ccx.tcx.sess.span_err( fcx.ccx.tcx.sess.span_err(
@ -1308,87 +1368,84 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Check field access expressions // Check field access expressions
fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool, fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool,
base: @ast::expr, field: ast::ident, tys: ~[@ast::ty]) base: @ast::expr, field: ast::ident, tys: ~[@ast::ty])
-> bool { -> bool
{
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let bot = check_expr(fcx, base, None); let bot = check_expr(fcx, base, None);
let expr_t = structurally_resolved_type(fcx, expr.span, let expr_t = structurally_resolved_type(fcx, expr.span,
fcx.expr_ty(base)); fcx.expr_ty(base));
let base_t = do_autoderef(fcx, expr.span, expr_t); let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
let mut handled = false;
let n_tys = vec::len(tys); let n_tys = vec::len(tys);
match structure_of(fcx, expr.span, base_t) { match structure_of(fcx, expr.span, base_t) {
ty::ty_rec(fields) => { ty::ty_rec(fields) => {
match ty::field_idx(field, fields) { match ty::field_idx(field, fields) {
Some(ix) => { Some(ix) => {
if n_tys > 0u { if n_tys > 0u {
tcx.sess.span_err(expr.span, tcx.sess.span_err(
~"can't provide type parameters \ expr.span,
to a field access"); ~"can't provide type parameters \
} to a field access");
fcx.write_ty(expr.id, fields[ix].mt.ty); }
handled = true; fcx.write_ty(expr.id, fields[ix].mt.ty);
} fcx.write_autoderef_adjustment(base.id, derefs);
_ => () return bot;
}
}
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)");
} }
} _ => ()
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; return bot;
} }
@ -2000,32 +2057,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
bot = check_field(fcx, expr, false, base, field, tys); bot = check_field(fcx, expr, false, base, field, tys);
} }
ast::expr_index(base, idx) => { ast::expr_index(base, idx) => {
bot |= check_expr(fcx, base, None); bot |= check_expr(fcx, base, None);
let raw_base_t = fcx.expr_ty(base); let raw_base_t = fcx.expr_ty(base);
let base_t = do_autoderef(fcx, expr.span, raw_base_t); let (base_t, derefs) = do_autoderef(fcx, expr.span, raw_base_t);
bot |= check_expr(fcx, idx, None); bot |= check_expr(fcx, idx, None);
let idx_t = fcx.expr_ty(idx); let idx_t = fcx.expr_ty(idx);
let base_sty = structure_of(fcx, expr.span, base_t); let base_sty = structure_of(fcx, expr.span, base_t);
match ty::index_sty(tcx, &base_sty) { match ty::index_sty(tcx, &base_sty) {
Some(mt) => { Some(mt) => {
require_integral(fcx, idx.span, idx_t); require_integral(fcx, idx.span, idx_t);
fcx.write_ty(id, mt.ty); fcx.write_ty(id, mt.ty);
} fcx.write_autoderef_adjustment(base.id, derefs);
None => { }
let resolved = structurally_resolved_type(fcx, expr.span, None => {
raw_base_t); let resolved = structurally_resolved_type(fcx, expr.span,
match lookup_op_method(fcx, expr, base, resolved, raw_base_t);
tcx.sess.ident_of(~"index"), match lookup_op_method(fcx, expr, base, resolved,
~[idx]) { tcx.sess.ident_of(~"index"), true,
Some((ret_ty, _)) => fcx.write_ty(id, ret_ty), ~[idx]) {
_ => { Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
tcx.sess.span_fatal( _ => {
expr.span, ~"cannot index a value of type `" + tcx.sess.span_fatal(
fcx.infcx().ty_to_str(base_t) + ~"`"); expr.span, ~"cannot index a value of type `" +
fcx.infcx().ty_to_str(base_t) + ~"`");
}
}
} }
}
} }
}
} }
} }
if bot { fcx.write_bot(expr.id); } if bot { fcx.write_bot(expr.id); }
@ -2304,16 +2362,6 @@ fn check_enum_variants(ccx: @crate_ctxt,
check_instantiable(ccx.tcx, sp, id); 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 { fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid {
match fcx.inh.locals.find(id) { match fcx.inh.locals.find(id) {
Some(x) => x, 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, 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 { match v {
ast::vstore_fixed(None) => ty::vstore_fixed(n), ast::expr_vstore_fixed(None) => ty::vstore_fixed(n),
ast::vstore_fixed(Some(u)) => { ast::expr_vstore_fixed(Some(u)) => {
if n != u { if n != u {
let s = fmt!("fixed-size sequence mismatch: %u vs. %u",u, n); let s = fmt!("fixed-size sequence mismatch: %u vs. %u",u, n);
fcx.ccx.tcx.sess.span_err(e.span,s); fcx.ccx.tcx.sess.span_err(e.span,s);
} }
ty::vstore_fixed(u) ty::vstore_fixed(u)
} }
ast::vstore_uniq => ty::vstore_uniq, ast::expr_vstore_uniq => ty::vstore_uniq,
ast::vstore_box => ty::vstore_box, ast::expr_vstore_box => ty::vstore_box,
ast::vstore_slice(_) => { ast::expr_vstore_slice => {
let r = fcx.infcx().next_region_var(e.span, e.id); let r = fcx.infcx().next_region_var(e.span, e.id);
ty::vstore_slice(r) ty::vstore_slice(r)
} }

View file

@ -27,10 +27,9 @@ fn eqtype(fcx: @fn_ctxt, sp: span,
} }
// Checks that the type `actual` can be assigned to `expected`. // Checks that the type `actual` can be assigned to `expected`.
fn assign(fcx: @fn_ctxt, sp: span, borrow_lb: ast::node_id, fn assign(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) {
expected: ty::t, expr: @ast::expr) {
let expr_ty = fcx.expr_ty(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::Ok(()) => { /* ok */ }
result::Err(ref err) => { result::Err(ref err) => {
fcx.report_mismatched_types(sp, expected, expr_ty, err); fcx.report_mismatched_types(sp, expected, expr_ty, err);

File diff suppressed because it is too large Load diff

View file

@ -155,116 +155,157 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
visit::visit_block(b, rcx, v); 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)", 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 { // constrain_auto_ref(rcx, expr);
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,
_ => ()
}
}
ast::expr_cast(source, _) => { match expr.node {
// Determine if we are casting `source` to an trait instance. ast::expr_path(*) => {
// If so, we have to be sure that the type of the source obeys // Avoid checking the use of local variables, as we
// the trait's region bound. // already check their definitions. The def'n always
// // encloses the use. So if the def'n is enclosed by the
// Note: there is a subtle point here concerning type // region, then the uses will also be enclosed (and
// parameters. It is possible that the type of `source` // otherwise, an error will have been reported at the
// contains type parameters, which in turn may contain regions // def'n site).
// that are not visible to us (only the caller knows about match lookup_def(rcx.fcx, expr.span, expr.id) {
// them). The kind checker is ultimately responsible for ast::def_local(*) | ast::def_arg(*) |
// guaranteeing region safety in that particular case. There ast::def_upvar(*) => return,
// 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);
}
_ => ()
} }
}
};
}
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; } if !visit_node(expr.id, expr.span, rcx) { return; }
visit::visit_expr(e, rcx, v); visit::visit_expr(expr, rcx, v);
} }
fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) { fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
visit::visit_stmt(s, rcx, v); 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 { 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 let fcx = rcx.fcx;
// 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
};
// find the region where this expr evaluation is taking place // find the region where this expr evaluation is taking place
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let encl_region = ty::encl_region(tcx, id); 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. // 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( fn constrain_free_variables(
@ -272,9 +313,11 @@ fn constrain_free_variables(
region: ty::region, region: ty::region,
expr: @ast::expr) 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; let tcx = rcx.fcx.ccx.tcx;
for get_freevars(tcx, expr.id).each |freevar| { for get_freevars(tcx, expr.id).each |freevar| {
debug!("freevar def is %?", freevar.def); 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( fn constrain_regions_in_type(
rcx: @rcx, rcx: @rcx,
encl_region: ty::region, encl_region: ty::region,
span: span, 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; let e = rcx.errors_reported;
ty::walk_regions_and_ty( ty::walk_regions_and_ty(

View file

@ -4,6 +4,7 @@ use infer::{resolve_type, resolve_and_force_all_but_regions,
use ast_util::new_def_hash; use ast_util::new_def_hash;
use syntax::print::pprust; use syntax::print::pprust;
use result::{Result, Ok, Err}; use result::{Result, Ok, Err};
use util::common::indenter;
// vtable resolution looks for places where trait bounds are // vtable resolution looks for places where trait bounds are
// subsituted in and figures out which vtable is used. There is some // 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) { fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
debug!("vtable: early_resolve_expr() ex with id %? (early: %b): %s", debug!("vtable: early_resolve_expr() ex with id %? (early: %b): %s",
ex.id, is_early, expr_to_str(ex, fcx.tcx().sess.intr())); ex.id, is_early, expr_to_str(ex, fcx.tcx().sess.intr()));
let _indent = indenter();
let cx = fcx.ccx; let cx = fcx.ccx;
match ex.node { match ex.node {
ast::expr_path(*) => { ast::expr_path(*) => {

View file

@ -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) fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
-> Option<ty::t> -> Option<ty::t>
{ {
let fcx = wbcx.fcx, tcx = fcx.ccx.tcx; let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
// Resolve any borrowings for the node with id `id` // Resolve any borrowings for the node with id `id`
match fcx.inh.borrowings.find(id) { match fcx.inh.adjustments.find(id) {
None => (), None => (),
Some(borrow) => { Some(adj) => {
match resolve_region(fcx.infcx(), borrow.region, let resolved_autoref = match adj.autoref {
resolve_all | force_all) { Some(ref autoref) => {
Err(e) => { match resolve_region(fcx.infcx(), autoref.region,
// This should not, I think, happen. resolve_all | force_all) {
fcx.ccx.tcx.sess.span_err( Err(e) => {
sp, fmt!("cannot resolve scope of borrow: %s", // This should not, I think, happen.
infer::fixup_err_to_str(e))); 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) => { None => None
debug!("Borrowing node %d -> region %?, mutbl %?", };
id, r, borrow.mutbl);
fcx.tcx().borrowings.insert(id, {region: r, let resolved_adj = @{autoref: resolved_autoref, ..*adj};
mutbl: borrow.mutbl}); 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) { fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { return; } if !wbcx.success { return; }
resolve_type_vars_for_node(wbcx, e.span, e.id); 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 { match e.node {
ast::expr_fn(_, decl, _, _) | ast::expr_fn(_, decl, _, _) |
ast::expr_fn_block(decl, _, _) => { ast::expr_fn_block(decl, _, _) => {

View file

@ -275,6 +275,7 @@ use unify::{vals_and_bindings, root};
use integral::{int_ty_set, int_ty_set_all}; use integral::{int_ty_set, int_ty_set_all};
use combine::{combine_fields, eq_tys}; use combine::{combine_fields, eq_tys};
use assignment::Assign; use assignment::Assign;
use to_str::to_str;
use sub::Sub; use sub::Sub;
use lub::Lub; 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 cres<T> = Result<T,ty::type_err>; // "combine result"
type ures = cres<()>; // "unify result" type ures = cres<()>; // "unify result"
type fres<T> = Result<T, fixup_err>; // "fixup 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 = @{ enum infer_ctxt = @{
tcx: ty::ctxt, tcx: ty::ctxt,
@ -469,19 +470,24 @@ impl ures: then {
} }
} }
trait cres_helpers<T> { trait ToUres {
fn to_ures() -> ures; 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 { fn to_ures() -> ures {
match self { match self {
Ok(_v) => Ok(()), Ok(_v) => Ok(()),
Err(e) => Err(e) 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> { fn compare(t: T, f: fn() -> ty::type_err) -> cres<T> {
do self.chain |s| { do self.chain |s| {
if s == t { if s == t {

View file

@ -134,18 +134,24 @@ priv impl Assign {
(ty::ty_box(_), ty::ty_rptr(r_b, mt_b)) => { (ty::ty_box(_), ty::ty_rptr(r_b, mt_b)) => {
let nr_b = ty::mk_box(self.infcx.tcx, let nr_b = ty::mk_box(self.infcx.tcx,
{ty: mt_b.ty, mutbl: m_const}); {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)) => { (ty::ty_uniq(_), ty::ty_rptr(r_b, mt_b)) => {
let nr_b = ty::mk_uniq(self.infcx.tcx, let nr_b = ty::mk_uniq(self.infcx.tcx,
{ty: mt_b.ty, mutbl: m_const}); {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(vs_a),
ty::ty_estr(ty::vstore_slice(r_b))) ty::ty_estr(ty::vstore_slice(r_b)))
if is_borrowable(vs_a) => { if is_borrowable(vs_a) => {
let nr_b = ty::mk_estr(self.infcx.tcx, 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), (ty::ty_evec(_, vs_a),
@ -154,7 +160,9 @@ priv impl Assign {
let nr_b = ty::mk_evec(self.infcx.tcx, let nr_b = ty::mk_evec(self.infcx.tcx,
{ty: mt_b.ty, mutbl: m_const}, {ty: mt_b.ty, mutbl: m_const},
vs_a); 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 /// variable `r_a >= r_b` and returns a corresponding assignment
/// record. See the discussion at the top of this file for more /// record. See the discussion at the top of this file for more
/// details. /// details.
fn try_assign(a: ty::t, fn try_assign(autoderefs: uint,
kind: ty::AutoRefKind,
a: ty::t,
nr_b: ty::t, nr_b: ty::t,
m: ast::mutability, m: ast::mutability,
r_b: ty::region) -> ares { r_b: ty::region) -> ares {
@ -193,7 +203,14 @@ priv impl Assign {
do sub.tys(a, nr_b).chain |_t| { do sub.tys(a, nr_b).chain |_t| {
let r_a = self.infcx.next_region_var_nb(self.span); let r_a = self.infcx.next_region_var_nb(self.span);
do sub.contraregions(r_a, r_b).chain |_r| { 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
})
}))
} }
} }
} }

View file

@ -18,7 +18,7 @@ use syntax::codemap;
use syntax::codemap::span; use syntax::codemap::span;
use syntax::print::pprust; use syntax::print::pprust;
use syntax::print::pprust::{path_to_str, proto_to_str, 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, ast_util};
use syntax::ast_map; use syntax::ast_map;
use driver::session::session; 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 { fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str {
let mut rs = ~""; let tstrs = ts.map(|t| ty_to_str(cx, t));
for ts.each |t| { rs += ty_to_str(cx, t); } fmt!("[%s]", str::connect(tstrs, ", "))
rs
} }
fn bound_to_str(cx: ctxt, b: param_bound) -> ~str { fn bound_to_str(cx: ctxt, b: param_bound) -> ~str {

View file

@ -19,4 +19,5 @@ fn cat(in_x : uint, in_y : int) -> cat {
fn main() { fn main() {
let nyan : cat = cat(52u, 99); let nyan : cat = cat(52u, 99);
nyan.speak = fn@() { debug!("meow"); }; //~ ERROR attempted to take value of method nyan.speak = fn@() { debug!("meow"); }; //~ ERROR attempted to take value of method
//~^ ERROR mismatched types
} }

View 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
}
}
}

View file

@ -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() {
}

View file

@ -5,7 +5,7 @@ fn borrow_from_arg_imm_ref(&&v: ~int) {
} }
fn borrow_from_arg_mut_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 //~^ NOTE impure due to access to impure function
} }

View file

@ -47,7 +47,7 @@ fn c() {
// ...but not impure fns // ...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 //~^ NOTE impure due to access to impure function
} }

View file

@ -53,7 +53,7 @@ fn c() {
(*q).purem(); (*q).purem();
// ...but not impure fns // ...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 //~^ NOTE impure due to access to impure function
} }

View file

@ -1,6 +1,6 @@
fn main() { fn main() {
let x: int = 3; 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; *y = 5;
log (debug, *y); log (debug, *y);
} }

View file

@ -1,7 +1,7 @@
enum foo = ~int; enum foo = ~int;
fn borrow(x: @mut foo) { 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 *x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
} }

View 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
}

View file

@ -5,7 +5,7 @@ fn want_slice(v: &[int]) -> int {
} }
fn has_mut_vec(+v: @~[mut 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 //~^ NOTE impure due to access to impure function
} }

View file

@ -4,7 +4,7 @@ fn test1(x: @mut ~int) {
// Here, evaluating the second argument actually invalidates the // Here, evaluating the second argument actually invalidates the
// first borrow, even though it occurs outside of the scope of the // first borrow, even though it occurs outside of the scope of the
// borrow! // 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 //~^ NOTE impure due to assigning to dereference of mutable @ pointer
} }

View file

@ -1,23 +1,19 @@
fn borrow(_v: &int) {} fn borrow(_v: &int) {}
fn box_mut(v: @mut ~int) { fn box_mut(v: @mut ~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
} }
fn box_rec_mut(v: @{mut f: ~int}) { fn box_rec_mut(v: @{mut f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_mut_rec(v: @mut {f: ~int}) { fn box_mut_rec(v: @mut {f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_imm(v: @~int) { fn box_imm(v: @~int) {
@ -33,28 +29,23 @@ fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
} }
fn box_const(v: @const ~int) { fn box_const(v: @const ~int) {
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(*v); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_rec_const(v: @{const f: ~int}) { fn box_rec_const(v: @{const f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_recs_const(v: @{f: {g: {const h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_const_rec(v: @const {f: ~int}) { fn box_const_rec(v: @const {f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_const_recs(v: @const {f: {g: {h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn main() { fn main() {

View file

@ -1,23 +1,19 @@
fn borrow(_v: &int) {} fn borrow(_v: &int) {}
fn box_mut(v: &mut ~int) { fn box_mut(v: &mut ~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
} }
fn box_rec_mut(v: &{mut f: ~int}) { fn box_rec_mut(v: &{mut f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_mut_rec(v: &mut {f: ~int}) { fn box_mut_rec(v: &mut {f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_imm(v: &~int) { fn box_imm(v: &~int) {
@ -33,28 +29,23 @@ fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
} }
fn box_const(v: &const ~int) { fn box_const(v: &const ~int) {
borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(*v); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_rec_const(v: &{const f: ~int}) { fn box_rec_const(v: &{const f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_recs_const(v: &{f: {g: {const h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_const_rec(v: &const {f: ~int}) { fn box_const_rec(v: &const {f: ~int}) {
borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory borrow(v.f); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn box_const_recs(v: &const {f: {g: {h: ~int}}}) { 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 borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
} }
fn main() { fn main() {

View file

@ -7,6 +7,5 @@ fn f<T:Eq>(&o: Option<T>) {
fn main() { fn main() {
f::<int>(option::None); f::<int>(option::None);
//~^ ERROR taking mut reference to static item //~^ ERROR illegal borrow: creating mutable alias to static item
//~^^ ERROR illegal borrow: creating mutable alias to aliasable, immutable memory
} }

View 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;
}

View file

@ -3,7 +3,7 @@ struct Foo {
} }
impl Foo { impl Foo {
fn f(&self) {} fn f(&const self) {}
} }
fn g(x: &mut Foo) { fn g(x: &mut Foo) {