From 8a8f200d102294cf1bd90cdacad995abccda7934 Mon Sep 17 00:00:00 2001 From: Niko Matsakis <niko@alum.mit.edu> Date: Tue, 11 Sep 2012 21:25:01 -0700 Subject: [PATCH] Introduce auto adjustment table to subsume autoderef/autoref/borrowings. Fixes #3261 Fixes #3443 --- src/libcore/pipes.rs | 4 +- src/libcore/unsafe.rs | 7 + src/libstd/ebml.rs | 20 +- src/libstd/net_tcp.rs | 2 +- src/libstd/sync.rs | 4 +- src/libsyntax/ast.rs | 11 +- src/libsyntax/ext/auto_serialize.rs | 2 +- src/libsyntax/ext/build.rs | 10 +- src/libsyntax/parse/parser.rs | 27 +- src/libsyntax/print/pprust.rs | 32 +- src/rustc/front/test.rs | 4 +- src/rustc/metadata/common.rs | 2 +- src/rustc/metadata/tydecode.rs | 52 +- src/rustc/metadata/tyencode.rs | 9 +- src/rustc/middle/astencode.rs | 148 +- src/rustc/middle/borrowck.rs | 30 +- src/rustc/middle/borrowck/check_loans.rs | 48 +- src/rustc/middle/borrowck/gather_loans.rs | 101 +- src/rustc/middle/borrowck/loan.rs | 49 +- src/rustc/middle/borrowck/preserve.rs | 2 +- src/rustc/middle/check_const.rs | 6 +- src/rustc/middle/const_eval.rs | 12 +- src/rustc/middle/kind.rs | 18 +- src/rustc/middle/mem_categorization.rs | 157 +- src/rustc/middle/trans/alt.rs | 2 +- src/rustc/middle/trans/callee.rs | 96 +- src/rustc/middle/trans/common.rs | 14 +- src/rustc/middle/trans/consts.rs | 4 +- src/rustc/middle/trans/controlflow.rs | 8 +- src/rustc/middle/trans/datum.rs | 8 +- src/rustc/middle/trans/expr.rs | 221 ++- src/rustc/middle/trans/meth.rs | 38 +- src/rustc/middle/ty.rs | 102 +- src/rustc/middle/typeck.rs | 13 +- src/rustc/middle/typeck/check.rs | 432 ++--- src/rustc/middle/typeck/check/demand.rs | 5 +- src/rustc/middle/typeck/check/method.rs | 1484 +++++++++-------- src/rustc/middle/typeck/check/regionck.rs | 260 ++- src/rustc/middle/typeck/check/vtable.rs | 3 + src/rustc/middle/typeck/check/writeback.rs | 57 +- src/rustc/middle/typeck/infer.rs | 14 +- src/rustc/middle/typeck/infer/assignment.rs | 29 +- src/rustc/util/ppaux.rs | 13 +- src/test/compile-fail/assign-to-method.rs | 1 + .../compile-fail/borrowck-autoref-3261.rs | 19 + ...borrowck-call-method-from-mut-aliasable.rs | 31 + src/test/compile-fail/borrowck-lend-args.rs | 2 +- .../borrowck-loan-rcvr-overloaded-op.rs | 2 +- src/test/compile-fail/borrowck-loan-rcvr.rs | 2 +- .../borrowck-mut-addr-of-imm-var.rs | 2 +- .../compile-fail/borrowck-mut-deref-comp.rs | 2 +- .../borrowck-mut-slice-of-imm-vec.rs | 8 + .../borrowck-mut-vec-as-imm-slice-bad.rs | 2 +- .../borrowck-pure-scope-in-call.rs | 2 +- .../compile-fail/borrowck-uniq-via-box.rs | 27 +- .../compile-fail/borrowck-uniq-via-ref.rs | 27 +- src/test/compile-fail/issue-511.rs | 3 +- src/test/run-pass/auto-ref-newtype.rs | 14 + .../autoderef-and-borrow-method-receiver.rs | 2 +- 59 files changed, 2098 insertions(+), 1608 deletions(-) create mode 100644 src/test/compile-fail/borrowck-autoref-3261.rs create mode 100644 src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs create mode 100644 src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs create mode 100644 src/test/run-pass/auto-ref-newtype.rs diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index 1bc706d16d1..570ddfb492a 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -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)) diff --git a/src/libcore/unsafe.rs b/src/libcore/unsafe.rs index 14205f39e9f..eb3fd3974cd 100644 --- a/src/libcore/unsafe.rs +++ b/src/libcore/unsafe.rs @@ -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 diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs index 1d70bfaf00a..d6b8c2f4908 100644 --- a/src/libstd/ebml.rs +++ b/src/libstd/ebml.rs @@ -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() -> () { () } diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs index d4eb5ce6998..7622483a64b 100644 --- a/src/libstd/net_tcp.rs +++ b/src/libstd/net_tcp.rs @@ -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) { diff --git a/src/libstd/sync.rs b/src/libstd/sync.rs index 9a176013474..25e6d85d1c0 100644 --- a/src/libstd/sync.rs +++ b/src/libstd/sync.rs @@ -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 = diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3d3fd5bfd11..af7d8d83b6b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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 diff --git a/src/libsyntax/ext/auto_serialize.rs b/src/libsyntax/ext/auto_serialize.rs index 6a31dac2e8f..52357ca4752 100644 --- a/src/libsyntax/ext/auto_serialize.rs +++ b/src/libsyntax/ext/auto_serialize.rs @@ -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 { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 12fbb00e6c6..8574c0c9082 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -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, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ea824ea2fb6..b8d55fb1055 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 46fbaed8b5d..49133242d54 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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, ~"["); diff --git a/src/rustc/front/test.rs b/src/rustc/front/test.rs index 5ac62379e76..60f14f4ca63 100644 --- a/src/rustc/front/test.rs +++ b/src/rustc/front/test.rs @@ -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()}; diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs index 15e15a8a54c..a95862a978b 100644 --- a/src/rustc/metadata/common.rs +++ b/src/rustc/metadata/common.rs @@ -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. diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs index 0c94213c08c..9b723c5d9eb 100644 --- a/src/rustc/metadata/tydecode.rs +++ b/src/rustc/metadata/tydecode.rs @@ -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) } diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs index 175691613a7..2c8df582a55 100644 --- a/src/rustc/metadata/tyencode.rs +++ b/src/rustc/metadata/tyencode.rs @@ -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 { diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs index 85e4a5f2e18..3172704c971 100644 --- a/src/rustc/middle/astencode.rs +++ b/src/rustc/middle/astencode.rs @@ -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)); diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs index bc237bab315..ea5bc48445e 100644 --- a/src/rustc/middle/borrowck.rs +++ b/src/rustc/middle/borrowck.rs @@ -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 => {} diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs index e2cbb68e95a..ff7a5210007 100644 --- a/src/rustc/middle/borrowck/check_loans.rs +++ b/src/rustc/middle/borrowck/check_loans.rs @@ -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); } diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs index 18e27e79fd1..449913e92b7 100644 --- a/src/rustc/middle/borrowck/gather_loans.rs +++ b/src/rustc/middle/borrowck/gather_loans.rs @@ -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) } - } } } diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs index 353022214c5..8d9d7a5796a 100644 --- a/src/rustc/middle/borrowck/loan.rs +++ b/src/rustc/middle/borrowck/loan.rs @@ -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) } } } diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs index d1c7e36a27a..6bfb0d091a4 100644 --- a/src/rustc/middle/borrowck/preserve.rs +++ b/src/rustc/middle/borrowck/preserve.rs @@ -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) } } diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs index d7cbd2969a3..0a1de0d11a4 100644 --- a/src/rustc/middle/check_const.rs +++ b/src/rustc/middle/check_const.rs @@ -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(*) | diff --git a/src/rustc/middle/const_eval.rs b/src/rustc/middle/const_eval.rs index 41bfe9ae026..6c9617d3310 100644 --- a/src/rustc/middle/const_eval.rs +++ b/src/rustc/middle/const_eval.rs @@ -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) | diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index 4e1d67ea365..e81c8f967e0 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -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) { diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs index c97431eed23..e6d27a7d09e 100644 --- a/src/rustc/middle/mem_categorization.rs +++ b/src/rustc/middle/mem_categorization.rs @@ -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*/ } diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index 421d7923234..725848592b1 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -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() } }; diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs index 3f1fc9aa3d5..4c33bd2b432 100644 --- a/src/rustc/middle/trans/callee.rs +++ b/src/rustc/middle/trans/callee.rs @@ -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) - } -} - diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index b7b84367410..87e27bcf6d7 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -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 { diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs index 24fd916823a..68d87288326 100644 --- a/src/rustc/middle/trans/consts.rs +++ b/src/rustc/middle/trans/consts.rs @@ -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 { diff --git a/src/rustc/middle/trans/controlflow.rs b/src/rustc/middle/trans/controlflow.rs index 83c5eb5f5f3..7e81b066693 100644 --- a/src/rustc/middle/trans/controlflow.rs +++ b/src/rustc/middle/trans/controlflow.rs @@ -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| { diff --git a/src/rustc/middle/trans/datum.rs b/src/rustc/middle/trans/datum.rs index 31db9c96bea..791cebad641 100644 --- a/src/rustc/middle/trans/datum.rs +++ b/src/rustc/middle/trans/datum.rs @@ -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); diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs index 96a30f62240..303e10ce818 100644 --- a/src/rustc/middle/trans/expr.rs +++ b/src/rustc/middle/trans/expr.rs @@ -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); diff --git a/src/rustc/middle/trans/meth.rs b/src/rustc/middle/trans/meth.rs index b923e35765a..a87ae02f8d2 100644 --- a/src/rustc/middle/trans/meth.rs +++ b/src/rustc/middle/trans/meth.rs @@ -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) diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 2ad5412e758..2edf0178e32 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -19,7 +19,9 @@ use syntax::ast::*; use syntax::print::pprust::*; use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str}; use std::serialization::{serialize_Option, - deserialize_Option}; + deserialize_Option, + serialize_uint, + deserialize_uint}; export TyVid, IntVid, FnVid, RegionVid, vid; export br_hashmap; @@ -155,6 +157,7 @@ export closure_kind; export ck_block; export ck_box; export ck_uniq; +export param_ty; export param_bound, param_bounds, bound_copy, bound_owned; export param_bounds_to_str, param_bound_to_str; export bound_send, bound_trait; @@ -192,6 +195,8 @@ export opt_region_variance; export serialize_opt_region_variance, deserialize_opt_region_variance; export determine_inherited_purity; export provided_trait_methods; +export AutoAdjustment, serialize_AutoAdjustment, deserialize_AutoAdjustment; +export AutoRef, AutoRefKind, AutoSlice, AutoPtr; // Data types @@ -287,19 +292,26 @@ impl region_variance: cmp::Eq { pure fn ne(&&other: region_variance) -> bool { !self.eq(other) } } -// N.B.: Borrows from inlined content are not accurately deserialized. This -// is because we don't need the details in trans, we only care if there is an -// entry in the table or not. -type borrow = { - region: ty::region, +#[auto_serialize] +type AutoAdjustment = { + autoderefs: uint, + autoref: Option<AutoRef> +}; + +#[auto_serialize] +type AutoRef = { + kind: AutoRefKind, + region: region, mutbl: ast::mutability }; -impl borrow : cmp::Eq { - pure fn eq(&&other: borrow) -> bool { - self.region == other.region && self.mutbl == other.mutbl - } - pure fn ne(&&other: borrow) -> bool { !self.eq(other) } +#[auto_serialize] +enum AutoRefKind { + /// Convert from @[]/~[] to &[] (or str) + AutoSlice, + + /// Convert from T to &T + AutoPtr } type ctxt = @@ -340,8 +352,7 @@ type ctxt = trait_method_cache: HashMap<def_id, @~[method]>, ty_param_bounds: HashMap<ast::node_id, param_bounds>, inferred_modes: HashMap<ast::node_id, ast::mode>, - // maps the id of borrowed expr to scope of borrowed ptr - borrowings: HashMap<ast::node_id, borrow>, + adjustments: HashMap<ast::node_id, @AutoAdjustment>, normalized_cache: HashMap<t, t>, lang_items: middle::lang_items::LanguageItems}; @@ -496,6 +507,7 @@ impl param_ty : to_bytes::IterBytes { /// Representation of regions: +#[auto_serialize] enum region { /// Bound regions are found (primarily) in function types. They indicate /// region parameters that have yet to be replaced with actual regions @@ -523,6 +535,7 @@ enum region { re_var(RegionVid) } +#[auto_serialize] enum bound_region { /// The self region for classes, impls (&T in a type defn or &self/T) br_self, @@ -533,35 +546,37 @@ enum bound_region { /// Named region parameters for functions (a in &a/T) br_named(ast::ident), - /// Handles capture-avoiding substitution in a rather subtle case. If you - /// have a closure whose argument types are being inferred based on the - /// expected type, and the expected type includes bound regions, then we - /// will wrap those bound regions in a br_cap_avoid() with the id of the - /// fn expression. This ensures that the names are not "captured" by the - /// enclosing scope, which may define the same names. For an example of - /// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs - /// and regions-ret-borrowed-1.rs. + /** + * Handles capture-avoiding substitution in a rather subtle case. If you + * have a closure whose argument types are being inferred based on the + * expected type, and the expected type includes bound regions, then we + * will wrap those bound regions in a br_cap_avoid() with the id of the + * fn expression. This ensures that the names are not "captured" by the + * enclosing scope, which may define the same names. For an example of + * where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs + * and regions-ret-borrowed-1.rs. */ br_cap_avoid(ast::node_id, @bound_region), } type opt_region = Option<region>; -/// The type substs represents the kinds of things that can be substituted to -/// convert a polytype into a monotype. Note however that substituting bound -/// regions other than `self` is done through a different mechanism. -/// -/// `tps` represents the type parameters in scope. They are indexed according -/// to the order in which they were declared. -/// -/// `self_r` indicates the region parameter `self` that is present on nominal -/// types (enums, classes) declared as having a region parameter. `self_r` -/// should always be none for types that are not region-parameterized and -/// Some(_) for types that are. The only bound region parameter that should -/// appear within a region-parameterized type is `self`. -/// -/// `self_ty` is the type to which `self` should be remapped, if any. The -/// `self` type is rather funny in that it can only appear on traits and -/// is always substituted away to the implementing type for a trait. +/** + * The type substs represents the kinds of things that can be substituted to + * convert a polytype into a monotype. Note however that substituting bound + * regions other than `self` is done through a different mechanism: + * + * - `tps` represents the type parameters in scope. They are indexed + * according to the order in which they were declared. + * + * - `self_r` indicates the region parameter `self` that is present on nominal + * types (enums, classes) declared as having a region parameter. `self_r` + * should always be none for types that are not region-parameterized and + * Some(_) for types that are. The only bound region parameter that should + * appear within a region-parameterized type is `self`. + * + * - `self_ty` is the type to which `self` should be remapped, if any. The + * `self` type is rather funny in that it can only appear on traits and is + * always substituted away to the implementing type for a trait. */ type substs = { self_r: opt_region, self_ty: Option<ty::t>, @@ -650,6 +665,7 @@ enum param_bound { enum TyVid = uint; enum IntVid = uint; enum FnVid = uint; +#[auto_serialize] enum RegionVid = uint; enum InferTy { @@ -842,7 +858,7 @@ fn mk_ctxt(s: session::session, trait_method_cache: new_def_hash(), ty_param_bounds: map::int_hash(), inferred_modes: map::int_hash(), - borrowings: map::int_hash(), + adjustments: map::int_hash(), normalized_cache: new_ty_hash(), lang_items: move lang_items} } @@ -1339,7 +1355,7 @@ fn substs_to_str(cx: ctxt, substs: &substs) -> ~str { fmt!("substs(self_r=%s, self_ty=%s, tps=%?)", substs.self_r.map_default(~"none", |r| region_to_str(cx, r)), substs.self_ty.map_default(~"none", |t| ty_to_str(cx, t)), - substs.tps.map(|t| ty_to_str(cx, t))) + tys_to_str(cx, substs.tps)) } fn param_bound_to_str(cx: ctxt, pb: ¶m_bound) -> ~str { @@ -2939,8 +2955,8 @@ fn expr_kind(tcx: ctxt, ast::expr_unary_move(*) | ast::expr_repeat(*) | ast::expr_lit(@{node: lit_str(_), _}) | - ast::expr_vstore(_, ast::vstore_slice(_)) | - ast::expr_vstore(_, ast::vstore_fixed(_)) | + ast::expr_vstore(_, ast::expr_vstore_slice) | + ast::expr_vstore(_, ast::expr_vstore_fixed(_)) | ast::expr_vec(*) => { RvalueDpsExpr } @@ -2990,8 +3006,8 @@ fn expr_kind(tcx: ctxt, ast::expr_unary(*) | ast::expr_addr_of(*) | ast::expr_binary(*) | - ast::expr_vstore(_, ast::vstore_box) | - ast::expr_vstore(_, ast::vstore_uniq) => { + ast::expr_vstore(_, ast::expr_vstore_box) | + ast::expr_vstore(_, ast::expr_vstore_uniq) => { RvalueDatumExpr } diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 439c0052259..4b146d89cf8 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -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 diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index de803402a13..9af764511d3 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -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) } diff --git a/src/rustc/middle/typeck/check/demand.rs b/src/rustc/middle/typeck/check/demand.rs index 8575bc6906a..3ae59910fe4 100644 --- a/src/rustc/middle/typeck/check/demand.rs +++ b/src/rustc/middle/typeck/check/demand.rs @@ -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); diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index 05c182a81bf..ff90f671d75 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -1,39 +1,813 @@ -/* Code to handle method lookups (which can be quite complex) */ +/*! + +# Method lookup + +Method lookup can be rather complex due to the interaction of a number +of factors, such as self types, autoderef, trait lookup, etc. The +algorithm is divided into two parts: candidate collection and +candidate selection. + +## Candidate collection + +A `Candidate` is a method item that might plausibly be the method +being invoked. Candidates are grouped into two kinds, inherent and +extension. Inherent candidates are those that are derived from the +type of the receiver itself. So, if you have a receiver of some +nominal type `Foo` (e.g., a struct), any methods defined within an +impl like `impl Foo` are inherent methods. Nothing needs to be +imported to use an inherent method, they are associated with the type +itself (note that inherent impls can only be defined in the same +module as the type itself). + +Inherent candidates are not always derived from impls. If you have a +trait instance, such as a value of type `ToStr`, then the trait +methods (`to_str()`, in this case) are inherently associated with it. +Another case is type parameters, in which case the methods of their +bounds are inherent. + +Extension candidates are derived from imported traits. If I have the +trait `ToStr` imported, and I call `to_str()` on a value of type `T`, +then we will go off to find out whether there is an impl of `ToStr` +for `T`. These kinds of method calls are called "extension methods". +They can be defined in any module, not only the one that defined `T`. +Furthermore, you must import the trait to call such a method. + +For better or worse, we currently give weight to inherent methods over +extension methods during candidate selection (below). + +## Candidate selection + +Once we know the set of candidates, we can go off and try to select +which one is actually being called. We do this by taking the type of +the receiver, let's call it R, and checking whether it matches against +the expected receiver type for each of the collected candidates. We +first check for inherent candidates and see whether we get exactly one +match (zero means keep searching, more than one is an error). If so, +we return that as the candidate. Otherwise we search the extension +candidates in the same way. + +If find no matching candidate at all, we proceed to auto-deref the +receiver type and search again. We keep doing that until we cannot +auto-deref any longer. At that point, we will attempt an auto-ref. +If THAT fails, method lookup fails altogether. + +## Why two phases? + +You might wonder why we first collect the candidates and then select. +Both the inherent candidate collection and the candidate selection +proceed by progressively deref'ing the receiver type, after all. The +answer is that two phases are needed to elegantly deal with explicit +self. After all, if there is an impl for the type `Foo`, it can +define a method with the type `@self`, which means that it expects a +receiver of type `@Foo`. If we have a receiver of type `@Foo`, but we +waited to search for that impl until we have deref'd the `@` away and +obtained the type `Foo`, we would never match this method. + +*/ use coherence::get_base_type_def_id; use middle::resolve::{Impl, MethodInfo}; -use middle::ty::{mk_box, mk_rptr, mk_uniq, FnTyBase, FnMeta, FnSig}; -use syntax::ast::{def_id, - sty_static, sty_box, sty_by_ref, sty_region, sty_uniq}; -use syntax::ast::{sty_value, by_ref, by_copy}; +use middle::ty::*; +use syntax::ast::{def_id, sty_by_ref, sty_value, sty_region, sty_box, + sty_uniq, sty_static, node_id, by_copy, by_ref, + m_const, m_mutbl, m_imm}; use syntax::ast_map; use syntax::ast_map::node_id_to_str; use syntax::ast_util::{dummy_sp, new_def_hash}; use dvec::DVec; -enum method_lookup_mode { - subtyping_mode, - assignability_mode, - immutable_reference_mode, - mutable_reference_mode +fn lookup( + fcx: @fn_ctxt, + + // In a call `a.b::<X, Y, ...>(...)`: + expr: @ast::expr, // The expression `a.b`. + self_expr: @ast::expr, // The expression `a`. + callee_id: node_id, // Where to store the type of `a.b` + m_name: ast::ident, // The ident `b`. + self_ty: ty::t, // The type of `a`. + supplied_tps: &[ty::t]) // The list of types X, Y, ... . + -> Option<method_map_entry> +{ + let lcx = LookupContext { + fcx: fcx, + expr: expr, + self_expr: self_expr, + callee_id: callee_id, + m_name: m_name, + supplied_tps: supplied_tps, + impl_dups: new_def_hash(), + inherent_candidates: DVec(), + extension_candidates: DVec() + }; + return lcx.do_lookup(self_ty); } -type candidate = { - self_ty: ty::t, // type of a in a.b() - self_substs: ty::substs, // values for any tvars def'd on the class - rcvr_ty: ty::t, // type of receiver in the method def - n_tps_m: uint, // number of tvars defined on the method - fty: ty::t, // type of the method - entry: method_map_entry, - mode: method_lookup_mode // the mode we used -}; +struct LookupContext { + fcx: @fn_ctxt, + expr: @ast::expr, + self_expr: @ast::expr, + callee_id: node_id, + m_name: ast::ident, + supplied_tps: &[ty::t], + impl_dups: HashMap<def_id, ()>, + inherent_candidates: DVec<Candidate>, + extension_candidates: DVec<Candidate> +} -fn transform_self_type_for_method - (tcx: ty::ctxt, - self_region: Option<ty::region>, - impl_ty: ty::t, - self_type: ast::self_ty_) - -> ty::t { +/** + * A potential method that might be called, assuming the receiver + * is of a suitable type. */ +struct Candidate { + rcvr_ty: ty::t, + rcvr_substs: ty::substs, + + // FIXME #3446---these two fields should be easily derived from + // origin, yet are not + num_method_tps: uint, + self_mode: ast::rmode, + + origin: method_origin, +} + +impl LookupContext { + fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> { + debug!("do_lookup(self_ty=%s, expr=%s, self_expr=%s)", + self.ty_to_str(self_ty), + expr_repr(self.tcx(), self.expr), + expr_repr(self.tcx(), self.self_expr)); + let _indenter = indenter(); + + // Prepare the list of candidates + self.push_inherent_candidates(self_ty); + self.push_extension_candidates(); + + let enum_dids = DVec(); + let mut self_ty = self_ty; + let mut autoderefs = 0; + loop { + debug!("loop: self_ty=%s autoderefs=%u", + self.ty_to_str(self_ty), autoderefs); + + match self.search_for_autoderefd_method(self_ty, autoderefs) { + Some(move mme) => { return Some(mme); } + None => {} + } + + // some special logic around newtypes: + match ty::get(self_ty).sty { + ty_enum(*) => { + // Note: in general, we prefer not to auto-ref a + // partially autoderef'd type, because it + // seems... crazy. But we have to be careful + // around newtype enums. They can be further + // deref'd, but they may also have intrinsic + // methods hanging off of them with interior type. + match self.search_for_appr_autorefd_method(self_ty, + autoderefs) { + Some(move mme) => { return Some(mme); } + None => {} + } + } + _ => {} + } + + match self.deref(self_ty, &enum_dids) { + None => { break; } + Some(ty) => { + self_ty = ty; + autoderefs += 1; + } + } + } + + self.search_for_appr_autorefd_method(self_ty, autoderefs) + } + + fn deref(ty: ty::t, enum_dids: &DVec<ast::def_id>) -> Option<ty::t> { + match ty::get(ty).sty { + ty_enum(did, _) => { + // Watch out for newtype'd enums like "enum t = @T". + // See discussion in typeck::check::do_autoderef(). + if enum_dids.contains(did) { + return None; + } + enum_dids.push(did); + } + _ => {} + } + + match ty::deref(self.tcx(), ty, false) { + None => None, + Some(t) => { + //FIXME(#3211) -- probably want to force ivars + Some(structurally_resolved_type(self.fcx, + self.self_expr.span, + t.ty)) + } + } + } + + // ______________________________________________________________________ + // Candidate collection (see comment at start of file) + + fn push_inherent_candidates(&self, self_ty: ty::t) { + /*! + * + * Collect all inherent candidates into + * `self.inherent_candidates`. See comment at the start of + * the file. To find the inherent candidates, we repeatedly + * deref the self-ty to find the "base-type". So, for + * example, if the receiver is @@C where `C` is a struct type, + * we'll want to find the inherent impls for `C`. */ + + let enum_dids = DVec(); + let mut self_ty = self_ty; + loop { + match get(self_ty).sty { + ty_param(p) => { + self.push_inherent_candidates_from_param(p); + } + ty_trait(did, ref substs, _) => { + self.push_inherent_candidates_from_trait( + self_ty, did, substs); + self.push_inherent_impl_candidates_for_type(did); + } + ty_self => { + // Call is of the form "self.foo()" and appears in one + // of a trait's default method implementations. + let self_did = self.fcx.self_impl_def_id.expect( + ~"unexpected `none` for self_impl_def_id"); + let substs = {self_r: None, self_ty: None, tps: ~[]}; + self.push_inherent_candidates_from_trait( + self_ty, self_did, &substs); + } + ty_enum(did, _) | ty_class(did, _) => { + self.push_inherent_impl_candidates_for_type(did); + } + _ => { /* No inherent methods in these types */ } + } + + // n.b.: Generally speaking, we only loop if we hit the + // fallthrough case in the match above. The exception + // would be newtype enums. + self_ty = match self.deref(self_ty, &enum_dids) { + None => { return; } + Some(ty) => { ty } + } + } + } + + fn push_extension_candidates(&self) { + // If the method being called is associated with a trait, then + // find all the impls of that trait. Each of those are + // candidates. + let opt_applicable_traits = self.fcx.ccx.trait_map.find(self.expr.id); + for opt_applicable_traits.each |applicable_traits| { + for applicable_traits.each |trait_did| { + let coherence_info = self.fcx.ccx.coherence_info; + let opt_impl_infos = + coherence_info.extension_methods.find(trait_did); + for opt_impl_infos.each |impl_infos| { + for impl_infos.each |impl_info| { + self.push_candidates_from_impl( + &self.extension_candidates, impl_info); + } + } + } + } + } + + fn push_inherent_candidates_from_param(&self, param_ty: param_ty) + { + debug!("push_inherent_candidates_from_param(param_ty=%?)", + param_ty); + let _indenter = indenter(); + + let tcx = self.tcx(); + let mut next_bound_idx = 0; // count only trait bounds + let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node); + for vec::each(*bounds) |bound| { + let bound_t = match bound { + ty::bound_trait(bound_t) => bound_t, + + ty::bound_copy | ty::bound_send | + ty::bound_const | ty::bound_owned => { + loop; // skip non-trait bounds + } + }; + + let this_bound_idx = next_bound_idx; + next_bound_idx += 1; + + let (trait_id, bound_substs) = match ty::get(bound_t).sty { + ty::ty_trait(i, substs, _) => (i, substs), + _ => { + self.bug(fmt!("add_candidates_from_param: \ + non-trait bound %s", + self.ty_to_str(bound_t))); + } + }; + + let trait_methods = ty::trait_methods(tcx, trait_id); + let pos = { + // FIXME #3453 can't use trait_methods.position + match vec::position(*trait_methods, + |m| (m.self_ty != ast::sty_static && + m.ident == self.m_name)) + { + Some(pos) => pos, + None => { + loop; // check next bound + } + } + }; + let method = &trait_methods[pos]; + + // Replace any appearance of `self` with the type of the + // generic parameter itself. Note that this is the only + // case where this replacement is necessary: in all other + // cases, we are either invoking a method directly from an + // impl or class (where the self type is not permitted), + // or from a trait type (in which case methods that refer + // to self are not permitted). + let rcvr_ty = ty::mk_param(tcx, param_ty.idx, param_ty.def_id); + let rcvr_substs = {self_ty: Some(rcvr_ty), ..bound_substs}; + + self.inherent_candidates.push(Candidate { + rcvr_ty: rcvr_ty, + rcvr_substs: rcvr_substs, + num_method_tps: method.tps.len(), + self_mode: get_mode_from_self_type(method.self_ty), + origin: method_param({trait_id:trait_id, + method_num:pos, + param_num:param_ty.idx, + bound_num:this_bound_idx}) + }); + } + } + + fn push_inherent_candidates_from_trait(&self, + self_ty: ty::t, + did: def_id, + substs: &ty::substs) + { + debug!("push_inherent_candidates_from_trait(did=%s, substs=%s)", + self.did_to_str(did), + substs_to_str(self.tcx(), substs)); + let _indenter = indenter(); + + let tcx = self.tcx(); + let ms = ty::trait_methods(tcx, did); + let index = match vec::position(*ms, |m| m.ident == self.m_name) { + Some(i) => i, + None => { return; } // no method with the right name + }; + let method = &ms[index]; + + /* FIXME(#3468) we should transform the vstore in accordance + with the self type + + match method.self_type { + ast::sty_region(_) => { + return; // inapplicable + } + ast::sty_by_ref | ast::sty_region(_) => vstore_slice(r) + ast::sty_box(_) => vstore_box, // XXX NDM mutability + ast::sty_uniq(_) => vstore_uniq + } + */ + + // It is illegal to invoke a method on a trait instance that + // refers to the `self` type. Nonetheless, we substitute + // `trait_ty` for `self` here, because it allows the compiler + // to soldier on. An error will be reported should this + // candidate be selected if the method refers to `self`. + let rcvr_substs = {self_ty: Some(self_ty), ..*substs}; + + self.inherent_candidates.push(Candidate { + rcvr_ty: self_ty, + rcvr_substs: move rcvr_substs, + num_method_tps: method.tps.len(), + self_mode: get_mode_from_self_type(method.self_ty), + origin: method_trait(did, index) + }); + } + + fn push_inherent_impl_candidates_for_type(did: def_id) + { + let opt_impl_infos = + self.fcx.ccx.coherence_info.inherent_methods.find(did); + for opt_impl_infos.each |impl_infos| { + for impl_infos.each |impl_info| { + self.push_candidates_from_impl( + &self.inherent_candidates, impl_info); + } + } + } + + fn push_candidates_from_impl(&self, candidates: &DVec<Candidate>, + impl_info: &resolve::Impl) + { + if !self.impl_dups.insert(impl_info.did, ()) { + return; // already visited + } + + let idx = { + // FIXME #3453 can't use impl_info.methods.position + match vec::position(impl_info.methods, + |m| m.ident == self.m_name) { + Some(idx) => idx, + None => { return; } // No method with the right name. + } + }; + + let tcx = self.tcx(); + let method = &impl_info.methods[idx]; + + let need_rp = match method.self_type { + ast::sty_region(_) => true, + _ => false + }; + + // determine the `self` of the impl with fresh + // variables for each parameter: + let {substs: impl_substs, ty: impl_ty} = + impl_self_ty(self.fcx, self.self_expr, impl_info.did, need_rp); + + let impl_ty = transform_self_type_for_method( + tcx, impl_substs.self_r, impl_ty, method.self_type); + + candidates.push(Candidate { + rcvr_ty: impl_ty, + rcvr_substs: move impl_substs, + num_method_tps: method.n_tps, + self_mode: get_mode_from_self_type(method.self_type), + origin: method_static(method.did) + }); + } + + // ______________________________________________________________________ + // Candidate selection (see comment at start of file) + + fn search_for_autoderefd_method( + &self, + self_ty: ty::t, + autoderefs: uint) + -> Option<method_map_entry> + { + match self.search_for_method(self_ty) { + None => None, + Some(move mme) => { + self.fcx.write_autoderef_adjustment( + self.self_expr.id, autoderefs); + Some(mme) + } + } + } + + fn search_for_appr_autorefd_method( + &self, + self_ty: ty::t, + autoderefs: uint) + -> Option<method_map_entry> + { + let tcx = self.tcx(); + + // Next, try auto-ref. The precise kind of auto-ref depends on + // the fully deref'd receiver type. In particular, we must + // treat dynamically sized types like `str`, `[]` or `fn` + // differently than other types because they cannot be fully + // deref'd, unlike say @T. + match ty::get(self_ty).sty { + ty_box(*) | ty_uniq(*) | ty_rptr(*) => { + // we should be fully autoderef'd + self.bug(fmt!("Receiver type %s should be fully \ + autoderef'd by this point", + self.ty_to_str(self_ty))); + } + + ty_infer(IntVar(_)) | // FIXME(#3211)---should be resolved + ty_self | ty_param(*) | ty_nil | ty_bot | ty_bool | + ty_int(*) | ty_uint(*) | + ty_float(*) | ty_enum(*) | ty_ptr(*) | ty_rec(*) | + ty_class(*) | ty_tup(*) => { + return self.search_for_autorefd_method( + AutoPtr, autoderefs, [m_const, m_imm, m_mutbl], + |m,r| ty::mk_rptr(tcx, r, {ty:self_ty, mutbl:m})); + } + + ty_trait(*) | ty_fn(*) => { + // NDM---eventually these should be some variant of autoref + return None; + } + + ty_estr(vstore_slice(_)) | + ty_evec(_, vstore_slice(_)) => { + return None; + } + + ty_evec(mt, vstore_box) | + ty_evec(mt, vstore_uniq) | + ty_evec(mt, vstore_fixed(_)) => { + return self.search_for_autorefd_method( + AutoSlice, autoderefs, [m_const, m_imm, m_mutbl], + |m,r| ty::mk_evec(tcx, + {ty:mt.ty, mutbl:m}, + vstore_slice(r))); + } + + ty_estr(vstore_box) | + ty_estr(vstore_uniq) | + ty_estr(vstore_fixed(_)) => { + return self.search_for_autorefd_method( + AutoSlice, autoderefs, [m_imm], + |_m,r| ty::mk_estr(tcx, vstore_slice(r))); + } + + ty_opaque_closure_ptr(_) | ty_unboxed_vec(_) | + ty_opaque_box | ty_type | ty_infer(TyVar(_)) => { + self.bug(fmt!("Unexpected type: %s", + self.ty_to_str(self_ty))); + } + } + } + + fn search_for_autorefd_method( + &self, + kind: AutoRefKind, + autoderefs: uint, + mutbls: &[ast::mutability], + mk_autoref_ty: &fn(ast::mutability, ty::region) -> ty::t) + -> Option<method_map_entry> + { + // This is hokey. We should have mutability inference as a + // variable. But for now, try &const, then &, then &mut: + let region = self.infcx().next_region_var(self.expr.span, + self.expr.id); + for mutbls.each |mutbl| { + let autoref_ty = mk_autoref_ty(mutbl, region); + match self.search_for_method(autoref_ty) { + None => {} + Some(move mme) => { + self.fcx.write_adjustment( + self.self_expr.id, + @{autoderefs: autoderefs, + autoref: Some({kind: kind, + region: region, + mutbl: mutbl})}); + return Some(mme); + } + } + } + return None; + } + + fn search_for_method(&self, + self_ty: ty::t) + -> Option<method_map_entry> + { + debug!("search_for_method(self_ty=%s)", self.ty_to_str(self_ty)); + let _indenter = indenter(); + + // I am not sure that inherent methods should have higher + // priority, but it is necessary ATM to handle some of the + // existing code. + + debug!("searching inherent candidates"); + match self.consider_candidates(self_ty, &self.inherent_candidates) { + None => {} + Some(move mme) => { + return Some(move mme); + } + } + + debug!("searching extension candidates"); + match self.consider_candidates(self_ty, &self.extension_candidates) { + None => { + return None; + } + Some(move mme) => { + return Some(move mme); + } + } + } + + fn consider_candidates(&self, + self_ty: ty::t, + candidates: &DVec<Candidate>) + -> Option<method_map_entry> + { + let relevant_candidates = + candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c)); + + if relevant_candidates.len() == 0 { + return None; + } + + if relevant_candidates.len() > 1 { + self.tcx().sess.span_err( + self.expr.span, + ~"multiple applicable methods in scope"); + for uint::range(0, relevant_candidates.len()) |idx| { + self.report_candidate(idx, &relevant_candidates[idx].origin); + } + } + + Some(self.confirm_candidate(self_ty, &relevant_candidates[0])) + } + + fn confirm_candidate(&self, + self_ty: ty::t, + candidate: &Candidate) + -> method_map_entry + { + let tcx = self.tcx(); + let fty = self.fn_ty_from_origin(&candidate.origin); + + self.enforce_trait_instance_limitations(fty, candidate); + + // before we only checked whether self_ty could be a subtype + // of rcvr_ty; now we actually make it so (this may cause + // variables to unify etc). Since we checked beforehand, and + // nothing has changed in the meantime, this unification + // should never fail. + match self.fcx.mk_subty(false, self.self_expr.span, + self_ty, candidate.rcvr_ty) { + result::Ok(_) => (), + result::Err(_) => { + self.bug(fmt!("%s was assignable to %s but now is not?", + self.ty_to_str(self_ty), + self.ty_to_str(candidate.rcvr_ty))); + } + } + + // Determine the values for the type parameters of the method. + // If they were not explicitly supplied, just construct fresh + // type variables. + let num_supplied_tps = self.supplied_tps.len(); + let m_substs = { + if num_supplied_tps == 0u { + self.fcx.infcx().next_ty_vars(candidate.num_method_tps) + } else if candidate.num_method_tps == 0u { + tcx.sess.span_err( + self.expr.span, + ~"this method does not take type parameters"); + self.fcx.infcx().next_ty_vars(candidate.num_method_tps) + } else if num_supplied_tps != candidate.num_method_tps { + tcx.sess.span_err( + self.expr.span, + ~"incorrect number of type \ + parameters given for this method"); + self.fcx.infcx().next_ty_vars(candidate.num_method_tps) + } else { + self.supplied_tps.to_vec() + } + }; + + // Construct the full set of type parameters for the method, + // which is equal to the class tps + the method tps. + let all_substs = {tps: vec::append(candidate.rcvr_substs.tps, + m_substs), + ..candidate.rcvr_substs}; + + self.fcx.write_ty_substs(self.callee_id, fty, all_substs); + return {self_arg: {mode: ast::expl(candidate.self_mode), + ty: candidate.rcvr_ty}, + origin: candidate.origin}; + } + + fn enforce_trait_instance_limitations(&self, + method_fty: ty::t, + candidate: &Candidate) + { + /*! + * + * There are some limitations to calling functions through a + * traint instance, because (a) the self type is not known + * (that's the whole point of a trait instance, after all, to + * obscure the self type) and (b) the call must go through a + * vtable and hence cannot be monomorphized. */ + + match candidate.origin { + method_static(*) | method_param(*) => { + return; // not a call to a trait instance + } + method_trait(*) => {} + } + + if ty::type_has_self(method_fty) { + self.tcx().sess.span_err( + self.expr.span, + ~"cannot call a method whose type contains a \ + self-type through a boxed trait"); + } + + if candidate.num_method_tps > 0 { + self.tcx().sess.span_err( + self.expr.span, + ~"cannot call a generic method through a boxed trait"); + } + } + + fn is_relevant(&self, self_ty: ty::t, candidate: &Candidate) -> bool { + self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok() + } + + fn fn_ty_from_origin(&self, origin: &method_origin) -> ty::t { + return match *origin { + method_static(did) => { + ty::lookup_item_type(self.tcx(), did).ty + } + method_param(ref mp) => { + type_of_trait_method(self.tcx(), mp.trait_id, mp.method_num) + } + method_trait(did, idx) => { + type_of_trait_method(self.tcx(), did, idx) + } + }; + + fn type_of_trait_method(tcx: ty::ctxt, + trait_did: def_id, + method_num: uint) -> ty::t { + let trait_methods = ty::trait_methods(tcx, trait_did); + ty::mk_fn(tcx, trait_methods[method_num].fty) + } + } + + fn report_candidate(idx: uint, origin: &method_origin) { + match *origin { + method_static(impl_did) => { + self.report_static_candidate(idx, impl_did) + } + method_param(mp) => { + self.report_param_candidate(idx, mp.trait_id) + } + method_trait(trait_did, _) => { + self.report_param_candidate(idx, trait_did) + } + } + } + + fn report_static_candidate(idx: uint, did: def_id) { + let span = if did.crate == ast::local_crate { + match self.tcx().items.get(did.node) { + ast_map::node_method(m, _, _) => m.span, + _ => fail ~"report_static_candidate: bad item" + } + } else { + self.expr.span + }; + self.tcx().sess.span_note( + span, + fmt!("candidate #%u is `%s`", + (idx+1u), + ty::item_path_str(self.tcx(), did))); + } + + fn report_param_candidate(idx: uint, did: def_id) { + self.tcx().sess.span_note( + self.expr.span, + fmt!("candidate #%u derives from the bound `%s`", + (idx+1u), + ty::item_path_str(self.tcx(), did))); + } + + fn report_trait_candidate(idx: uint, did: def_id) { + self.tcx().sess.span_note( + self.expr.span, + fmt!("candidate #%u derives from the type of the receiver, \ + which is the trait `%s`", + (idx+1u), + ty::item_path_str(self.tcx(), did))); + } + + fn infcx() -> infer::infer_ctxt { + self.fcx.inh.infcx + } + + fn tcx() -> ty::ctxt { + self.fcx.tcx() + } + + fn ty_to_str(t: ty::t) -> ~str { + self.fcx.infcx().ty_to_str(t) + } + + fn did_to_str(did: def_id) -> ~str { + ty::item_path_str(self.tcx(), did) + } + + fn bug(s: ~str) -> ! { + self.tcx().sess.bug(s) + } +} + +fn transform_self_type_for_method(tcx: ty::ctxt, + self_region: Option<ty::region>, + impl_ty: ty::t, + self_type: ast::self_ty_) + -> ty::t +{ match self_type { sty_static => { tcx.sess.bug(~"calling transform_self_type_for_method on \ @@ -57,663 +831,5 @@ fn transform_self_type_for_method } fn get_mode_from_self_type(self_type: ast::self_ty_) -> ast::rmode { - match self_type { - sty_value => by_copy, - _ => by_ref - } + match self_type { sty_value => by_copy, _ => by_ref } } - -fn lookup(fcx: @fn_ctxt, - - // In a call `a.b::<X, Y, ...>(...)`: - expr: @ast::expr, // The expression `a.b`. - self_expr: @ast::expr, // The expression `a`. - borrow_lb: ast::node_id, // Scope to borrow the expression `a` for. - node_id: ast::node_id, // The node_id in which to store the type of - // `a.b`. - m_name: ast::ident, // The ident `b`. - self_ty: ty::t, // The type of `a`. - supplied_tps: ~[ty::t], // The list of types X, Y, ... . - include_private: bool) -> lookup { - - lookup { - fcx: fcx, - expr: expr, - self_expr: self_expr, - borrow_lb: borrow_lb, - node_id: node_id, - m_name: m_name, - self_ty: self_ty, - derefs: 0u, - candidates: DVec(), - candidate_impls: new_def_hash(), - supplied_tps: supplied_tps, - include_private: include_private - } -} - -struct lookup { - fcx: @fn_ctxt, - expr: @ast::expr, - self_expr: @ast::expr, - borrow_lb: ast::node_id, - node_id: ast::node_id, - m_name: ast::ident, - mut self_ty: ty::t, - mut derefs: uint, - candidates: DVec<candidate>, - candidate_impls: HashMap<def_id, ()>, - supplied_tps: ~[ty::t], - include_private: bool, -} - -impl lookup { - - // Entrypoint: - fn method() -> Option<method_map_entry> { - debug!("method lookup(m_name=%s, self_ty=%s, %?)", - self.fcx.tcx().sess.str_of(self.m_name), - self.fcx.infcx().ty_to_str(self.self_ty), - ty::get(self.self_ty).sty); - - // Determine if there are any inherent methods we can call. - // (An inherent method is one that belongs to no trait, but is - // inherent to a class or impl.) - let optional_inherent_methods; - match get_base_type_def_id(self.fcx.infcx(), - self.self_expr.span, - self.self_ty) { - None => { - optional_inherent_methods = None; - } - Some(base_type_def_id) => { - debug!("(checking method) found base type"); - optional_inherent_methods = - self.fcx.ccx.coherence_info.inherent_methods.find - (base_type_def_id); - - if optional_inherent_methods.is_none() { - debug!("(checking method) ... no inherent methods found"); - } else { - debug!("(checking method) ... inherent methods found"); - } - } - } - - let matching_modes = - [subtyping_mode, assignability_mode, - immutable_reference_mode, mutable_reference_mode]; - - loop { - // Try to find a method that is keyed directly off of the - // type. This only happens for boxed traits, type params, - // classes, and self. If we see some sort of pointer, then - // we look at candidates for the pointed to type to match - // them against methods that take explicit self parameters. - // N.B.: this looking through boxes to match against - // explicit self parameters is *not* the same as - // autoderef. - // Try each of the possible matching semantics in turn. - for matching_modes.each |mode| { - match ty::get(self.self_ty).sty { - ty::ty_box(mt) | ty::ty_uniq(mt) | ty::ty_rptr(_, mt) => { - self.add_candidates_from_type(mt.ty, mode); - } - _ => { self.add_candidates_from_type(self.self_ty, mode); } - } - if self.candidates.len() > 0u { break; } - } - - // if we found anything, stop now. otherwise continue to - // loop for impls in scope. Note: I don't love these - // semantics, but that's what we had so I am preserving - // it. - if self.candidates.len() > 0u { break; } - - // Try each of the possible matching semantics in turn. - for matching_modes.each |mode| { - self.add_inherent_and_extension_candidates( - optional_inherent_methods, mode); - // If we find anything, stop. - if self.candidates.len() > 0u { break; } - } - // if we found anything, stop before attempting auto-deref. - if self.candidates.len() > 0u { - debug!("(checking method) found at least one inherent \ - method; giving up looking now"); - break; - } - - // check whether we can autoderef and if so loop around again. - match ty::deref(self.tcx(), self.self_ty, false) { - None => break, - Some(mt) => { - debug!("(checking method) ... autodereffing"); - self.self_ty = mt.ty; - self.derefs += 1u; - } - } - } - - if self.candidates.len() == 0u { - debug!("(checking method) couldn't find any candidate methods; \ - returning none"); - return None; - } - - if self.candidates.len() > 1u { - self.tcx().sess.span_err( - self.expr.span, - ~"multiple applicable methods in scope"); - - for self.candidates.eachi |i, candidate| { - match candidate.entry.origin { - method_static(did) => { - self.report_static_candidate(i, did); - } - method_param(p) => { - self.report_param_candidate(i, p.trait_id); - } - method_trait(did, _) => { - self.report_trait_candidate(i, did); - } - } - } - } - - Some(self.write_mty_from_candidate(self.candidates[0u])) - } - - fn tcx() -> ty::ctxt { self.fcx.ccx.tcx } - - fn report_static_candidate(idx: uint, did: ast::def_id) { - let span = if did.crate == ast::local_crate { - match self.tcx().items.get(did.node) { - ast_map::node_method(m, _, _) => m.span, - _ => fail ~"report_static_candidate: bad item" - } - } else { - self.expr.span - }; - self.tcx().sess.span_note( - span, - fmt!("candidate #%u is `%s`", - (idx+1u), - ty::item_path_str(self.tcx(), did))); - } - - fn report_param_candidate(idx: uint, did: ast::def_id) { - self.tcx().sess.span_note( - self.expr.span, - fmt!("candidate #%u derives from the bound `%s`", - (idx+1u), - ty::item_path_str(self.tcx(), did))); - } - - fn report_trait_candidate(idx: uint, did: ast::def_id) { - self.tcx().sess.span_note( - self.expr.span, - fmt!("candidate #%u derives from the type of the receiver, \ - which is the trait `%s`", - (idx+1u), - ty::item_path_str(self.tcx(), did))); - } - - fn add_candidates_from_type(inner_ty: ty::t, mode: method_lookup_mode) { - match ty::get(inner_ty).sty { - // First, see whether this is a bounded parameter. - ty::ty_param(p) => { - self.add_candidates_from_param(inner_ty, mode, p.idx, p.def_id); - } - ty::ty_trait(did, substs, _) => { - self.add_candidates_from_trait(inner_ty, mode, did, substs); - } - ty::ty_class(did, substs) => { - self.add_candidates_from_class(inner_ty, mode, did, substs); - } - ty::ty_self => { - // Call is of the form "self.foo()" and appears in one - // of a trait's provided methods. - let self_def_id = self.fcx.self_impl_def_id.expect( - ~"unexpected `none` for self_impl_def_id"); - - let substs = { - self_r: None, - self_ty: None, - tps: ~[], - }; - - self.add_candidates_from_trait(inner_ty, mode, - self_def_id, substs); - } - _ => () - } - } - - fn add_candidates_from_param(inner_ty: ty::t, mode: method_lookup_mode, - n: uint, did: ast::def_id) { - debug!("add_candidates_from_param"); - - let tcx = self.tcx(); - let mut trait_bnd_idx = 0u; // count only trait bounds - let bounds = tcx.ty_param_bounds.get(did.node); - for vec::each(*bounds) |bound| { - let (trait_id, bound_substs) = match bound { - ty::bound_copy | ty::bound_send | ty::bound_const | - ty::bound_owned => { - loop; /* ok */ - } - ty::bound_trait(bound_t) => { - match ty::get(bound_t).sty { - ty::ty_trait(i, substs, _) => (i, substs), - _ => fail ~"add_candidates_from_param: non-trait bound" - } - } - }; - - let trt_methods = ty::trait_methods(tcx, trait_id); - let match_fn: &fn(m: ty::method) -> bool = |m| { - m.self_ty != ast::sty_static && m.ident == self.m_name - }; - match vec::position(*trt_methods, match_fn) { - None => { - /* check next bound */ - trait_bnd_idx += 1u; - } - - Some(pos) => { - // Replace any appearance of `self` with the type of the - // generic parameter itself. Note that this is the only case - // where this replacement is necessary: in all other cases, we - // are either invoking a method directly from an impl or class - // (where the self type is not permitted), or from a trait - // type (in which case methods that refer to self are not - // permitted). - let substs = {self_ty: Some(self.self_ty), - .. bound_substs}; - - self.add_candidates_from_m( - inner_ty, - mode, - substs, trt_methods[pos], - method_param({trait_id:trait_id, - method_num:pos, - param_num:n, - bound_num:trait_bnd_idx})); - } - } - } - - } - - fn add_candidates_from_trait(inner_ty: ty::t, - mode: method_lookup_mode, - did: ast::def_id, - trait_substs: ty::substs) { - - debug!("add_candidates_from_trait"); - - let ms = *ty::trait_methods(self.tcx(), did); - for ms.eachi |i, m| { - if m.ident != self.m_name { loop; } - - let m_fty = ty::mk_fn(self.tcx(), m.fty); - - if ty::type_has_self(m_fty) { - self.tcx().sess.span_err( - self.expr.span, - ~"cannot call a method whose type contains a \ - self-type through a boxed trait"); - } - - if (*m.tps).len() > 0u { - self.tcx().sess.span_err( - self.expr.span, - ~"cannot call a generic method through a \ - boxed trait"); - } - - // Note: although it is illegal to invoke a method that uses self - // through a trait instance, we use a dummy subst here so that we - // can soldier on with the compilation. - let substs = {self_ty: Some(self.self_ty), - .. trait_substs}; - - self.add_candidates_from_m( - inner_ty, mode, substs, m, method_trait(did, i)); - } - } - - fn add_candidates_from_class(inner_ty: ty::t, - mode: method_lookup_mode, - did: ast::def_id, - class_substs: ty::substs) { - - debug!("add_candidates_from_class"); - - let ms = *ty::trait_methods(self.tcx(), did); - - for ms.each |m| { - if m.ident != self.m_name { loop; } - - if m.vis == ast::private && !self.include_private { - self.tcx().sess.span_fatal( - self.expr.span, - ~"call to private method not allowed outside \ - its defining class"); - } - - // look up method named <name>. - let m_declared = ty::lookup_class_method_by_name( - self.tcx(), did, self.m_name, self.expr.span); - - self.add_candidates_from_m( - inner_ty, mode, class_substs, m, method_static(m_declared)); - } - } - - fn ty_from_did(did: ast::def_id) -> ty::t { - match ty::get(ty::lookup_item_type(self.tcx(), did).ty).sty { - ty::ty_fn(ref fty) => { - ty::mk_fn(self.tcx(), FnTyBase { - meta: FnMeta {proto: ty::proto_vstore(ty::vstore_box), - ..fty.meta}, - sig: fty.sig - }) - } - _ => fail ~"ty_from_did: not function ty" - } - /* - if did.crate == ast::local_crate { - match check self.tcx().items.get(did.node) { - ast_map::node_method(m, _, _) { - // NDM trait/impl regions - let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none); - ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty}) - } - } - } else { - match check ty::get(csearch::get_type(self.tcx(), did).ty) - .sty { - - ty::ty_fn(fty) { - ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty}) - } - } - } - */ - } - - fn check_type_match(impl_ty: ty::t, - mode: method_lookup_mode) - -> Result<(), ty::type_err> { - // Depending on our argument, we find potential matches by - // checking subtypability, type assignability, or reference - // subtypability. Collect the matches. - match mode { - subtyping_mode => self.fcx.can_mk_subty(self.self_ty, impl_ty), - assignability_mode => self.fcx.can_mk_assignty(self.self_ty, - impl_ty), - immutable_reference_mode => { - let region = self.fcx.infcx().next_region_var( - self.self_expr.span, - self.self_expr.id); - let tm = { ty: self.self_ty, mutbl: ast::m_imm }; - let ref_ty = ty::mk_rptr(self.tcx(), region, tm); - self.fcx.can_mk_subty(ref_ty, impl_ty) - } - mutable_reference_mode => { - let region = self.fcx.infcx().next_region_var( - self.self_expr.span, - self.self_expr.id); - let tm = { ty: self.self_ty, mutbl: ast::m_mutbl }; - let ref_ty = ty::mk_rptr(self.tcx(), region, tm); - self.fcx.can_mk_subty(ref_ty, impl_ty) - } - } - } - - // Returns true if any were added and false otherwise. - fn add_candidates_from_impl(im: @resolve::Impl, mode: method_lookup_mode) - -> bool { - let mut added_any = false; - - // Check whether this impl has a method with the right name. - for im.methods.find(|m| m.ident == self.m_name).each |m| { - - let need_rp = match m.self_type { ast::sty_region(_) => true, - _ => false }; - - // determine the `self` of the impl with fresh - // variables for each parameter: - let {substs: impl_substs, ty: impl_ty} = - impl_self_ty(self.fcx, self.self_expr, im.did, need_rp); - - let impl_ty = transform_self_type_for_method( - self.tcx(), impl_substs.self_r, - impl_ty, m.self_type); - - let matches = self.check_type_match(impl_ty, mode); - debug!("matches = %?", matches); - match matches { - result::Err(_) => { /* keep looking */ } - result::Ok(_) => { - if !self.candidate_impls.contains_key(im.did) { - let fty = self.ty_from_did(m.did); - self.candidates.push( - {self_ty: self.self_ty, - self_substs: impl_substs, - rcvr_ty: impl_ty, - n_tps_m: m.n_tps, - fty: fty, - entry: {derefs: self.derefs, - self_mode: get_mode_from_self_type( - m.self_type), - origin: method_static(m.did)}, - mode: mode}); - self.candidate_impls.insert(im.did, ()); - added_any = true; - } - } - } - } - - return added_any; - } - - fn add_candidates_from_m(inner_ty: ty::t, - mode: method_lookup_mode, - self_substs: ty::substs, - m: ty::method, - origin: method_origin) { - let tcx = self.fcx.ccx.tcx; - - // If we don't have a self region but have an region pointer - // explicit self, we need to make up a new region. - let self_r = match self_substs.self_r { - None => { - match m.self_ty { - ast::sty_region(_) => - Some(self.fcx.infcx().next_region_var( - self.self_expr.span, - self.self_expr.id)), - _ => None - } - } - Some(_) => self_substs.self_r - }; - let self_substs = {self_r: self_r,.. self_substs}; - - // Before we can be sure we succeeded we need to match the - // self type against the impl type that we get when we apply - // the explicit self parameter to whatever inner type we are - // looking at (which may be something that the self_type - // points to). - let impl_ty = transform_self_type_for_method( - self.tcx(), self_substs.self_r, - inner_ty, m.self_ty); - - let matches = self.check_type_match(impl_ty, mode); - debug!("matches = %?", matches); - if matches.is_err() { return; } - - // a bit hokey, but the method unbound has a bare protocol, whereas - // a.b has a protocol like fn@() (perhaps eventually fn&()): - let fty = ty::mk_fn(tcx, FnTyBase { - meta: FnMeta {proto: ty::proto_vstore(ty::vstore_box), - ..m.fty.meta}, - sig: m.fty.sig - }); - - self.candidates.push( - {self_ty: self.self_ty, - self_substs: self_substs, - rcvr_ty: self.self_ty, - n_tps_m: (*m.tps).len(), - fty: fty, - entry: {derefs: self.derefs, - self_mode: get_mode_from_self_type(m.self_ty), - origin: origin}, - mode: mode}); - } - - fn add_inherent_and_extension_candidates(optional_inherent_methods: - Option<@DVec<@Impl>>, - mode: method_lookup_mode) { - - // Add inherent methods. - match optional_inherent_methods { - None => { - // Continue. - } - Some(inherent_methods) => { - debug!("(adding inherent and extension candidates) adding \ - inherent candidates"); - for inherent_methods.each |implementation| { - debug!("(adding inherent and extension candidates) \ - adding candidates from impl: %s", - node_id_to_str(self.tcx().items, - implementation.did.node, - self.fcx.tcx().sess.parse_sess - .interner)); - self.add_candidates_from_impl(implementation, mode); - } - } - } - - // Add trait methods. - match self.fcx.ccx.trait_map.find(self.expr.id) { - None => { - // Should only happen for placement new right now. - } - Some(trait_ids) => { - for (*trait_ids).each |trait_id| { - debug!("(adding inherent and extension candidates) \ - trying trait: %s", - self.def_id_to_str(trait_id)); - - let coherence_info = self.fcx.ccx.coherence_info; - match coherence_info.extension_methods.find(trait_id) { - None => { - // Do nothing. - } - Some(extension_methods) => { - for extension_methods.each |implementation| { - debug!("(adding inherent and extension \ - candidates) adding impl %s", - self.def_id_to_str - (implementation.did)); - self.add_candidates_from_impl(implementation, mode); - } - } - } - } - } - } - } - - fn def_id_to_str(def_id: ast::def_id) -> ~str { - if def_id.crate == ast::local_crate { - node_id_to_str(self.tcx().items, def_id.node, - self.fcx.tcx().sess.parse_sess.interner) - } else { - ast_map::path_to_str(csearch::get_item_path(self.tcx(), def_id), - self.fcx.tcx().sess.parse_sess.interner) - } - } - - fn write_mty_from_candidate(cand: candidate) -> method_map_entry { - let tcx = self.fcx.ccx.tcx; - - debug!("write_mty_from_candidate(n_tps_m=%u, fty=%s, entry=%?)", - cand.n_tps_m, - self.fcx.infcx().ty_to_str(cand.fty), - cand.entry); - - match cand.mode { - subtyping_mode | assignability_mode => { - // Make the actual receiver type (cand.self_ty) assignable to - // the required receiver type (cand.rcvr_ty). If this method - // is not from an impl, this'll basically be a no-nop. - match self.fcx.mk_assignty(self.self_expr, self.borrow_lb, - cand.self_ty, cand.rcvr_ty) { - result::Ok(_) => (), - result::Err(_) => { - self.tcx().sess.span_bug( - self.expr.span, - fmt!("%s was assignable to %s but now is not?", - self.fcx.infcx().ty_to_str(cand.self_ty), - self.fcx.infcx().ty_to_str(cand.rcvr_ty))); - } - } - } - immutable_reference_mode => { - // Borrow as an immutable reference. - self.add_borrow(ast::m_imm); - } - mutable_reference_mode => { - // Borrow as a mutable reference. - self.add_borrow(ast::m_mutbl); - } - } - - // Construct the full set of type parameters for the method, - // which is equal to the class tps + the method tps. - let n_tps_supplied = self.supplied_tps.len(); - let n_tps_m = cand.n_tps_m; - let m_substs = { - if n_tps_supplied == 0u { - self.fcx.infcx().next_ty_vars(n_tps_m) - } else if n_tps_m == 0u { - tcx.sess.span_err( - self.expr.span, - ~"this method does not take type parameters"); - self.fcx.infcx().next_ty_vars(n_tps_m) - } else if n_tps_supplied != n_tps_m { - tcx.sess.span_err( - self.expr.span, - ~"incorrect number of type \ - parameters given for this method"); - self.fcx.infcx().next_ty_vars(n_tps_m) - } else { - self.supplied_tps - } - }; - - let all_substs = {tps: vec::append(cand.self_substs.tps, m_substs), - .. cand.self_substs}; - - self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs); - - return cand.entry; - } - - fn add_borrow(mutbl: ast::mutability) { - let region_var = self.fcx.infcx().next_region_var( - self.self_expr.span, - self.self_expr.id); - self.fcx.inh.borrowings.insert(self.self_expr.id, {region: region_var, - mutbl: mutbl}); - } -} - diff --git a/src/rustc/middle/typeck/check/regionck.rs b/src/rustc/middle/typeck/check/regionck.rs index 17c29b704f5..d5c6be4116a 100644 --- a/src/rustc/middle/typeck/check/regionck.rs +++ b/src/rustc/middle/typeck/check/regionck.rs @@ -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( diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs index 276b7eef578..ccc5e4c1a82 100644 --- a/src/rustc/middle/typeck/check/vtable.rs +++ b/src/rustc/middle/typeck/check/vtable.rs @@ -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(*) => { diff --git a/src/rustc/middle/typeck/check/writeback.rs b/src/rustc/middle/typeck/check/writeback.rs index f132337b686..d57b012503b 100644 --- a/src/rustc/middle/typeck/check/writeback.rs +++ b/src/rustc/middle/typeck/check/writeback.rs @@ -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, _, _) => { diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs index ed77217c625..a0bb828e412 100644 --- a/src/rustc/middle/typeck/infer.rs +++ b/src/rustc/middle/typeck/infer.rs @@ -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 { diff --git a/src/rustc/middle/typeck/infer/assignment.rs b/src/rustc/middle/typeck/infer/assignment.rs index fdb23c90b10..53731551df5 100644 --- a/src/rustc/middle/typeck/infer/assignment.rs +++ b/src/rustc/middle/typeck/infer/assignment.rs @@ -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 + }) + })) } } } diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs index a9363f0f1da..fce115c1c2a 100644 --- a/src/rustc/util/ppaux.rs +++ b/src/rustc/util/ppaux.rs @@ -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 { diff --git a/src/test/compile-fail/assign-to-method.rs b/src/test/compile-fail/assign-to-method.rs index fd578ff5c4f..24878e36cf3 100644 --- a/src/test/compile-fail/assign-to-method.rs +++ b/src/test/compile-fail/assign-to-method.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs new file mode 100644 index 00000000000..c38c5cbbf93 --- /dev/null +++ b/src/test/compile-fail/borrowck-autoref-3261.rs @@ -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 + } + } +} \ No newline at end of file diff --git a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs new file mode 100644 index 00000000000..14e268e52ad --- /dev/null +++ b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs @@ -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() { +} + diff --git a/src/test/compile-fail/borrowck-lend-args.rs b/src/test/compile-fail/borrowck-lend-args.rs index 7978ffc9550..3cb7009ee27 100644 --- a/src/test/compile-fail/borrowck-lend-args.rs +++ b/src/test/compile-fail/borrowck-lend-args.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs index 849eefa124e..7fdfbae75a6 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs index 39550e6ecd4..7111600d186 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs index d3e6bb68a48..af002ac686b 100644 --- a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs +++ b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs @@ -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); } diff --git a/src/test/compile-fail/borrowck-mut-deref-comp.rs b/src/test/compile-fail/borrowck-mut-deref-comp.rs index 005689a9897..19964740a6a 100644 --- a/src/test/compile-fail/borrowck-mut-deref-comp.rs +++ b/src/test/compile-fail/borrowck-mut-deref-comp.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs new file mode 100644 index 00000000000..652183a053e --- /dev/null +++ b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs @@ -0,0 +1,8 @@ +fn write(v: &[mut int]) { + v[0] += 1; +} + +fn main() { + let v = ~[1, 2, 3]; + write(v); //~ ERROR illegal borrow +} diff --git a/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs index da394fbf372..7b8d8e99b0a 100644 --- a/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs +++ b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-pure-scope-in-call.rs b/src/test/compile-fail/borrowck-pure-scope-in-call.rs index b7cb1202c3d..59707bfcf21 100644 --- a/src/test/compile-fail/borrowck-pure-scope-in-call.rs +++ b/src/test/compile-fail/borrowck-pure-scope-in-call.rs @@ -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 } diff --git a/src/test/compile-fail/borrowck-uniq-via-box.rs b/src/test/compile-fail/borrowck-uniq-via-box.rs index 130a6f6bd85..c3fa58b617a 100644 --- a/src/test/compile-fail/borrowck-uniq-via-box.rs +++ b/src/test/compile-fail/borrowck-uniq-via-box.rs @@ -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() { diff --git a/src/test/compile-fail/borrowck-uniq-via-ref.rs b/src/test/compile-fail/borrowck-uniq-via-ref.rs index e52b3c7807d..1a5390e62a6 100644 --- a/src/test/compile-fail/borrowck-uniq-via-ref.rs +++ b/src/test/compile-fail/borrowck-uniq-via-ref.rs @@ -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() { diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs index 9fb642e0d66..a3498dd1968 100644 --- a/src/test/compile-fail/issue-511.rs +++ b/src/test/compile-fail/issue-511.rs @@ -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 } diff --git a/src/test/run-pass/auto-ref-newtype.rs b/src/test/run-pass/auto-ref-newtype.rs new file mode 100644 index 00000000000..184acbfa44f --- /dev/null +++ b/src/test/run-pass/auto-ref-newtype.rs @@ -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; +} + diff --git a/src/test/run-pass/autoderef-and-borrow-method-receiver.rs b/src/test/run-pass/autoderef-and-borrow-method-receiver.rs index 3a47a47aad6..2e60b416887 100644 --- a/src/test/run-pass/autoderef-and-borrow-method-receiver.rs +++ b/src/test/run-pass/autoderef-and-borrow-method-receiver.rs @@ -3,7 +3,7 @@ struct Foo { } impl Foo { - fn f(&self) {} + fn f(&const self) {} } fn g(x: &mut Foo) {