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

View File

@ -354,6 +354,13 @@ impl<T: Send> Exclusive<T> {
move result
}
}
#[inline(always)]
unsafe fn with_imm<U>(f: fn(x: &T) -> U) -> U {
do self.with |x| {
f(unsafe::transmute_immut(x))
}
}
}
// FIXME(#2585) make this a by-move method on the exclusive

View File

@ -314,6 +314,8 @@ enum EbmlSerializerTag {
EsEnum, EsEnumVid, EsEnumBody,
EsVec, EsVecLen, EsVecElt,
EsOpaque,
EsLabel // Used only when debugging
}
@ -340,6 +342,14 @@ impl ebml::Writer: SerializerPriv {
}
}
impl ebml::Writer {
fn emit_opaque(f: fn()) {
do self.wr_tag(EsOpaque as uint) {
f()
}
}
}
impl ebml::Writer: serialization::Serializer {
fn emit_nil() {}
@ -397,7 +407,7 @@ impl ebml::Writer: serialization::Serializer {
}
type EbmlDeserializer_ = {mut parent: ebml::Doc,
mut pos: uint};
mut pos: uint};
enum EbmlDeserializer {
EbmlDeserializer_(EbmlDeserializer_)
@ -462,6 +472,14 @@ priv impl EbmlDeserializer {
}
}
impl EbmlDeserializer {
fn read_opaque<R>(op: fn(ebml::Doc) -> R) -> R {
do self.push_doc(self.next_doc(EsOpaque)) {
op(copy self.parent)
}
}
}
impl EbmlDeserializer: serialization::Deserializer {
fn read_nil() -> () { () }

View File

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

View File

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

View File

@ -421,6 +421,15 @@ enum vstore {
vstore_slice(@region) // &[1,2,3,4](foo)?
}
#[auto_serialize]
enum expr_vstore {
// FIXME (#2112): Change uint to @expr (actually only constant exprs)
expr_vstore_fixed(Option<uint>), // [1,2,3,4]/_ or 4
expr_vstore_uniq, // ~[1,2,3,4]
expr_vstore_box, // @[1,2,3,4]
expr_vstore_slice // &[1,2,3,4]
}
pure fn is_blockish(p: ast::proto) -> bool {
match p {
proto_block => true,
@ -662,7 +671,7 @@ enum alt_mode { alt_check, alt_exhaustive, }
#[auto_serialize]
enum expr_ {
expr_vstore(@expr, vstore),
expr_vstore(@expr, expr_vstore),
expr_vec(~[@expr], mutability),
expr_rec(~[field], Option<@expr>),
expr_call(@expr, ~[@expr], bool), // True iff last argument is a block

View File

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

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

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

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

View File

@ -294,7 +294,7 @@ fn mk_test_desc_vec(cx: test_ctxt) -> @ast::expr {
span: dummy_sp()};
return @{id: cx.sess.next_node_id(),
callee_id: cx.sess.next_node_id(),
node: ast::expr_vstore(inner_expr, ast::vstore_uniq),
node: ast::expr_vstore(inner_expr, ast::expr_vstore_uniq),
span: dummy_sp()};
}
@ -316,7 +316,7 @@ fn mk_test_desc_rec(cx: test_ctxt, test: test) -> @ast::expr {
let name_expr = {id: cx.sess.next_node_id(),
callee_id: cx.sess.next_node_id(),
node: ast::expr_vstore(name_expr_inner,
ast::vstore_uniq),
ast::expr_vstore_uniq),
span: dummy_sp()};

View File

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

View File

@ -11,8 +11,10 @@ use middle::ty;
use std::map::HashMap;
use ty::{FnTyBase, FnMeta, FnSig};
export parse_ty_data, parse_def_id, parse_ident;
export parse_state_from_data;
export parse_arg_data, parse_ty_data, parse_def_id, parse_ident;
export parse_bounds_data;
export pstate;
// Compact string representation for ty::t values. API ty_str &
// parse_from_str. Extra parameters are for converting to/from def_ids in the
@ -53,13 +55,25 @@ fn parse_ident_(st: @pstate, is_last: fn@(char) -> bool) ->
return st.tcx.sess.ident_of(rslt);
}
fn parse_state_from_data(data: @~[u8], crate_num: int,
pos: uint, tcx: ty::ctxt)
-> @pstate
{
@{data: data, crate: crate_num, mut pos: pos, tcx: tcx}
}
fn parse_ty_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
conv: conv_did) -> ty::t {
let st = @{data: data, crate: crate_num, mut pos: pos, tcx: tcx};
let st = parse_state_from_data(data, crate_num, pos, tcx);
parse_ty(st, conv)
}
fn parse_arg_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
conv: conv_did) -> ty::arg {
let st = parse_state_from_data(data, crate_num, pos, tcx);
parse_arg(st, conv)
}
fn parse_ret_ty(st: @pstate, conv: conv_did) -> (ast::ret_style, ty::t) {
match peek(st) {
'!' => { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) }
@ -373,6 +387,23 @@ fn parse_purity(c: char) -> purity {
}
}
fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
{mode: parse_mode(st),
ty: parse_ty(st, conv)}
}
fn parse_mode(st: @pstate) -> ast::mode {
let m = ast::expl(match next(st) {
'&' => ast::by_mutbl_ref,
'-' => ast::by_move,
'+' => ast::by_copy,
'=' => ast::by_ref,
'#' => ast::by_val,
_ => fail ~"bad mode"
});
return m;
}
fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
let proto = parse_proto(st);
let purity = parse_purity(next(st));
@ -380,16 +411,8 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
assert (next(st) == '[');
let mut inputs: ~[ty::arg] = ~[];
while peek(st) != ']' {
let mode = match peek(st) {
'&' => ast::by_mutbl_ref,
'-' => ast::by_move,
'+' => ast::by_copy,
'=' => ast::by_ref,
'#' => ast::by_val,
_ => fail ~"bad mode"
};
st.pos += 1u;
vec::push(inputs, {mode: ast::expl(mode), ty: parse_ty(st, conv)});
let mode = parse_mode(st);
vec::push(inputs, {mode: mode, ty: parse_ty(st, conv)});
}
st.pos += 1u; // eat the ']'
let (ret_style, ret_ty) = parse_ret_ty(st, conv);
@ -432,8 +455,9 @@ fn parse_def_id(buf: &[u8]) -> ast::def_id {
fn parse_bounds_data(data: @~[u8], start: uint,
crate_num: int, tcx: ty::ctxt, conv: conv_did)
-> @~[ty::param_bound] {
let st = @{data: data, crate: crate_num, mut pos: start, tcx: tcx};
-> @~[ty::param_bound]
{
let st = parse_state_from_data(data, crate_num, start, tcx);
parse_bounds(st, conv)
}

View File

@ -15,6 +15,7 @@ export ac_use_abbrevs;
export enc_ty;
export enc_bounds;
export enc_mode;
export enc_arg;
type ctxt = {
diag: span_handler,
@ -323,6 +324,11 @@ fn enc_proto(w: io::Writer, cx: @ctxt, proto: ty::fn_proto) {
}
}
fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
enc_mode(w, cx, arg.mode);
enc_ty(w, cx, arg.ty);
}
fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
match ty::resolved_mode(cx.tcx, m) {
by_mutbl_ref => w.write_char('&'),
@ -348,8 +354,7 @@ fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) {
enc_bounds(w, cx, ft.meta.bounds);
w.write_char('[');
for ft.sig.inputs.each |arg| {
enc_mode(w, cx, arg.mode);
enc_ty(w, cx, arg.ty);
enc_arg(w, cx, arg);
}
w.write_char(']');
match ft.meta.ret_style {

View File

@ -18,10 +18,8 @@ use std::serialization::DeserializerHelpers;
use std::prettyprint::Serializer;
use middle::{ty, typeck};
use middle::typeck::{method_origin, method_map_entry,
serialize_method_map_entry,
deserialize_method_map_entry,
vtable_res,
vtable_origin};
vtable_res,
vtable_origin};
use driver::session::session;
use middle::freevars::{freevar_entry,
serialize_freevar_entry,
@ -385,6 +383,45 @@ impl ast::def: tr {
}
}
// ______________________________________________________________________
// Encoding and decoding of adjustment information
impl ty::AutoAdjustment: tr {
fn tr(xcx: extended_decode_ctxt) -> ty::AutoAdjustment {
{autoderefs: self.autoderefs,
autoref: self.autoref.map(|ar| ar.tr(xcx))}
}
}
impl ty::AutoRef: tr {
fn tr(xcx: extended_decode_ctxt) -> ty::AutoRef {
{kind: self.kind,
region: self.region.tr(xcx),
mutbl: self.mutbl}
}
}
impl ty::region: tr {
fn tr(xcx: extended_decode_ctxt) -> ty::region {
match self {
ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
ty::re_static | ty::re_var(*) => self,
}
}
}
impl ty::bound_region: tr {
fn tr(xcx: extended_decode_ctxt) -> ty::bound_region {
match self {
ty::br_anon(_) | ty::br_named(_) | ty::br_self => self,
ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id),
@br.tr(xcx))
}
}
}
// ______________________________________________________________________
// Encoding and decoding of freevar information
@ -416,12 +453,31 @@ trait read_method_map_entry_helper {
fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry;
}
fn serialize_method_map_entry(ecx: @e::encode_ctxt,
ebml_w: ebml::Writer,
mme: method_map_entry) {
do ebml_w.emit_rec {
do ebml_w.emit_rec_field(~"self_arg", 0u) {
ebml_w.emit_arg(ecx, mme.self_arg);
}
do ebml_w.emit_rec_field(~"origin", 1u) {
typeck::serialize_method_origin(ebml_w, mme.origin);
}
}
}
impl ebml::EbmlDeserializer: read_method_map_entry_helper {
fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry {
let mme = deserialize_method_map_entry(self);
{derefs: mme.derefs,
self_mode: mme.self_mode,
origin: mme.origin.tr(xcx)}
do self.read_rec {
{self_arg:
self.read_rec_field(~"self_arg", 0u, || {
self.read_arg(xcx)
}),
origin:
self.read_rec_field(~"origin", 1u, || {
typeck::deserialize_method_origin(self).tr(xcx)
})}
}
}
}
@ -445,8 +501,8 @@ impl method_origin: tr {
// Encoding and decoding vtable_res
fn encode_vtable_res(ecx: @e::encode_ctxt,
ebml_w: ebml::Writer,
dr: typeck::vtable_res) {
ebml_w: ebml::Writer,
dr: typeck::vtable_res) {
// can't autogenerate this code because automatic serialization of
// ty::t doesn't work, and there is no way (atm) to have
// hand-written serialization routines combine with auto-generated
@ -573,6 +629,7 @@ impl @e::encode_ctxt: get_ty_str_ctxt {
}
trait ebml_writer_helpers {
fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg);
fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t);
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]);
fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds);
@ -581,17 +638,27 @@ trait ebml_writer_helpers {
impl ebml::Writer: ebml_writer_helpers {
fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) {
e::write_type(ecx, self, ty)
}
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
do self.emit_from_vec(tys) |ty| {
do self.emit_opaque {
e::write_type(ecx, self, ty)
}
}
fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg) {
do self.emit_opaque {
tyencode::enc_arg(self.writer, ecx.ty_str_ctxt(), arg);
}
}
fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
do self.emit_from_vec(tys) |ty| {
self.emit_ty(ecx, ty)
}
}
fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds) {
tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
do self.emit_opaque {
tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
}
}
fn emit_tpbt(ecx: @e::encode_ctxt, tpbt: ty::ty_param_bounds_and_ty) {
@ -664,7 +731,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
do ebml_w.tag(c::tag_table_node_type) {
ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) {
e::write_type(ecx, ebml_w, ty)
ebml_w.emit_ty(ecx, ty);
}
}
}
@ -743,7 +810,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
do ebml_w.tag(c::tag_table_method_map) {
ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) {
serialize_method_map_entry(ebml_w, mme)
serialize_method_map_entry(ecx, ebml_w, mme)
}
}
}
@ -757,13 +824,11 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
}
}
do option::iter(tcx.borrowings.find(id)) |_borrow| {
do ebml_w.tag(c::tag_table_borrowings) {
do option::iter(tcx.adjustments.find(id)) |adj| {
do ebml_w.tag(c::tag_table_adjustments) {
ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) {
// N.B. We don't actually serialize borrows as, in
// trans, we only care whether a value is borrowed or
// not.
ty::serialize_AutoAdjustment(ebml_w, *adj)
}
}
}
@ -782,6 +847,7 @@ impl ebml::Doc: doc_decoder_helpers {
}
trait ebml_deserializer_decoder_helpers {
fn read_arg(xcx: extended_decode_ctxt) -> ty::arg;
fn read_ty(xcx: extended_decode_ctxt) -> ty::t;
fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t];
fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound];
@ -791,15 +857,25 @@ trait ebml_deserializer_decoder_helpers {
impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
fn read_arg(xcx: extended_decode_ctxt) -> ty::arg {
do self.read_opaque |doc| {
tydecode::parse_arg_data(
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|a| xcx.tr_def_id(a))
}
}
fn read_ty(xcx: extended_decode_ctxt) -> ty::t {
// Note: regions types embed local node ids. In principle, we
// should translate these node ids into the new decode
// context. However, we do not bother, because region types
// are not used during trans.
tydecode::parse_ty_data(
self.parent.data, xcx.dcx.cdata.cnum, self.pos, xcx.dcx.tcx,
|a| xcx.tr_def_id(a) )
do self.read_opaque |doc| {
tydecode::parse_ty_data(
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|a| xcx.tr_def_id(a))
}
}
fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t] {
@ -807,13 +883,16 @@ impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
}
fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound] {
tydecode::parse_bounds_data(
self.parent.data, self.pos, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
|a| xcx.tr_def_id(a) )
do self.read_opaque |doc| {
tydecode::parse_bounds_data(
doc.data, doc.start, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
|a| xcx.tr_def_id(a))
}
}
fn read_ty_param_bounds_and_ty(xcx: extended_decode_ctxt)
-> ty::ty_param_bounds_and_ty {
-> ty::ty_param_bounds_and_ty
{
do self.read_rec {
{
bounds: self.read_rec_field(~"bounds", 0u, || {
@ -881,12 +960,9 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
} else if tag == (c::tag_table_vtable_map as uint) {
dcx.maps.vtable_map.insert(id,
val_dsr.read_vtable_res(xcx));
} else if tag == (c::tag_table_borrowings as uint) {
// N.B.: we don't actually *serialize* borrows because, in
// trans, the only thing we care about is whether a value was
// borrowed or not.
let borrow = {region: ty::re_static, mutbl: ast::m_imm};
dcx.tcx.borrowings.insert(id, borrow);
} else if tag == (c::tag_table_adjustments as uint) {
let adj = @ty::deserialize_AutoAdjustment(val_dsr).tr(xcx);
dcx.tcx.adjustments.insert(id, adj);
} else {
xcx.dcx.tcx.sess.bug(
fmt!("unknown tag found in side tables: %x", tag));

View File

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

View File

@ -75,8 +75,7 @@ fn check_loans(bccx: borrowck_ctxt,
enum assignment_type {
at_straight_up,
at_swap,
at_mutbl_ref,
at_swap
}
impl assignment_type : cmp::Eq {
@ -92,15 +91,13 @@ impl assignment_type {
// are only assigned once; but it doesn't consider &mut
match self {
at_straight_up => true,
at_swap => true,
at_mutbl_ref => false
at_swap => true
}
}
fn ing_form(desc: ~str) -> ~str {
match self {
at_straight_up => ~"assigning to " + desc,
at_swap => ~"swapping to and from " + desc,
at_mutbl_ref => ~"taking mut reference to " + desc
at_swap => ~"swapping to and from " + desc
}
}
}
@ -369,11 +366,9 @@ impl check_loan_ctxt {
// taking a mutable ref. that will create a loan of its own
// which will be checked for compat separately in
// check_for_conflicting_loans()
if at != at_mutbl_ref {
for cmt.lp.each |lp| {
self.check_for_loan_conflicting_with_assignment(
at, ex, cmt, lp);
}
for cmt.lp.each |lp| {
self.check_for_loan_conflicting_with_assignment(
at, ex, cmt, lp);
}
self.bccx.add_to_mutbl_map(cmt);
@ -430,8 +425,8 @@ impl check_loan_ctxt {
self.tcx().sess.span_err(
e.cmt.span,
fmt!("illegal borrow unless pure: %s",
self.bccx.bckerr_code_to_str(e.code)));
self.bccx.note_and_explain_bckerr(e.code);
self.bccx.bckerr_to_str(e)));
self.bccx.note_and_explain_bckerr(e);
self.tcx().sess.span_note(
sp,
fmt!("impure due to %s", msg));
@ -531,14 +526,12 @@ impl check_loan_ctxt {
ty::node_id_to_type(self.tcx(), callee_id));
do vec::iter2(args, arg_tys) |arg, arg_ty| {
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
ast::by_move => {
self.check_move_out(arg);
}
ast::by_mutbl_ref => {
self.check_assignment(at_mutbl_ref, arg);
}
ast::by_ref | ast::by_copy | ast::by_val => {
}
ast::by_move => {
self.check_move_out(arg);
}
ast::by_mutbl_ref | ast::by_ref |
ast::by_copy | ast::by_val => {
}
}
}
}
@ -645,19 +638,6 @@ fn check_loans_in_expr(expr: @ast::expr,
}
}
}
ast::expr_addr_of(mutbl, base) => {
match mutbl {
m_const => { /*all memory is const*/ }
m_mutbl => {
// If we are taking an &mut ptr, make sure the memory
// being pointed at is assignable in the first place:
self.check_assignment(at_mutbl_ref, base);
}
m_imm => {
// XXX explain why no check is req'd here
}
}
}
ast::expr_call(f, args, _) => {
self.check_call(expr, Some(f), f.id, f.span, args);
}

View File

@ -6,9 +6,9 @@
// their associated scopes. In phase two, checking loans, we will then make
// sure that all of these loans are honored.
use mem_categorization::{opt_deref_kind};
use mem_categorization::{mem_categorization_ctxt, opt_deref_kind};
use preserve::{preserve_condition, pc_ok, pc_if_pure};
use ty::ty_region;
use ty::{ty_region};
export gather_loans;
@ -94,9 +94,8 @@ fn req_loans_in_expr(ex: @ast::expr,
ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
// If this expression is borrowed, have to ensure it remains valid:
for tcx.borrowings.find(ex.id).each |borrow| {
let cmt = self.bccx.cat_borrow_of_expr(ex);
self.guarantee_valid(cmt, borrow.mutbl, borrow.region);
for tcx.adjustments.find(ex.id).each |adjustments| {
self.guarantee_adjustments(ex, adjustments);
}
// Special checks for various kinds of expressions:
@ -125,45 +124,9 @@ fn req_loans_in_expr(ex: @ast::expr,
self.guarantee_valid(arg_cmt, m_imm, scope_r);
}
ast::by_val => {
// Rust's by-val does not actually give ownership to
// the callee. This means that if a pointer type is
// passed, it is effectively a borrow, and so the
// caller must guarantee that the data remains valid.
//
// Subtle: we only guarantee that the pointer is valid
// and const. Technically, we ought to pass in the
// mutability that the caller expects (e.g., if the
// formal argument has type @mut, we should guarantee
// validity and mutability, not validity and const).
// However, the type system already guarantees that
// the caller's mutability is compatible with the
// callee, so this is not necessary. (Note that with
// actual borrows, typeck is more liberal and allows
// the pointer to be borrowed as immutable even if it
// is mutable in the caller's frame, thus effectively
// passing the buck onto us to enforce this)
//
// FIXME (#2493): this handling is not really adequate.
// For example, if there is a type like, {f: ~[int]}, we
// will ignore it, but we ought to be requiring it to be
// immutable (whereas something like {f:int} would be
// fine).
//
match opt_deref_kind(arg_ty.ty) {
Some(deref_ptr(region_ptr(_))) |
Some(deref_ptr(unsafe_ptr)) => {
/* region pointers are (by induction) guaranteed */
/* unsafe pointers are the user's problem */
}
Some(deref_comp(_)) |
None => {
/* not a pointer, no worries */
}
Some(deref_ptr(_)) => {
let arg_cmt = self.bccx.cat_borrow_of_expr(arg);
self.guarantee_valid(arg_cmt, m_const, scope_r);
}
}
// FIXME (#2493): safety checks would be required here,
// but the correct set is really hard to get right,
// and modes are going away anyhow.
}
ast::by_move | ast::by_copy => {}
}
@ -261,6 +224,42 @@ fn req_loans_in_expr(ex: @ast::expr,
impl gather_loan_ctxt {
fn tcx() -> ty::ctxt { self.bccx.tcx }
fn guarantee_adjustments(expr: @ast::expr,
adjustment: &ty::AutoAdjustment) {
debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
expr_repr(self.tcx(), expr), adjustment);
let _i = indenter();
match adjustment.autoref {
None => {
debug!("no autoref");
return;
}
Some(ref autoref) => {
let mcx = &mem_categorization_ctxt {
tcx: self.tcx(),
method_map: self.bccx.method_map};
let mut cmt = mcx.cat_expr_autoderefd(expr, adjustment);
debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
match autoref.kind {
ty::AutoPtr => {
self.guarantee_valid(cmt,
autoref.mutbl,
autoref.region)
}
ty::AutoSlice => {
let cmt_index = mcx.cat_index(expr, cmt);
self.guarantee_valid(cmt_index,
autoref.mutbl,
autoref.region)
}
}
}
}
}
// guarantees that addr_of(cmt) will be valid for the duration of
// `static_scope_r`, or reports an error. This may entail taking
// out loans, which will be added to the `req_loan_map`. This can
@ -387,25 +386,17 @@ impl gather_loan_ctxt {
// mutable memory.
fn check_mutbl(req_mutbl: ast::mutability,
cmt: cmt) -> bckres<preserve_condition> {
match (req_mutbl, cmt.mutbl) {
(m_const, _) |
(m_imm, m_imm) |
(m_mutbl, m_mutbl) => {
if req_mutbl == m_const || req_mutbl == cmt.mutbl {
Ok(pc_ok)
}
(_, m_const) |
(m_imm, m_mutbl) |
(m_mutbl, m_imm) => {
} else {
let e = {cmt: cmt,
code: err_mutbl(req_mutbl, cmt.mutbl)};
code: err_mutbl(req_mutbl)};
if req_mutbl == m_imm {
// you can treat mutable things as imm if you are pure
Ok(pc_if_pure(e))
} else {
Err(e)
}
}
}
}

View File

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

View File

@ -169,7 +169,7 @@ priv impl &preserve_ctxt {
}
Err(e) => {
debug!("must root @T, err: %s",
self.bccx.bckerr_code_to_str(e.code));
self.bccx.bckerr_to_str(e));
self.attempt_root(cmt, base, derefs)
}
}

View File

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

View File

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

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
// to check the copy
match cx.method_map.find(e.id) {
Some({self_mode: by_copy, _}) => maybe_copy(cx, lhs, None),
_ => ()
Some(ref mme) => {
match ty::arg_mode(cx.tcx, mme.self_arg) {
by_copy => maybe_copy(cx, lhs, None),
by_ref | by_val | by_mutbl_ref | by_move => ()
}
}
_ => ()
}
}
expr_repeat(element, count_expr, _) => {
@ -437,11 +442,18 @@ fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
!is_nullary_variant(cx, ex) &&
// borrowed unique value isn't really a copy
!cx.tcx.borrowings.contains_key(ex.id)
!is_autorefd(cx, ex)
{
let ty = ty::expr_ty(cx.tcx, ex);
check_copy(cx, ex.id, ty, ex.span, implicit_copy, why);
}
fn is_autorefd(cx: ctx, ex: @expr) -> bool {
match cx.tcx.adjustments.find(ex.id) {
None => false,
Some(ref adj) => adj.autoref.is_some()
}
}
}
fn check_imm_free_var(cx: ctx, def: def, sp: span) {

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(
tcx: ty::ctxt,
method_map: typeck::method_map,
@ -420,33 +409,40 @@ struct mem_categorization_ctxt {
}
impl &mem_categorization_ctxt {
fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
// Any expression can be borrowed (to account for auto-ref on method
// receivers), but @, ~, @vec, and ~vec are handled specially.
let expr_ty = ty::expr_ty(self.tcx, expr);
match ty::get(expr_ty).sty {
ty::ty_evec(*) | ty::ty_estr(*) => {
self.cat_index(expr, expr)
}
fn cat_expr(expr: @ast::expr) -> cmt {
match self.tcx.adjustments.find(expr.id) {
None => {
// No adjustments.
self.cat_expr_unadjusted(expr)
}
ty::ty_uniq(*) | ty::ty_box(*) | ty::ty_rptr(*) => {
let cmt = self.cat_expr(expr);
self.cat_deref(expr, cmt, 0u, true).get()
}
/*
ty::ty_fn({proto, _}) {
self.cat_call(expr, expr, proto)
}
*/
_ => {
self.cat_rvalue(expr, expr_ty)
}
Some(adjustment) => {
match adjustment.autoref {
Some(_) => {
// Equivalent to &*expr or something similar.
// This is an rvalue, effectively.
let expr_ty = ty::expr_ty(self.tcx, expr);
self.cat_rvalue(expr, expr_ty)
}
None => {
// Equivalent to *expr or something similar.
self.cat_expr_autoderefd(expr, adjustment)
}
}
}
}
}
fn cat_expr(expr: @ast::expr) -> cmt {
fn cat_expr_autoderefd(expr: @ast::expr,
adjustment: &ty::AutoAdjustment) -> cmt {
let mut cmt = self.cat_expr_unadjusted(expr);
for uint::range(1, adjustment.autoderefs+1) |deref| {
cmt = self.cat_deref(expr, cmt, deref);
}
return cmt;
}
fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
debug!("cat_expr: id=%d expr=%s",
expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
@ -459,15 +455,7 @@ impl &mem_categorization_ctxt {
}
let base_cmt = self.cat_expr(e_base);
match self.cat_deref(expr, base_cmt, 0u, true) {
Some(cmt) => return cmt,
None => {
tcx.sess.span_bug(
e_base.span,
fmt!("Explicit deref of non-derefable type `%s`",
ty_to_str(tcx, tcx.ty(e_base))));
}
}
self.cat_deref(expr, base_cmt, 0)
}
ast::expr_field(base, f_name, _) => {
@ -475,7 +463,7 @@ impl &mem_categorization_ctxt {
return self.cat_method_ref(expr, expr_ty);
}
let base_cmt = self.cat_autoderef(base);
let base_cmt = self.cat_expr(base);
self.cat_field(expr, base_cmt, f_name)
}
@ -484,7 +472,8 @@ impl &mem_categorization_ctxt {
return self.cat_rvalue(expr, expr_ty);
}
self.cat_index(expr, base)
let base_cmt = self.cat_expr(base);
self.cat_index(expr, base_cmt)
}
ast::expr_path(_) => {
@ -666,11 +655,21 @@ impl &mem_categorization_ctxt {
mutbl: m, ty: self.tcx.ty(node)}
}
fn cat_deref<N:ast_node>(node: N, base_cmt: cmt, derefs: uint,
expl: bool) -> Option<cmt> {
do ty::deref(self.tcx, base_cmt.ty, expl).map |mt| {
match deref_kind(self.tcx, base_cmt.ty) {
deref_ptr(ptr) => {
fn cat_deref<N:ast_node>(node: N,
base_cmt: cmt,
deref_cnt: uint) -> cmt {
let mt = match ty::deref(self.tcx, base_cmt.ty, true) {
Some(mt) => mt,
None => {
self.tcx.sess.span_bug(
node.span(),
fmt!("Explicit deref of non-derefable type: %s",
ty_to_str(self.tcx, base_cmt.ty)));
}
};
match deref_kind(self.tcx, base_cmt.ty) {
deref_ptr(ptr) => {
let lp = do base_cmt.lp.chain |l| {
// Given that the ptr itself is loanable, we can
// loan out deref'd uniq ptrs as the data they are
@ -678,41 +677,38 @@ impl &mem_categorization_ctxt {
// Other ptr types admit aliases and are therefore
// not loanable.
match ptr {
uniq_ptr => {Some(@lp_deref(l, ptr))}
gc_ptr | region_ptr(_) | unsafe_ptr => {None}
uniq_ptr => {Some(@lp_deref(l, ptr))}
gc_ptr | region_ptr(_) | unsafe_ptr => {None}
}
};
// for unique ptrs, we inherit mutability from the
// owning reference.
let m = match ptr {
uniq_ptr => {
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
}
gc_ptr | region_ptr(_) | unsafe_ptr => {
mt.mutbl
}
uniq_ptr => {
self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
}
gc_ptr | region_ptr(_) | unsafe_ptr => {
mt.mutbl
}
};
@{id:node.id(), span:node.span(),
cat:cat_deref(base_cmt, derefs, ptr), lp:lp,
cat:cat_deref(base_cmt, deref_cnt, ptr), lp:lp,
mutbl:m, ty:mt.ty}
}
}
deref_comp(comp) => {
deref_comp(comp) => {
let lp = base_cmt.lp.map(|l| @lp_comp(l, comp) );
let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
@{id:node.id(), span:node.span(),
cat:cat_comp(base_cmt, comp), lp:lp,
mutbl:m, ty:mt.ty}
}
}
}
}
fn cat_index(expr: @ast::expr, base: @ast::expr) -> cmt {
let base_cmt = self.cat_autoderef(base);
fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt {
let mt = match ty::index(self.tcx, base_cmt.ty) {
Some(mt) => mt,
None => {
@ -781,25 +777,6 @@ impl &mem_categorization_ctxt {
mutbl:m_imm, ty:expr_ty}
}
fn cat_autoderef(base: @ast::expr) -> cmt {
// Creates a string of implicit derefences so long as base is
// dereferencable. n.b., it is important that these dereferences are
// associated with the field/index that caused the autoderef (expr).
// This is used later to adjust ref counts and so forth in trans.
// Given something like base.f where base has type @m1 @m2 T, we want
// to yield the equivalent categories to (**base).f.
let mut cmt = self.cat_expr(base);
let mut ctr = 0u;
loop {
ctr += 1u;
match self.cat_deref(base, cmt, ctr, false) {
None => return cmt,
Some(cmt1) => cmt = cmt1
}
}
}
fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
op(cmt, pat);
@ -900,15 +877,9 @@ impl &mem_categorization_ctxt {
ast::pat_box(subpat) | ast::pat_uniq(subpat) |
ast::pat_region(subpat) => {
// @p1, ~p1, &p1
match self.cat_deref(subpat, cmt, 0u, true) {
Some(subcmt) => {
self.cat_pattern(subcmt, subpat, op);
}
None => {
tcx.sess.span_bug(pat.span, ~"Non derefable type");
}
}
// @p1, ~p1
let subcmt = self.cat_deref(subpat, cmt, 0);
self.cat_pattern(subcmt, subpat, op);
}
ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ }

View File

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

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| {
let arg_val = unpack_result!(bcx, {
trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups,
if i == last { ret_flag } else { None },
0u)
if i == last { ret_flag } else { None })
});
vec::push(llargs, arg_val);
}
@ -480,18 +479,17 @@ fn trans_arg_expr(bcx: block,
formal_ty: ty::arg,
arg_expr: @ast::expr,
temp_cleanups: &mut ~[ValueRef],
ret_flag: Option<ValueRef>,
derefs: uint)
ret_flag: Option<ValueRef>)
-> Result
{
let _icx = bcx.insn_ctxt("trans_arg_expr");
let ccx = bcx.ccx();
debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \
ret_flag=%?, derefs=%?)",
ret_flag=%?)",
formal_ty.mode, bcx.ty_to_str(formal_ty.ty),
bcx.expr_to_str(arg_expr),
ret_flag.map(|v| bcx.val_str(v)), derefs);
ret_flag.map(|v| bcx.val_str(v)));
let _indenter = indenter();
// translate the arg expr to a datum
@ -528,20 +526,7 @@ fn trans_arg_expr(bcx: block,
let mut arg_datum = arg_datumblock.datum;
let mut bcx = arg_datumblock.bcx;
debug!(" initial value: %s", arg_datum.to_str(bcx.ccx()));
// auto-deref value as required (this only applies to method
// call receivers) of method
if derefs != 0 {
arg_datum = arg_datum.autoderef(bcx, arg_expr.id, derefs);
debug!(" deref'd value: %s", arg_datum.to_str(bcx.ccx()));
};
// borrow value (convert from @T to &T and so forth)
let arg_datum = unpack_datum!(bcx, {
adapt_borrowed_value(bcx, arg_datum, arg_expr)
});
debug!(" borrowed value: %s", arg_datum.to_str(bcx.ccx()));
debug!(" arg datum: %s", arg_datum.to_str(bcx.ccx()));
// finally, deal with the various modes
let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode);
@ -601,74 +586,3 @@ fn trans_arg_expr(bcx: block,
return rslt(bcx, val);
}
// when invoking a method, an argument of type @T or ~T can be implicltly
// converted to an argument of type &T. Similarly, ~[T] can be converted to
// &[T] and so on. If such a conversion (called borrowing) is necessary,
// then the borrowings table will have an appropriate entry inserted. This
// routine consults this table and performs these adaptations. It returns a
// new location for the borrowed result as well as a new type for the argument
// that reflects the borrowed value and not the original.
fn adapt_borrowed_value(bcx: block,
datum: Datum,
expr: @ast::expr) -> DatumBlock
{
if !expr_is_borrowed(bcx, expr) {
return DatumBlock {bcx: bcx, datum: datum};
}
debug!("adapt_borrowed_value(datum=%s, expr=%s)",
datum.to_str(bcx.ccx()),
bcx.expr_to_str(expr));
match ty::get(datum.ty).sty {
ty::ty_uniq(_) | ty::ty_box(_) => {
let body_datum = datum.box_body(bcx);
let rptr_datum = body_datum.to_rptr(bcx);
return DatumBlock {bcx: bcx, datum: rptr_datum};
}
ty::ty_estr(_) | ty::ty_evec(_, _) => {
let ccx = bcx.ccx();
let val = datum.to_appropriate_llval(bcx);
let unit_ty = ty::sequence_element_type(ccx.tcx, datum.ty);
let llunit_ty = type_of::type_of(ccx, unit_ty);
let (base, len) = datum.get_base_and_len(bcx);
let p = alloca(bcx, T_struct(~[T_ptr(llunit_ty), ccx.int_type]));
debug!("adapt_borrowed_value: adapting %s to %s",
val_str(bcx.ccx().tn, val),
val_str(bcx.ccx().tn, p));
Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base]));
Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
// this isn't necessarily the type that rust would assign
// but it's close enough for trans purposes, as it will
// have the same runtime representation
let slice_ty = ty::mk_evec(bcx.tcx(),
{ty: unit_ty, mutbl: ast::m_imm},
ty::vstore_slice(ty::re_static));
return DatumBlock {bcx: bcx,
datum: Datum {val: p,
mode: ByRef,
ty: slice_ty,
source: FromRvalue}};
}
_ => {
// Just take a reference. This is basically like trans_addr_of.
//
// NDM---this code is almost certainly wrong. I presume its
// purpose is auto-ref? What if an @T is autoref'd? No good.
let rptr_datum = datum.to_rptr(bcx);
return DatumBlock {bcx: bcx, datum: rptr_datum};
}
}
fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool {
bcx.tcx().borrowings.contains_key(e.id)
}
}

View File

@ -604,7 +604,7 @@ impl block {
}
fn expr_to_str(e: @ast::expr) -> ~str {
fmt!("expr(%d: %s)", e.id, expr_to_str(e, self.sess().intr()))
util::ppaux::expr_repr(self.tcx(), e)
}
fn expr_is_lval(e: @ast::expr) -> bool {
@ -1180,13 +1180,17 @@ fn path_str(sess: session::session, p: path) -> ~str {
r
}
fn monomorphize_type(bcx: block, t: ty::t) -> ty::t {
match bcx.fcx.param_substs {
Some(substs) => ty::subst_tps(bcx.tcx(), substs.tys, t),
_ => { assert !ty::type_has_params(t); t }
}
}
fn node_id_type(bcx: block, id: ast::node_id) -> ty::t {
let tcx = bcx.tcx();
let t = ty::node_id_to_type(tcx, id);
match bcx.fcx.param_substs {
Some(substs) => ty::subst_tps(tcx, substs.tys, t),
_ => { assert !ty::type_has_params(t); t }
}
monomorphize_type(bcx, t)
}
fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {

View File

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

View File

@ -41,7 +41,7 @@ fn trans_if(bcx: block,
let _icx = bcx.insn_ctxt("trans_if");
let Result {bcx, val: cond_val} =
expr::trans_to_appropriate_llval(bcx, cond);
expr::trans_to_datum(bcx, cond).to_result();
let then_bcx_in = scope_block(bcx, thn.info(), ~"then");
let else_bcx_in = scope_block(bcx, els.info(), ~"else");
@ -121,7 +121,7 @@ fn trans_while(bcx: block, cond: @ast::expr, body: ast::blk)
// compile the condition
let Result {bcx: cond_bcx_out, val: cond_val} =
expr::trans_to_appropriate_llval(cond_bcx_in, cond);
expr::trans_to_datum(cond_bcx_in, cond).to_result();
let cond_bcx_out =
trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
@ -179,7 +179,7 @@ fn trans_log(log_ex: @ast::expr,
let current_level = Load(bcx, global);
let level = unpack_result!(bcx, {
do with_scope_result(bcx, lvl.info(), ~"level") |bcx| {
expr::trans_to_appropriate_llval(bcx, lvl)
expr::trans_to_datum(bcx, lvl).to_result()
}
});
@ -278,7 +278,7 @@ fn trans_check_expr(bcx: block, chk_expr: @ast::expr,
+ ~" failed";
let Result {bcx, val} = {
do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| {
expr::trans_to_appropriate_llval(bcx, pred_expr)
expr::trans_to_datum(bcx, pred_expr).to_result()
}
};
do with_cond(bcx, Not(bcx, val)) |bcx| {

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
* returns a by-ref Datum pointing to it. You must arrange
* any cleanups etc yourself! */
* returns a by-ref Datum pointing to it. If `zero` is true, the
* space will be zeroed when it is allocated; this is normally not
* necessary, but in the case of automatic rooting in match
* statements it is possible to have temporaries that may not get
* initialized if a certain arm is not taken, so we must zero
* them. You must arrange any cleanups etc yourself! */
let llty = type_of::type_of(bcx.ccx(), ty);
let scratch = alloca_maybe_zeroed(bcx, llty, zero);

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
in specific scenarios:
- `trans_to_appropriate_llval()` can be used when you just want
an LLVM ValueRef. It will return by value if the value in
question is immediate, or by ref otherwise.
- `trans_lvalue()` is exactly like `trans_to_datum()` but it only
works on lvalues. This is mostly used as an assertion for those
places where only an lvalue is expected. It also guarantees that
@ -57,10 +54,10 @@ If you invoke `trans_into()`, no cleanup is scheduled for you. The
value is written into the given destination and is assumed to be owned
by that destination.
When you invoke `trans_to_datum()` or `trans_to_appropriate_llval()`
on an rvalue, the resulting datum/value will have an appropriate
cleanup scheduled for the innermost cleanup scope. If you later use
`move_to()` or `drop_val()`, this cleanup will be canceled.
When you invoke `trans_to_datum()` on an rvalue, the resulting
datum/value will have an appropriate cleanup scheduled for the
innermost cleanup scope. If you later use `move_to()` or
`drop_val()`, this cleanup will be canceled.
During the evaluation of an expression, temporary cleanups are created
and later canceled. These represent intermediate or partial results
@ -112,21 +109,22 @@ use base::*;
use syntax::print::pprust::{expr_to_str};
use util::ppaux::ty_to_str;
use util::common::indenter;
use ty::{AutoPtr, AutoSlice};
// The primary two functions for translating expressions:
export trans_to_datum, trans_into;
// More specific variants than trans_to_datum/trans_into that are useful
// in some scenarios:
export trans_local_var;
// Other helpers, types, and so forth:
export with_field_tys;
export Dest, SaveIn, Ignore;
export cast_type_kind;
export cast_kind, cast_pointer, cast_integral, cast_float;
export cast_enum, cast_other;
// More specific variants than trans_to_datum/trans_into that are useful
// in some scenarios:
export trans_to_appropriate_llval, trans_lvalue, trans_local_var;
// Other helpers:
export with_field_tys;
// Destinations
// These are passed around by the code generating functions to track the
@ -160,15 +158,98 @@ impl Dest : cmp::Eq {
pure fn ne(&&other: Dest) -> bool { !self.eq(other) }
}
fn trans_to_appropriate_llval(bcx: block,
expr: @ast::expr) -> common::Result {
let mut bcx = bcx;
let datum = unpack_datum!(bcx, trans_to_datum(bcx, expr));
debug!("trans_to_appropriate_llval(): datum=%s", datum.to_str(bcx.ccx()));
rslt(bcx, datum.to_appropriate_llval(bcx))
fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
return match bcx.tcx().adjustments.find(expr.id) {
None => {
trans_to_datum_unadjusted(bcx, expr)
}
Some(adj) => {
let mut bcx = bcx;
let mut datum = unpack_datum!(bcx, {
trans_to_datum_unadjusted(bcx, expr)
});
if adj.autoderefs > 0 {
datum = datum.autoderef(bcx, expr.id, adj.autoderefs);
}
datum = match adj.autoref {
None => datum,
Some(ref autoref) => {
match autoref.kind {
AutoPtr => {
unpack_datum!(bcx, auto_ref(bcx, datum))
}
AutoSlice => {
unpack_datum!(bcx, auto_slice(bcx, datum))
}
}
}
};
debug!("after adjustments, datum=%s", datum.to_str(bcx.ccx()));
return DatumBlock {bcx: bcx, datum: datum};
}
};
fn auto_ref(bcx: block, datum: Datum) -> DatumBlock {
DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)}
}
fn auto_slice(bcx: block, datum: Datum) -> DatumBlock {
// This is not the most efficient thing possible; since slices
// are two words it'd be better if this were compiled in
// 'dest' mode, but I can't find a nice way to structure the
// code and keep it DRY that accommodates that use case at the
// moment.
let tcx = bcx.tcx();
let unit_ty = ty::sequence_element_type(tcx, datum.ty);
let (base, len) = datum.get_base_and_len(bcx);
// this type may have a different region/mutability than the
// real one, but it will have the same runtime representation
let slice_ty = ty::mk_evec(tcx, {ty: unit_ty, mutbl: ast::m_imm},
ty::vstore_slice(ty::re_static));
let scratch = scratch_datum(bcx, slice_ty, false);
Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
DatumBlock {bcx: bcx, datum: scratch}
}
}
fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
return match bcx.tcx().adjustments.find(expr.id) {
None => trans_into_unadjusted(bcx, expr, dest),
Some(_) => {
// use trans_to_datum, which is mildly less efficient but
// which will perform the adjustments:
let datumblock = trans_to_datum(bcx, expr);
match dest {
Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
}
}
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
return match bcx.tcx().adjustments.find(expr.id) {
None => trans_lvalue_unadjusted(bcx, expr),
Some(_) => {
bcx.sess().span_bug(
expr.span,
fmt!("trans_lvalue() called on an expression \
with adjustments"));
}
};
}
fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
/*!
*
* Translates an expression into a datum. If this expression
@ -178,35 +259,38 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
let mut bcx = bcx;
debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
debug!("trans_to_datum_unadjusted(expr=%s)", bcx.expr_to_str(expr));
let _indenter = indenter();
debuginfo::update_source_pos(bcx, expr.span);
match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
ty::LvalueExpr => {
return trans_lvalue(bcx, expr);
return trans_lvalue_unadjusted(bcx, expr);
}
ty::RvalueDatumExpr => {
let datum = unpack_datum!(bcx, trans_rvalue_datum(bcx, expr));
let datum = unpack_datum!(bcx, {
trans_rvalue_datum_unadjusted(bcx, expr)
});
datum.add_clean(bcx);
return DatumBlock {bcx: bcx, datum: datum};
}
ty::RvalueStmtExpr => {
bcx = trans_rvalue_stmt(bcx, expr);
bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
return nil(bcx, expr_ty(bcx, expr));
}
ty::RvalueDpsExpr => {
let ty = expr_ty(bcx, expr);
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
bcx = trans_rvalue_dps(bcx, expr, Ignore);
bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
return nil(bcx, ty);
} else {
let scratch = scratch_datum(bcx, ty, false);
bcx = trans_rvalue_dps(bcx, expr, SaveIn(scratch.val));
bcx = trans_rvalue_dps_unadjusted(
bcx, expr, SaveIn(scratch.val));
// Note: this is not obviously a good idea. It causes
// immediate values to be loaded immediately after a
@ -231,10 +315,10 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
}
}
fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
fn trans_into_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block {
let ty = expr_ty(bcx, expr);
debug!("trans_into(expr=%s, dest=%s)",
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
bcx.expr_to_str(expr),
dest.to_str(bcx.ccx()));
let _indenter = indenter();
@ -253,39 +337,39 @@ fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
debug!("expr kind = %?", kind);
match kind {
ty::LvalueExpr => {
let datumblock = trans_lvalue(bcx, expr);
let datumblock = trans_lvalue_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
ty::RvalueDatumExpr => {
let datumblock = trans_rvalue_datum(bcx, expr);
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.drop_val(),
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
ty::RvalueDpsExpr => {
return trans_rvalue_dps(bcx, expr, dest);
return trans_rvalue_dps_unadjusted(bcx, expr, dest);
}
ty::RvalueStmtExpr => {
return trans_rvalue_stmt(bcx, expr);
return trans_rvalue_stmt_unadjusted(bcx, expr);
}
}
}
fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
let _icx = bcx.insn_ctxt("trans_rvalue_datum");
fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
match expr.node {
ast::expr_vstore(contents, ast::vstore_box) => {
ast::expr_vstore(contents, ast::expr_vstore_box) => {
return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared,
expr, contents);
}
ast::expr_vstore(contents, ast::vstore_uniq) => {
ast::expr_vstore(contents, ast::expr_vstore_uniq) => {
return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
expr, contents);
}
@ -310,13 +394,14 @@ fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
_ => {
bcx.tcx().sess.span_bug(
expr.span,
fmt!("trans_rvalue_datum reached fall-through case: %?",
fmt!("trans_rvalue_datum_unadjusted reached \
fall-through case: %?",
expr.node));
}
}
}
fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
let mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rvalue_stmt");
@ -378,22 +463,25 @@ fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
_ => {
bcx.tcx().sess.span_bug(
expr.span,
fmt!("trans_rvalue_stmt reached fall-through case: %?",
fmt!("trans_rvalue_stmt_unadjusted reached \
fall-through case: %?",
expr.node));
}
};
}
fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
dest: Dest) -> block {
let mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rvalue_dps");
let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted");
let tcx = bcx.tcx();
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
match expr.node {
ast::expr_path(_) => {
return trans_def_dps(bcx, expr, bcx.def(expr.id), dest);
return trans_def_dps_unadjusted(bcx, expr,
bcx.def(expr.id), dest);
}
ast::expr_if(cond, thn, els) => {
return controlflow::trans_if(bcx, cond, thn, els, dest);
@ -416,10 +504,10 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
ast::expr_lit(@{node: ast::lit_str(s), _}) => {
return tvec::trans_lit_str(bcx, expr, s, dest);
}
ast::expr_vstore(contents, ast::vstore_slice(_)) => {
ast::expr_vstore(contents, ast::expr_vstore_slice) => {
return tvec::trans_slice_vstore(bcx, expr, contents, dest);
}
ast::expr_vstore(contents, ast::vstore_fixed(_)) => {
ast::expr_vstore(contents, ast::expr_vstore_fixed(_)) => {
return tvec::trans_fixed_vstore(bcx, expr, contents, dest);
}
ast::expr_vec(*) | ast::expr_repeat(*) => {
@ -527,15 +615,16 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
_ => {
bcx.tcx().sess.span_bug(
expr.span,
fmt!("trans_rvalue_dps reached fall-through case: %?",
fmt!("trans_rvalue_dps_unadjusted reached \
fall-through case: %?",
expr.node));
}
}
}
fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
def: ast::def, dest: Dest) -> block {
let _icx = bcx.insn_ctxt("trans_def_dps");
fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
def: ast::def, dest: Dest) -> block {
let _icx = bcx.insn_ctxt("trans_def_dps_unadjusted");
let ccx = bcx.ccx();
let lldest = match dest {
@ -575,13 +664,13 @@ fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
}
}
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
//!
//
// Translates an lvalue expression, always yielding a by-ref
// datum. Generally speaking you should call trans_to_datum()
// instead, but sometimes we call trans_lvalue() directly as a
// means of asserting that a particular expression is an lvalue.
fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
/*!
*
* Translates an lvalue expression, always yielding a by-ref
* datum. Generally speaking you should call trans_to_datum()
* instead, but sometimes we call trans_lvalue() directly as a
* means of asserting that a particular expression is an lvalue. */
let _icx = bcx.insn_ctxt("trans_lval");
let mut bcx = bcx;
@ -798,12 +887,7 @@ fn trans_rec_field(bcx: block,
let mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rec_field");
// Translate and autoderef the base expression. We should have a
// record or a struct when we're done, both of which are currently
// non-immediate and hence always tracked by reference.
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| {
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
DatumBlock {
@ -822,14 +906,11 @@ fn trans_index(bcx: block,
let base_ty = expr_ty(bcx, base);
let mut bcx = bcx;
// Translate and autoderef the base expression. We should have some sort
// of vector (@[], &[], ~[], []/_, etc) when we're done.
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
// Translate index expression and cast to a suitable LLVM integer.
// Rust is less strict than LLVM in this regard.
let Result {bcx, val: ix_val} = trans_to_appropriate_llval(bcx, idx);
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
let ix_size = shape::llsize_of_real(bcx.ccx(), val_ty(ix_val));
let int_size = shape::llsize_of_real(bcx.ccx(), ccx.int_type);
let ix_val = {
@ -989,11 +1070,11 @@ fn trans_unary_datum(bcx: block,
return match op {
ast::not => {
let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty)
}
ast::neg => {
let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
let llneg = {
if ty::type_is_fp(un_ty) {
FNeg(bcx, val)
@ -1153,7 +1234,7 @@ fn trans_lazy_binop(bcx: block,
let Result {bcx: past_lhs, val: lhs} = {
do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
trans_to_appropriate_llval(bcx, a)
trans_to_datum(bcx, a).to_result()
}
};
@ -1170,7 +1251,7 @@ fn trans_lazy_binop(bcx: block,
}
let Result {bcx: past_rhs, val: rhs} = {
do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| {
trans_to_appropriate_llval(bcx, b)
trans_to_datum(bcx, b).to_result()
}
};
@ -1299,7 +1380,7 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
let t_out = node_id_type(bcx, id);
let mut bcx = bcx;
let llexpr = unpack_result!(bcx, trans_to_appropriate_llval(bcx, expr));
let llexpr = unpack_result!(bcx, trans_to_datum(bcx, expr).to_result());
let ll_t_in = val_ty(llexpr);
let t_in = expr_ty(bcx, expr);
let ll_t_out = type_of::type_of(ccx, t_out);

View File

@ -95,18 +95,17 @@ fn trans_method(ccx: @crate_ctxt,
fn trans_self_arg(bcx: block, base: @ast::expr,
mentry: typeck::method_map_entry) -> Result {
let _icx = bcx.insn_ctxt("impl::trans_self_arg");
let basety = expr_ty(bcx, base);
let mode = ast::expl(mentry.self_mode);
let mut temp_cleanups = ~[];
let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base,
&mut temp_cleanups, None, mentry.derefs);
let self_arg = {mode: mentry.self_arg.mode,
ty: monomorphize_type(bcx, mentry.self_arg.ty)};
let result = trans_arg_expr(bcx, self_arg, base,
&mut temp_cleanups, None);
// by-ref self argument should not require cleanup in the case of
// other arguments failing:
//assert temp_cleanups == ~[];
//do vec::iter(temp_cleanups) |c| {
// revoke_clean(bcx, c)
//}
// FIXME(#3446)---this is wrong, actually. The temp_cleanups
// should be revoked only after all arguments have been passed.
for temp_cleanups.each |c| {
revoke_clean(bcx, c)
}
return result;
}
@ -120,14 +119,14 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
typeck::method_static(did) => {
let callee_fn = callee::trans_fn_ref(bcx, did, callee_id);
let Result {bcx, val} = trans_self_arg(bcx, self, mentry);
let tcx = bcx.tcx();
Callee {
bcx: bcx,
data: Method(MethodData {
llfn: callee_fn.llfn,
llself: val,
self_ty: node_id_type(bcx, self.id),
self_mode: mentry.self_mode
self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
})
}
}
@ -144,7 +143,7 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
}
}
typeck::method_trait(_, off) => {
trans_trait_callee(bcx, callee_id, off, self, mentry.derefs)
trans_trait_callee(bcx, callee_id, off, self)
}
}
}
@ -176,7 +175,9 @@ fn trans_static_method_callee(bcx: block,
let vtbls = resolve_vtables_in_fn_ctxt(
bcx.fcx, ccx.maps.vtable_map.get(callee_id));
match vtbls[0] { // is index 0 always the one we want?
// FIXME(#3446) -- I am pretty sure index 0 is not the right one,
// if the static method is implemented on a generic type. (NDM)
match vtbls[0] {
typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
@ -276,18 +277,19 @@ fn trans_monomorphized_callee(bcx: block,
let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty);
// combine the self environment with the rest
let tcx = bcx.tcx();
Callee {
bcx: bcx,
data: Method(MethodData {
llfn: llfn_val,
llself: llself_val,
self_ty: node_id_type(bcx, base.id),
self_mode: mentry.self_mode
self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
})
}
}
typeck::vtable_trait(*) => {
trans_trait_callee(bcx, callee_id, n_method, base, mentry.derefs)
trans_trait_callee(bcx, callee_id, n_method, base)
}
typeck::vtable_param(*) => {
fail ~"vtable_param left in monomorphized function's vtable substs";
@ -388,8 +390,7 @@ fn combine_impl_and_methods_origins(bcx: block,
fn trans_trait_callee(bcx: block,
callee_id: ast::node_id,
n_method: uint,
self_expr: @ast::expr,
autoderefs: uint)
self_expr: @ast::expr)
-> Callee
{
//!
@ -404,7 +405,6 @@ fn trans_trait_callee(bcx: block,
let _icx = bcx.insn_ctxt("impl::trans_trait_callee");
let mut bcx = bcx;
let self_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr));
let self_datum = self_datum.autoderef(bcx, self_expr.id, autoderefs);
let llpair = self_datum.to_ref_llval(bcx);
let callee_ty = node_id_type(bcx, callee_id);
trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair)

View File

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

View File

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

View File

@ -102,7 +102,7 @@ struct inherited {
locals: HashMap<ast::node_id, TyVid>,
node_types: HashMap<ast::node_id, ty::t>,
node_type_substs: HashMap<ast::node_id, ty::substs>,
borrowings: HashMap<ast::node_id, ty::borrow>,
adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
}
struct fn_ctxt {
@ -143,7 +143,7 @@ fn blank_inherited(ccx: @crate_ctxt) -> @inherited {
locals: int_hash(),
node_types: map::int_hash(),
node_type_substs: map::int_hash(),
borrowings: map::int_hash()
adjustments: map::int_hash()
}
}
@ -604,6 +604,7 @@ impl @fn_ctxt {
node_id, ty_to_str(self.tcx(), ty), self.tag());
self.inh.node_types.insert(node_id, ty);
}
fn write_substs(node_id: ast::node_id, +substs: ty::substs) {
if !ty::substs_is_noop(&substs) {
debug!("write_substs(%d, %s) in fcx %s",
@ -613,12 +614,24 @@ impl @fn_ctxt {
self.inh.node_type_substs.insert(node_id, substs);
}
}
fn write_ty_substs(node_id: ast::node_id, ty: ty::t,
+substs: ty::substs) {
let ty = ty::subst(self.tcx(), &substs, ty);
self.write_ty(node_id, ty);
self.write_substs(node_id, substs);
}
fn write_autoderef_adjustment(node_id: ast::node_id, derefs: uint) {
if derefs == 0 { return; }
self.write_adjustment(node_id, @{autoderefs: derefs, autoref: None});
}
fn write_adjustment(node_id: ast::node_id, adj: @ty::AutoAdjustment) {
debug!("write_adjustment(node_id=%?, adj=%?)", node_id, adj);
self.inh.adjustments.insert(node_id, adj);
}
fn write_nil(node_id: ast::node_id) {
self.write_ty(node_id, ty::mk_nil(self.tcx()));
}
@ -630,14 +643,17 @@ impl @fn_ctxt {
ast_ty_to_ty(self, self, ast_t)
}
fn expr_to_str(expr: @ast::expr) -> ~str {
expr_repr(self.tcx(), expr)
}
fn expr_ty(ex: @ast::expr) -> ty::t {
match self.inh.node_types.find(ex.id) {
Some(t) => t,
None => {
self.tcx().sess.bug(
fmt!("no type for expr %d (%s) in fcx %s",
ex.id, expr_to_str(ex, self.ccx.tcx.sess.intr()),
self.tag()));
fmt!("no type for %s in fcx %s",
self.expr_to_str(ex), self.tag()));
}
}
}
@ -691,22 +707,15 @@ impl @fn_ctxt {
infer::can_mk_subty(self.infcx(), sub, sup)
}
fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> {
fn mk_assignty(expr: @ast::expr, sub: ty::t, sup: ty::t)
-> Result<(), ty::type_err>
{
match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) {
Ok(None) => result::Ok(()),
Err(e) => result::Err(e),
Ok(Some(borrow)) => {
match self.mk_subr(true, expr.span,
ty::re_scope(borrow_lb), borrow.region) {
Err(e) => Err(e),
Ok(()) => {
debug!("inserting borrowing of expr %?: %?",
expr.id, borrow);
self.inh.borrowings.insert(expr.id, borrow);
Ok(())
}
}
Ok(Some(adjustment)) => {
self.write_adjustment(expr.id, adjustment);
Ok(())
}
}
}
@ -753,9 +762,18 @@ impl @fn_ctxt {
}
}
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> (ty::t, uint) {
/*!
*
* Autoderefs the type `t` as many times as possible, returning
* a new type and a counter for how many times the type was
* deref'd. If the counter is non-zero, the receiver is responsible
* for inserting an AutoAdjustment record into `tcx.adjustments`
* so that trans/borrowck/etc know about this autoderef. */
let mut t1 = t;
let mut enum_dids = ~[];
let mut autoderefs = 0;
loop {
let sty = structure_of(fcx, sp, t1);
@ -773,13 +791,14 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
}
ty::ty_enum(did, _) => {
// Watch out for a type like `enum t = @t`. Such a
// type would otherwise infinitely auto-deref. This
// is the only autoderef loop that needs to be
// type would otherwise infinitely auto-deref. Only
// autoderef loops during typeck (basically, this one
// and the loops in typeck::check::method) need to be
// concerned with this, as an error will be reported
// on the enum definition as well because the enum is
// not instantiable.
if vec::contains(enum_dids, did) {
return t1;
return (t1, autoderefs);
}
vec::push(enum_dids, did);
}
@ -788,8 +807,13 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
// Otherwise, deref if type is derefable:
match ty::deref_sty(fcx.ccx.tcx, &sty, false) {
None => return t1,
Some(mt) => t1 = mt.ty
None => {
return (t1, autoderefs);
}
Some(mt) => {
autoderefs += 1;
t1 = mt.ty
}
}
};
}
@ -904,16 +928,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
expected: Option<ty::t>,
unifier: fn()) -> bool {
debug!(
">> typechecking expr %d (%s)",
expr.id, syntax::print::pprust::expr_to_str(expr,
fcx.ccx.tcx.sess.intr()));
debug!(">> typechecking %s", fcx.expr_to_str(expr));
// A generic function to factor out common logic from call and
// overloaded operations
fn check_call_inner(
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
fcx: @fn_ctxt,
sp: span,
call_expr_id: ast::node_id,
in_fty: ty::t,
callee_expr: @ast::expr,
check_args: bool,
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
let mut bot = false;
@ -956,7 +981,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Grab the argument types, supplying fresh type variables
// if the wrong number of arguments were supplied
let expected_arg_count = vec::len(fn_ty.sig.inputs);
let arg_tys = if expected_arg_count == supplied_arg_count {
let formal_tys = if expected_arg_count == supplied_arg_count {
fn_ty.sig.inputs.map(|a| a.ty)
} else {
fcx.ccx.tcx.sess.span_err(
@ -983,6 +1008,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// of arguments when we typecheck the functions. This isn't really the
// right way to do this.
for [false, true]/_.each |check_blocks| {
debug!("check_blocks=%b", check_blocks);
// More awful hacks: before we check the blocks, try to do
// an "opportunistic" vtable resolution of any trait
// bounds on the call.
@ -990,19 +1017,26 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
vtable::early_resolve_expr(callee_expr, fcx, true);
}
for args.eachi |i, a| {
let is_block = match a.node {
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
ast::expr_do_body(*) => true,
_ => false
for args.eachi |i, arg| {
let is_block = match arg.node {
ast::expr_fn_block(*) | ast::expr_loop_body(*) |
ast::expr_do_body(*) => true,
_ => false
};
if is_block == check_blocks {
let arg_ty = arg_tys[i];
bot |= check_expr_with_unifier(
fcx, a, Some(arg_ty),
|| demand::assign(fcx, a.span, call_expr_id,
arg_ty, a)
debug!("checking the argument");
let formal_ty = formal_tys[i];
if check_args {
bot |= check_expr_with_unifier(
fcx, arg, Some(formal_ty),
|| demand::assign(fcx, arg.span, formal_ty, arg)
);
} else {
demand::assign(fcx, arg.span, formal_ty, arg);
bot |= ty::type_is_bot(fcx.expr_ty(arg));
}
}
}
}
@ -1036,7 +1070,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Call the generic checker.
let fty = {
let r = check_call_inner(fcx, sp, call_expr_id,
fn_ty, f, args);
fn_ty, f, true, args);
bot |= r.bot;
r.fty
};
@ -1092,16 +1126,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
self_ex: @ast::expr, self_t: ty::t,
opname: ast::ident, args: ~[@ast::expr])
-> Option<(ty::t, bool)> {
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
op_ex.callee_id, opname, self_t, ~[], false);
match lkup.method() {
opname: ast::ident, check_args: bool,
args: ~[@ast::expr])
-> Option<(ty::t, bool)>
{
match method::lookup(fcx, op_ex, self_ex,
op_ex.callee_id, opname, self_t, ~[]) {
Some(origin) => {
let {fty: method_ty, bot: bot} = {
let method_ty = fcx.node_ty(op_ex.callee_id);
check_call_inner(fcx, op_ex.span, op_ex.id,
method_ty, op_ex, args)
method_ty, op_ex, check_args, args)
};
fcx.ccx.method_map.insert(op_ex.id, origin);
Some((ty::ty_fn_ret(method_ty), bot))
@ -1109,71 +1144,95 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
_ => None
}
}
fn check_rel_op(fcx: @fn_ctxt,
expr: @ast::expr,
op: ast::binop,
lhs: @ast::expr,
rhs: @ast::expr) -> bool
{
// We know that only things of equal type can be compared, so
// go ahead and unify the two types before we do anything else
// (with other operators, we must be much more careful not to
// make assumptions, due to the possibility of operator
// overloading; but overloaded == still enforces the
// requirement that only equal types are compared).
let tcx = fcx.ccx.tcx;
let lhs_bot = check_expr(fcx, lhs, None);
let lhs_t = fcx.expr_ty(lhs);
let rhs_bot = check_expr_with(fcx, rhs, lhs_t);
let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
if ty::is_binopable(tcx, lhs_t, op) {
let result_t = ty::mk_bool(tcx);
fcx.write_ty(expr.id, result_t);
return lhs_bot | rhs_bot;
}
let (result, rhs_bot) =
check_user_binop(fcx, expr, lhs, lhs_t, op, false, rhs);
fcx.write_ty(expr.id, result);
return lhs_bot | rhs_bot;
}
// could be either a expr_binop or an expr_assign_binop
fn check_binop(fcx: @fn_ctxt, expr: @ast::expr,
op: ast::binop,
lhs: @ast::expr,
rhs: @ast::expr) -> bool {
let tcx = fcx.ccx.tcx;
let lhs_bot = check_expr(fcx, lhs, None);
let lhs_t = fcx.expr_ty(lhs);
// Hack: Unify the two sides if this is a relational operator.
// Relational operators are different for type inferencing
// reasons.
match op {
ast::eq | ast::ne | ast::lt | ast::le | ast::ge | ast::gt => {
check_expr_with(fcx, rhs, lhs_t);
return check_rel_op(fcx, expr, op, lhs, rhs);
}
_ => {}
}
let lhs_bot = check_expr(fcx, lhs, None);
let lhs_t = fcx.expr_ty(lhs);
let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
return match (op, ty::get(lhs_t).sty) {
(_, _) if ty::type_is_integral(lhs_t) &&
ast_util::is_shift_binop(op) => {
if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
// Shift is a special case: rhs can be any integral type
let rhs_bot = check_expr(fcx, rhs, None);
let rhs_t = fcx.expr_ty(rhs);
require_integral(fcx, rhs.span, rhs_t);
fcx.write_ty(expr.id, lhs_t);
lhs_bot | rhs_bot
}
return lhs_bot | rhs_bot;
}
(_, _) if ty::is_binopable(tcx, lhs_t, op) => {
if ty::is_binopable(tcx, lhs_t, op) {
let tvar = fcx.infcx().next_ty_var();
demand::suptype(fcx, expr.span, tvar, lhs_t);
let rhs_bot = check_expr_with(fcx, rhs, tvar);
let result_t = match op {
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
ast::gt => {
if !ty::type_is_scalar(lhs_t) {
fcx.ccx.tcx.sess.span_bug(expr.span,
~"non-scalar compare");
}
ty::mk_bool(fcx.ccx.tcx)
}
_ => lhs_t
};
let result_t = lhs_t;
fcx.write_ty(expr.id, result_t);
if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
else { lhs_bot }
}
return {
if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
else { lhs_bot }
};
}
(_, _) => {
let (result, rhs_bot) =
check_user_binop(fcx, expr, lhs, lhs_t, op, rhs);
fcx.write_ty(expr.id, result);
lhs_bot | rhs_bot
}
};
let (result, rhs_bot) =
check_user_binop(fcx, expr, lhs, lhs_t, op, true, rhs);
fcx.write_ty(expr.id, result);
return lhs_bot | rhs_bot;
}
fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr,
lhs_expr: @ast::expr, lhs_resolved_t: ty::t,
op: ast::binop, rhs: @ast::expr) -> (ty::t, bool) {
op: ast::binop, check_rhs: bool,
rhs: @ast::expr) -> (ty::t, bool)
{
let tcx = fcx.ccx.tcx;
match ast_util::binop_to_method_name(op) {
Some(name) => {
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
fcx.tcx().sess.ident_of(name), ~[rhs]) {
fcx.tcx().sess.ident_of(name),
check_rhs, ~[rhs]) {
Some(pair) => return pair,
_ => ()
}
@ -1202,11 +1261,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
(lhs_resolved_t, false)
}
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
ex: @ast::expr,
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
match lookup_op_method(fcx, ex, rhs_expr, rhs_t,
fcx.tcx().sess.ident_of(mname), ~[]) {
fcx.tcx().sess.ident_of(mname), true, ~[]) {
Some((ret_ty, _)) => ret_ty,
_ => {
fcx.ccx.tcx.sess.span_err(
@ -1308,87 +1368,84 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Check field access expressions
fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool,
base: @ast::expr, field: ast::ident, tys: ~[@ast::ty])
-> bool {
-> bool
{
let tcx = fcx.ccx.tcx;
let bot = check_expr(fcx, base, None);
let expr_t = structurally_resolved_type(fcx, expr.span,
fcx.expr_ty(base));
let base_t = do_autoderef(fcx, expr.span, expr_t);
let mut handled = false;
let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
let n_tys = vec::len(tys);
match structure_of(fcx, expr.span, base_t) {
ty::ty_rec(fields) => {
match ty::field_idx(field, fields) {
Some(ix) => {
if n_tys > 0u {
tcx.sess.span_err(expr.span,
~"can't provide type parameters \
to a field access");
}
fcx.write_ty(expr.id, fields[ix].mt.ty);
handled = true;
}
_ => ()
}
}
ty::ty_class(base_id, substs) => {
// This is just for fields -- the same code handles
// methods in both classes and traits
// (1) verify that the class id actually has a field called
// field
debug!("class named %s", ty_to_str(tcx, base_t));
let cls_items = ty::lookup_class_fields(tcx, base_id);
match lookup_field_ty(tcx, base_id, cls_items, field, &substs) {
Some(field_ty) => {
// (2) look up what field's type is, and return it
fcx.write_ty(expr.id, field_ty);
handled = true;
}
None => ()
}
}
_ => ()
}
if !handled {
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
let is_self_ref = self_ref(fcx, base.id);
// this will be the call or block that immediately
// encloses the method call
let borrow_lb = fcx.tcx().region_map.get(expr.id);
let lkup = method::lookup(fcx, expr, base, borrow_lb,
expr.id, field, expr_t, tps,
is_self_ref);
match lkup.method() {
Some(entry) => {
fcx.ccx.method_map.insert(expr.id, entry);
// If we have resolved to a method but this is not in
// a callee position, error
if !is_callee {
tcx.sess.span_err(
expr.span,
~"attempted to take value of method \
(try writing an anonymous function)");
ty::ty_rec(fields) => {
match ty::field_idx(field, fields) {
Some(ix) => {
if n_tys > 0u {
tcx.sess.span_err(
expr.span,
~"can't provide type parameters \
to a field access");
}
fcx.write_ty(expr.id, fields[ix].mt.ty);
fcx.write_autoderef_adjustment(base.id, derefs);
return bot;
}
}
None => {
let t_err =
fcx.infcx().resolve_type_vars_if_possible(expr_t);
let msg =
fmt!(
"attempted access of field `%s` on type `%s`, \
but no field or method with that name was found",
tcx.sess.str_of(field),
fcx.infcx().ty_to_str(t_err));
tcx.sess.span_err(expr.span, msg);
// NB: Add bogus type to allow typechecking to continue
fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
_ => ()
}
}
ty::ty_class(base_id, substs) => {
// This is just for fields -- the same code handles
// methods in both classes and traits
// (1) verify that the class id actually has a field called
// field
debug!("class named %s", ty_to_str(tcx, base_t));
let cls_items = ty::lookup_class_fields(tcx, base_id);
match lookup_field_ty(tcx, base_id, cls_items,
field, &substs) {
Some(field_ty) => {
// (2) look up what field's type is, and return it
fcx.write_ty(expr.id, field_ty);
fcx.write_autoderef_adjustment(base.id, derefs);
return bot;
}
None => ()
}
}
_ => ()
}
let tps = vec::map(tys, |ty| fcx.to_ty(ty));
match method::lookup(fcx, expr, base, expr.id,
field, expr_t, tps) {
Some(entry) => {
fcx.ccx.method_map.insert(expr.id, entry);
// If we have resolved to a method but this is not in
// a callee position, error
if !is_callee {
tcx.sess.span_err(
expr.span,
~"attempted to take value of method \
(try writing an anonymous function)");
}
}
None => {
let t_err =
fcx.infcx().resolve_type_vars_if_possible(expr_t);
let msg =
fmt!(
"attempted access of field `%s` on type `%s`, \
but no field or method with that name was found",
tcx.sess.str_of(field),
fcx.infcx().ty_to_str(t_err));
tcx.sess.span_err(expr.span, msg);
// NB: Add bogus type to allow typechecking to continue
fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
}
}
return bot;
}
@ -2000,32 +2057,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
bot = check_field(fcx, expr, false, base, field, tys);
}
ast::expr_index(base, idx) => {
bot |= check_expr(fcx, base, None);
let raw_base_t = fcx.expr_ty(base);
let base_t = do_autoderef(fcx, expr.span, raw_base_t);
bot |= check_expr(fcx, idx, None);
let idx_t = fcx.expr_ty(idx);
let base_sty = structure_of(fcx, expr.span, base_t);
match ty::index_sty(tcx, &base_sty) {
Some(mt) => {
require_integral(fcx, idx.span, idx_t);
fcx.write_ty(id, mt.ty);
}
None => {
let resolved = structurally_resolved_type(fcx, expr.span,
raw_base_t);
match lookup_op_method(fcx, expr, base, resolved,
tcx.sess.ident_of(~"index"),
~[idx]) {
Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
_ => {
tcx.sess.span_fatal(
expr.span, ~"cannot index a value of type `" +
fcx.infcx().ty_to_str(base_t) + ~"`");
bot |= check_expr(fcx, base, None);
let raw_base_t = fcx.expr_ty(base);
let (base_t, derefs) = do_autoderef(fcx, expr.span, raw_base_t);
bot |= check_expr(fcx, idx, None);
let idx_t = fcx.expr_ty(idx);
let base_sty = structure_of(fcx, expr.span, base_t);
match ty::index_sty(tcx, &base_sty) {
Some(mt) => {
require_integral(fcx, idx.span, idx_t);
fcx.write_ty(id, mt.ty);
fcx.write_autoderef_adjustment(base.id, derefs);
}
None => {
let resolved = structurally_resolved_type(fcx, expr.span,
raw_base_t);
match lookup_op_method(fcx, expr, base, resolved,
tcx.sess.ident_of(~"index"), true,
~[idx]) {
Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
_ => {
tcx.sess.span_fatal(
expr.span, ~"cannot index a value of type `" +
fcx.infcx().ty_to_str(base_t) + ~"`");
}
}
}
}
}
}
}
}
if bot { fcx.write_bot(expr.id); }
@ -2304,16 +2362,6 @@ fn check_enum_variants(ccx: @crate_ctxt,
check_instantiable(ccx.tcx, sp, id);
}
// Determines whether the given node ID is a use of the def of
// the self ID for the current method, if there is one
// self IDs in an outer scope count. so that means that you can
// call your own private methods from nested functions inside
// class methods
fn self_ref(fcx: @fn_ctxt, id: ast::node_id) -> bool {
option::map_default(fcx.ccx.tcx.def_map.find(id), false,
ast_util::is_self)
}
fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid {
match fcx.inh.locals.find(id) {
Some(x) => x,
@ -2480,19 +2528,19 @@ fn type_is_c_like_enum(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
}
fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
v: ast::vstore) -> ty::vstore {
v: ast::expr_vstore) -> ty::vstore {
match v {
ast::vstore_fixed(None) => ty::vstore_fixed(n),
ast::vstore_fixed(Some(u)) => {
ast::expr_vstore_fixed(None) => ty::vstore_fixed(n),
ast::expr_vstore_fixed(Some(u)) => {
if n != u {
let s = fmt!("fixed-size sequence mismatch: %u vs. %u",u, n);
fcx.ccx.tcx.sess.span_err(e.span,s);
}
ty::vstore_fixed(u)
}
ast::vstore_uniq => ty::vstore_uniq,
ast::vstore_box => ty::vstore_box,
ast::vstore_slice(_) => {
ast::expr_vstore_uniq => ty::vstore_uniq,
ast::expr_vstore_box => ty::vstore_box,
ast::expr_vstore_slice => {
let r = fcx.infcx().next_region_var(e.span, e.id);
ty::vstore_slice(r)
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -155,116 +155,157 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
visit::visit_block(b, rcx, v);
}
fn visit_expr(e: @ast::expr, &&rcx: @rcx, v: rvt) {
fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
debug!("visit_expr(e=%s)",
pprust::expr_to_str(e, rcx.fcx.tcx().sess.intr()));
pprust::expr_to_str(expr, rcx.fcx.tcx().sess.intr()));
match e.node {
ast::expr_path(*) => {
// Avoid checking the use of local variables, as we already
// check their definitions. The def'n always encloses the
// use. So if the def'n is enclosed by the region, then the
// uses will also be enclosed (and otherwise, an error will
// have been reported at the def'n site).
match lookup_def(rcx.fcx, e.span, e.id) {
ast::def_local(*) | ast::def_arg(*) | ast::def_upvar(*) => return,
_ => ()
}
}
// constrain_auto_ref(rcx, expr);
ast::expr_cast(source, _) => {
// Determine if we are casting `source` to an trait instance.
// If so, we have to be sure that the type of the source obeys
// the trait's region bound.
//
// Note: there is a subtle point here concerning type
// parameters. It is possible that the type of `source`
// contains type parameters, which in turn may contain regions
// that are not visible to us (only the caller knows about
// them). The kind checker is ultimately responsible for
// guaranteeing region safety in that particular case. There
// is an extensive comment on the function
// check_cast_for_escaping_regions() in kind.rs explaining how
// it goes about doing that.
match rcx.resolve_node_type(e.id) {
result::Err(_) => { return; /* typeck will fail anyhow */ }
result::Ok(target_ty) => {
match ty::get(target_ty).sty {
ty::ty_trait(_, substs, _) => {
let trait_region = match substs.self_r {
Some(r) => {r}
None => {ty::re_static}
};
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, trait_region,
e.span, source_ty);
}
_ => ()
match expr.node {
ast::expr_path(*) => {
// Avoid checking the use of local variables, as we
// already check their definitions. The def'n always
// encloses the use. So if the def'n is enclosed by the
// region, then the uses will also be enclosed (and
// otherwise, an error will have been reported at the
// def'n site).
match lookup_def(rcx.fcx, expr.span, expr.id) {
ast::def_local(*) | ast::def_arg(*) |
ast::def_upvar(*) => return,
_ => ()
}
}
};
}
ast::expr_addr_of(*) => {
// FIXME(#3148) -- in some cases, we need to capture a dependency
// between the regions found in operand the resulting region type.
// See #3148 for more details.
}
ast::expr_fn(*) | ast::expr_fn_block(*) => {
match rcx.resolve_node_type(e.id) {
result::Err(_) => return, // Typechecking will fail anyhow.
result::Ok(function_type) => {
match ty::get(function_type).sty {
ty::ty_fn(ref fn_ty) => {
match fn_ty.meta.proto {
proto_vstore(vstore_slice(region)) => {
constrain_free_variables(rcx, region, e);
}
_ => {}
}
}
_ => ()
}
}
}
}
_ => ()
ast::expr_cast(source, _) => {
// Determine if we are casting `source` to an trait
// instance. If so, we have to be sure that the type of
// the source obeys the trait's region bound.
//
// Note: there is a subtle point here concerning type
// parameters. It is possible that the type of `source`
// contains type parameters, which in turn may contain
// regions that are not visible to us (only the caller
// knows about them). The kind checker is ultimately
// responsible for guaranteeing region safety in that
// particular case. There is an extensive comment on the
// function check_cast_for_escaping_regions() in kind.rs
// explaining how it goes about doing that.
match rcx.resolve_node_type(expr.id) {
result::Err(_) => { return; /*typeck will fail anyhow*/ }
result::Ok(target_ty) => {
match ty::get(target_ty).sty {
ty::ty_trait(_, substs, _) => {
let trait_region = match substs.self_r {
Some(r) => {r}
None => {ty::re_static}
};
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, trait_region,
expr.span, source_ty);
}
_ => ()
}
}
};
}
ast::expr_addr_of(*) => {
// FIXME(#3148) -- in some cases, we need to capture a
// dependency between the regions found in operand the
// resulting region type. See #3148 for more details.
}
ast::expr_fn(*) | ast::expr_fn_block(*) => {
match rcx.resolve_node_type(expr.id) {
result::Err(_) => return, // Typechecking will fail anyhow.
result::Ok(function_type) => {
match ty::get(function_type).sty {
ty::ty_fn(ref fn_ty) => {
match fn_ty.meta.proto {
proto_vstore(vstore_slice(region)) => {
constrain_free_variables(rcx, region,
expr);
}
_ => {}
}
}
_ => ()
}
}
}
}
_ => ()
}
if !visit_node(e.id, e.span, rcx) { return; }
visit::visit_expr(e, rcx, v);
if !visit_node(expr.id, expr.span, rcx) { return; }
visit::visit_expr(expr, rcx, v);
}
fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
visit::visit_stmt(s, rcx, v);
}
// checks the type of the node `id` and reports an error if it
// references a region that is not in scope for that node. Returns
// false if an error is reported; this is used to cause us to cut off
// region checking for that subtree to avoid reporting tons of errors.
fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
let fcx = rcx.fcx;
/*!
*
* checks the type of the node `id` and reports an error if it
* references a region that is not in scope for that node.
* Returns false if an error is reported; this is used to cause us
* to cut off region checking for that subtree to avoid reporting
* tons of errors. */
// Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase.
let ty = match rcx.resolve_node_type(id) {
result::Err(_) => return true,
result::Ok(ty) => ty
};
let fcx = rcx.fcx;
// find the region where this expr evaluation is taking place
let tcx = fcx.ccx.tcx;
let encl_region = ty::encl_region(tcx, id);
debug!("visit_node(ty=%s, id=%d, encl_region=%?)",
ty_to_str(tcx, ty), id, encl_region);
// Otherwise, look at the type and see if it is a region pointer.
return constrain_regions_in_type(rcx, encl_region, span, ty);
constrain_regions_in_type_of_node(rcx, id, encl_region, span)
}
fn constrain_auto_ref(
rcx: @rcx,
expr: @ast::expr)
{
/*!
*
* If `expr` is auto-ref'd (e.g., as part of a borrow), then this
* function ensures that the lifetime of the resulting borrowed
* ptr includes at least the expression `expr`. */
let adjustment = rcx.fcx.inh.adjustments.find(expr.id);
let region = match adjustment {
Some(@{autoref: Some(ref auto_ref), _}) => auto_ref.region,
_ => { return; }
};
let tcx = rcx.fcx.tcx();
let expr_region = ty::re_scope(expr.id);
match rcx.fcx.mk_subr(true, expr.span, expr_region, region) {
result::Ok(()) => {}
result::Err(_) => {
// In practice, this cannot happen: `region` is always a
// region variable, and constraints on region variables
// are collected and then resolved later. However, I
// included the span_err() here (rather than, say,
// span_bug()) because it seemed more future-proof: if,
// for some reason, the code were to change so that in
// some cases `region` is not a region variable, then
// reporting an error would be the correct path.
tcx.sess.span_err(
expr.span,
~"lifetime of borrowed pointer does not include \
the expression being borrowed");
note_and_explain_region(
tcx,
~"lifetime of the borrowed pointer is",
region,
~"");
rcx.errors_reported += 1;
}
}
}
fn constrain_free_variables(
@ -272,9 +313,11 @@ fn constrain_free_variables(
region: ty::region,
expr: @ast::expr)
{
// Make sure that all regions referenced by the free
// variables inside the closure outlive the closure
// itself.
/*!
*
* Make sure that all free variables referenced inside the closure
* outlive the closure itself. */
let tcx = rcx.fcx.ccx.tcx;
for get_freevars(tcx, expr.id).each |freevar| {
debug!("freevar def is %?", freevar.def);
@ -302,11 +345,44 @@ fn constrain_free_variables(
}
}
fn constrain_regions_in_type_of_node(
rcx: @rcx,
id: ast::node_id,
encl_region: ty::region,
span: span) -> bool
{
let tcx = rcx.fcx.tcx();
// Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase.
let ty = match rcx.resolve_node_type(id) {
result::Err(_) => return true,
result::Ok(ty) => ty
};
debug!("constrain_regions_in_type_of_node(\
ty=%s, id=%d, encl_region=%?)",
ty_to_str(tcx, ty), id, encl_region);
constrain_regions_in_type(rcx, encl_region, span, ty)
}
fn constrain_regions_in_type(
rcx: @rcx,
encl_region: ty::region,
span: span,
ty: ty::t) -> bool {
ty: ty::t) -> bool
{
/*!
*
* Requires that any regions which appear in `ty` must be
* superregions of `encl_region`. This prevents regions from
* being used outside of the block in which they are valid.
* Recall that regions represent blocks of code or expressions:
* this requirement basically says "any place that uses or may use
* a region R must be within the block of code that R corresponds
* to." */
let e = rcx.errors_reported;
ty::walk_regions_and_ty(

View File

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

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ use syntax::codemap;
use syntax::codemap::span;
use syntax::print::pprust;
use syntax::print::pprust::{path_to_str, proto_to_str,
mode_to_str, purity_to_str};
mode_to_str, purity_to_str};
use syntax::{ast, ast_util};
use syntax::ast_map;
use driver::session::session;
@ -229,10 +229,15 @@ fn proto_ty_to_str(cx: ctxt, proto: ty::fn_proto) -> ~str {
}
}
fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str {
fmt!("expr(%d: %s)",
expr.id,
pprust::expr_to_str(expr, cx.sess.intr()))
}
fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str {
let mut rs = ~"";
for ts.each |t| { rs += ty_to_str(cx, t); }
rs
let tstrs = ts.map(|t| ty_to_str(cx, t));
fmt!("[%s]", str::connect(tstrs, ", "))
}
fn bound_to_str(cx: ctxt, b: param_bound) -> ~str {

View File

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

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

View File

@ -47,7 +47,7 @@ fn c() {
// ...but not impure fns
(*q).times(3); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
(*q).times(3); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
}

View File

@ -53,7 +53,7 @@ fn c() {
(*q).purem();
// ...but not impure fns
(*q).impurem(); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
(*q).impurem(); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to access to impure function
}

View File

@ -1,6 +1,6 @@
fn main() {
let x: int = 3;
let y: &mut int = &mut x; //~ ERROR taking mut reference to immutable local variable
let y: &mut int = &mut x; //~ ERROR illegal borrow
*y = 5;
log (debug, *y);
}

View File

@ -1,7 +1,7 @@
enum foo = ~int;
fn borrow(x: @mut foo) {
let _y = &***x; //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
let _y = &***x; //~ ERROR illegal borrow unless pure
*x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
}

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

View File

@ -4,7 +4,7 @@ fn test1(x: @mut ~int) {
// Here, evaluating the second argument actually invalidates the
// first borrow, even though it occurs outside of the scope of the
// borrow!
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
pure_borrow(*x, *x = ~5); //~ ERROR illegal borrow unless pure
//~^ NOTE impure due to assigning to dereference of mutable @ pointer
}

View File

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

View File

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

View File

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

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 {
fn f(&self) {}
fn f(&const self) {}
}
fn g(x: &mut Foo) {