2014-04-21 18:21:53 -05:00
|
|
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* A different sort of visitor for walking fn bodies. Unlike the
|
|
|
|
* normal visitor, which just walks the entire body in one shot, the
|
|
|
|
* `ExprUseVisitor` determines how expressions are being used.
|
|
|
|
*/
|
|
|
|
|
2014-08-18 10:29:44 -05:00
|
|
|
use middle::mem_categorization as mc;
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def;
|
2014-04-21 18:21:53 -05:00
|
|
|
use middle::freevars;
|
|
|
|
use middle::pat_util;
|
|
|
|
use middle::ty;
|
2014-07-01 16:30:33 -05:00
|
|
|
use middle::typeck::{MethodCall, MethodObject, MethodOrigin, MethodParam};
|
2014-05-29 00:26:56 -05:00
|
|
|
use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure};
|
2014-04-21 18:21:53 -05:00
|
|
|
use middle::typeck;
|
|
|
|
use util::ppaux::Repr;
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
use std::gc::Gc;
|
2014-07-01 16:30:33 -05:00
|
|
|
use syntax::ast;
|
|
|
|
use syntax::codemap::Span;
|
2014-05-16 12:15:33 -05:00
|
|
|
|
2014-04-21 18:21:53 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// The Delegate trait
|
|
|
|
|
2014-04-30 19:22:36 -05:00
|
|
|
/// This trait defines the callbacks you can expect to receive when
|
|
|
|
/// employing the ExprUseVisitor.
|
2014-04-21 18:21:53 -05:00
|
|
|
pub trait Delegate {
|
|
|
|
// The value found at `cmt` is either copied or moved, depending
|
|
|
|
// on mode.
|
|
|
|
fn consume(&mut self,
|
2014-04-28 07:50:50 -05:00
|
|
|
consume_id: ast::NodeId,
|
|
|
|
consume_span: Span,
|
|
|
|
cmt: mc::cmt,
|
|
|
|
mode: ConsumeMode);
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// The value found at `cmt` is either copied or moved via the
|
|
|
|
// pattern binding `consume_pat`, depending on mode.
|
|
|
|
fn consume_pat(&mut self,
|
2014-04-28 07:50:50 -05:00
|
|
|
consume_pat: &ast::Pat,
|
|
|
|
cmt: mc::cmt,
|
|
|
|
mode: ConsumeMode);
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// The value found at `borrow` is being borrowed at the point
|
|
|
|
// `borrow_id` for the region `loan_region` with kind `bk`.
|
|
|
|
fn borrow(&mut self,
|
2014-04-28 07:50:50 -05:00
|
|
|
borrow_id: ast::NodeId,
|
|
|
|
borrow_span: Span,
|
|
|
|
cmt: mc::cmt,
|
|
|
|
loan_region: ty::Region,
|
|
|
|
bk: ty::BorrowKind,
|
|
|
|
loan_cause: LoanCause);
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// The local variable `id` is declared but not initialized.
|
|
|
|
fn decl_without_init(&mut self,
|
2014-05-19 00:53:01 -05:00
|
|
|
id: ast::NodeId,
|
|
|
|
span: Span);
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// The path at `cmt` is being assigned to.
|
|
|
|
fn mutate(&mut self,
|
2014-04-28 07:50:50 -05:00
|
|
|
assignment_id: ast::NodeId,
|
|
|
|
assignment_span: Span,
|
|
|
|
assignee_cmt: mc::cmt,
|
|
|
|
mode: MutateMode);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq)]
|
2014-04-30 19:22:36 -05:00
|
|
|
pub enum LoanCause {
|
|
|
|
ClosureCapture(Span),
|
|
|
|
AddrOf,
|
|
|
|
AutoRef,
|
|
|
|
RefBinding,
|
|
|
|
OverloadedOperator,
|
2014-07-21 22:54:28 -05:00
|
|
|
ClosureInvocation,
|
|
|
|
ForLoop,
|
2014-04-30 19:22:36 -05:00
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq,Show)]
|
2014-04-30 19:22:36 -05:00
|
|
|
pub enum ConsumeMode {
|
2014-06-06 13:59:33 -05:00
|
|
|
Copy, // reference to x where x has a type that copies
|
|
|
|
Move(MoveReason), // reference to x where x has a type that moves
|
|
|
|
}
|
|
|
|
|
|
|
|
#[deriving(PartialEq,Show)]
|
|
|
|
pub enum MoveReason {
|
|
|
|
DirectRefMove,
|
|
|
|
PatBindingMove,
|
|
|
|
CaptureMove,
|
2014-04-30 19:22:36 -05:00
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq,Show)]
|
2014-04-30 19:22:36 -05:00
|
|
|
pub enum MutateMode {
|
2014-06-06 13:59:32 -05:00
|
|
|
Init,
|
2014-04-30 19:22:36 -05:00
|
|
|
JustWrite, // x = y
|
|
|
|
WriteAndRead, // x += y
|
|
|
|
}
|
|
|
|
|
2014-07-01 16:30:33 -05:00
|
|
|
enum OverloadedCallType {
|
|
|
|
FnOverloadedCall,
|
|
|
|
FnMutOverloadedCall,
|
|
|
|
FnOnceOverloadedCall,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OverloadedCallType {
|
|
|
|
fn from_trait_id(tcx: &ty::ctxt, trait_id: ast::DefId)
|
|
|
|
-> OverloadedCallType {
|
|
|
|
for &(maybe_function_trait, overloaded_call_type) in [
|
|
|
|
(tcx.lang_items.fn_once_trait(), FnOnceOverloadedCall),
|
|
|
|
(tcx.lang_items.fn_mut_trait(), FnMutOverloadedCall),
|
|
|
|
(tcx.lang_items.fn_trait(), FnOverloadedCall)
|
|
|
|
].iter() {
|
|
|
|
match maybe_function_trait {
|
|
|
|
Some(function_trait) if function_trait == trait_id => {
|
|
|
|
return overloaded_call_type
|
|
|
|
}
|
|
|
|
_ => continue,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tcx.sess.bug("overloaded call didn't map to known function trait")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_method_id(tcx: &ty::ctxt, method_id: ast::DefId)
|
|
|
|
-> OverloadedCallType {
|
2014-08-20 11:12:16 -05:00
|
|
|
let method_descriptor = match ty::impl_or_trait_item(tcx, method_id) {
|
|
|
|
ty::MethodTraitItem(ref method_descriptor) => {
|
|
|
|
(*method_descriptor).clone()
|
|
|
|
}
|
|
|
|
};
|
2014-07-01 16:30:33 -05:00
|
|
|
let impl_id = match method_descriptor.container {
|
|
|
|
ty::TraitContainer(_) => {
|
|
|
|
tcx.sess.bug("statically resolved overloaded call method \
|
|
|
|
belonged to a trait?!")
|
|
|
|
}
|
|
|
|
ty::ImplContainer(impl_id) => impl_id,
|
|
|
|
};
|
|
|
|
let trait_ref = match ty::impl_trait_ref(tcx, impl_id) {
|
|
|
|
None => {
|
|
|
|
tcx.sess.bug("statically resolved overloaded call impl \
|
|
|
|
didn't implement a trait?!")
|
|
|
|
}
|
|
|
|
Some(ref trait_ref) => (*trait_ref).clone(),
|
|
|
|
};
|
|
|
|
OverloadedCallType::from_trait_id(tcx, trait_ref.def_id)
|
|
|
|
}
|
|
|
|
|
2014-08-20 11:12:16 -05:00
|
|
|
fn from_unboxed_closure(tcx: &ty::ctxt, closure_did: ast::DefId)
|
|
|
|
-> OverloadedCallType {
|
|
|
|
let trait_did =
|
|
|
|
tcx.unboxed_closures
|
|
|
|
.borrow()
|
|
|
|
.find(&closure_did)
|
|
|
|
.expect("OverloadedCallType::from_unboxed_closure: didn't \
|
|
|
|
find closure id")
|
|
|
|
.kind
|
|
|
|
.trait_did(tcx);
|
|
|
|
OverloadedCallType::from_trait_id(tcx, trait_did)
|
|
|
|
}
|
|
|
|
|
2014-07-01 16:30:33 -05:00
|
|
|
fn from_method_origin(tcx: &ty::ctxt, origin: &MethodOrigin)
|
|
|
|
-> OverloadedCallType {
|
|
|
|
match *origin {
|
|
|
|
MethodStatic(def_id) => {
|
|
|
|
OverloadedCallType::from_method_id(tcx, def_id)
|
|
|
|
}
|
2014-05-29 00:26:56 -05:00
|
|
|
MethodStaticUnboxedClosure(def_id) => {
|
2014-08-20 11:12:16 -05:00
|
|
|
OverloadedCallType::from_unboxed_closure(tcx, def_id)
|
2014-05-29 00:26:56 -05:00
|
|
|
}
|
2014-07-01 16:30:33 -05:00
|
|
|
MethodParam(ref method_param) => {
|
|
|
|
OverloadedCallType::from_trait_id(tcx, method_param.trait_id)
|
|
|
|
}
|
|
|
|
MethodObject(ref method_object) => {
|
|
|
|
OverloadedCallType::from_trait_id(tcx, method_object.trait_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:21:53 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// The ExprUseVisitor type
|
|
|
|
//
|
|
|
|
// This is the code that actually walks the tree. Like
|
|
|
|
// mem_categorization, it requires a TYPER, which is a type that
|
|
|
|
// supplies types from the tree. After type checking is complete, you
|
|
|
|
// can just use the tcx as the typer.
|
|
|
|
|
2014-08-27 20:46:52 -05:00
|
|
|
pub struct ExprUseVisitor<'d,'t,TYPER:'t> {
|
|
|
|
typer: &'t TYPER,
|
|
|
|
mc: mc::MemCategorizationContext<'t,TYPER>,
|
|
|
|
delegate: &'d mut Delegate+'d,
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the TYPER results in an error, it's because the type check
|
|
|
|
// failed (or will fail, when the error is uncovered and reported
|
|
|
|
// during writeback). In this case, we just ignore this part of the
|
|
|
|
// code.
|
|
|
|
//
|
|
|
|
// Note that this macro appears similar to try!(), but, unlike try!(),
|
|
|
|
// it does not propagate the error.
|
2014-04-30 19:22:36 -05:00
|
|
|
macro_rules! return_if_err(
|
2014-04-21 18:21:53 -05:00
|
|
|
($inp: expr) => (
|
|
|
|
match $inp {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(()) => return
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2014-04-24 05:17:58 -05:00
|
|
|
impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
|
|
|
|
pub fn new(delegate: &'d mut Delegate,
|
2014-04-21 18:21:53 -05:00
|
|
|
typer: &'t TYPER)
|
2014-04-24 05:17:58 -05:00
|
|
|
-> ExprUseVisitor<'d,'t,TYPER> {
|
2014-04-21 18:21:53 -05:00
|
|
|
ExprUseVisitor { typer: typer,
|
|
|
|
mc: mc::MemCategorizationContext::new(typer),
|
|
|
|
delegate: delegate }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn walk_fn(&mut self,
|
|
|
|
decl: &ast::FnDecl,
|
|
|
|
body: &ast::Block) {
|
|
|
|
self.walk_arg_patterns(decl, body);
|
|
|
|
self.walk_block(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_arg_patterns(&mut self,
|
|
|
|
decl: &ast::FnDecl,
|
|
|
|
body: &ast::Block) {
|
|
|
|
for arg in decl.inputs.iter() {
|
2014-08-25 15:30:49 -05:00
|
|
|
let arg_ty = return_if_err!(self.typer.node_ty(arg.pat.id));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
let arg_cmt = self.mc.cat_rvalue(
|
|
|
|
arg.id,
|
|
|
|
arg.pat.span,
|
|
|
|
ty::ReScope(body.id), // Args live only as long as the fn body.
|
|
|
|
arg_ty);
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
self.walk_pat(arg_cmt, arg.pat.clone());
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
|
|
|
|
self.typer.tcx()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn delegate_consume(&mut self,
|
|
|
|
consume_id: ast::NodeId,
|
|
|
|
consume_span: Span,
|
|
|
|
cmt: mc::cmt) {
|
2014-06-06 13:59:33 -05:00
|
|
|
let mode = copy_or_move(self.tcx(), cmt.ty, DirectRefMove);
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate.consume(consume_id, consume_span, cmt, mode);
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn consume_exprs(&mut self, exprs: &Vec<Gc<ast::Expr>>) {
|
|
|
|
for expr in exprs.iter() {
|
|
|
|
self.consume_expr(&**expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn consume_expr(&mut self, expr: &ast::Expr) {
|
|
|
|
debug!("consume_expr(expr={})", expr.repr(self.tcx()));
|
|
|
|
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate_consume(expr.id, expr.span, cmt);
|
2014-05-21 07:49:16 -05:00
|
|
|
self.walk_expr(expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn mutate_expr(&mut self,
|
|
|
|
assignment_expr: &ast::Expr,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
mode: MutateMode) {
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate.mutate(assignment_expr.id, assignment_expr.span, cmt, mode);
|
|
|
|
self.walk_expr(expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn borrow_expr(&mut self,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
r: ty::Region,
|
|
|
|
bk: ty::BorrowKind,
|
|
|
|
cause: LoanCause) {
|
|
|
|
debug!("borrow_expr(expr={}, r={}, bk={})",
|
|
|
|
expr.repr(self.tcx()), r.repr(self.tcx()), bk.repr(self.tcx()));
|
|
|
|
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt = return_if_err!(self.mc.cat_expr(expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate.borrow(expr.id, expr.span, cmt, r, bk, cause);
|
|
|
|
|
|
|
|
// Note: Unlike consume, we can ignore ExprParen. cat_expr
|
|
|
|
// already skips over them, and walk will uncover any
|
|
|
|
// attachments or whatever.
|
|
|
|
self.walk_expr(expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn select_from_expr(&mut self, expr: &ast::Expr) {
|
|
|
|
self.walk_expr(expr)
|
|
|
|
}
|
|
|
|
|
librustc: Disallow mutation and assignment in pattern guards, and modify
the CFG for match statements.
There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.
I discussed this with Niko and we decided this was the best plan of
action.
This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz if self.f(...) => { ... }
_ => { ... }
}
}
}
Change this code to not use a guard. For example:
impl Foo {
fn f(&mut self, ...) {}
fn g(&mut self, ...) {
match bar {
Baz => {
if self.f(...) {
...
} else {
...
}
}
_ => { ... }
}
}
}
Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.
Closes #14684.
[breaking-change]
2014-07-25 17:18:19 -05:00
|
|
|
pub fn walk_expr(&mut self, expr: &ast::Expr) {
|
2014-04-21 18:21:53 -05:00
|
|
|
debug!("walk_expr(expr={})", expr.repr(self.tcx()));
|
|
|
|
|
|
|
|
self.walk_adjustment(expr);
|
|
|
|
|
|
|
|
match expr.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprParen(ref subexpr) => {
|
|
|
|
self.walk_expr(&**subexpr)
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprPath(..) => { }
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprUnary(ast::UnDeref, ref base) => { // *base
|
|
|
|
if !self.walk_overloaded_operator(expr, &**base, []) {
|
|
|
|
self.select_from_expr(&**base);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprField(ref base, _, _) => { // base.f
|
|
|
|
self.select_from_expr(&**base);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprIndex(ref lhs, ref rhs) => { // lhs[rhs]
|
|
|
|
if !self.walk_overloaded_operator(expr, &**lhs, [rhs.clone()]) {
|
|
|
|
self.select_from_expr(&**lhs);
|
|
|
|
self.consume_expr(&**rhs);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprCall(ref callee, ref args) => { // callee(args)
|
|
|
|
self.walk_callee(expr, &**callee);
|
2014-04-21 18:21:53 -05:00
|
|
|
self.consume_exprs(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprMethodCall(_, _, ref args) => { // callee.m(args)
|
|
|
|
self.consume_exprs(args);
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprStruct(_, ref fields, ref opt_with) => {
|
|
|
|
self.walk_struct_expr(expr, fields, opt_with.clone());
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprTup(ref exprs) => {
|
|
|
|
self.consume_exprs(exprs);
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprIf(ref cond_expr, ref then_blk, ref opt_else_expr) => {
|
|
|
|
self.consume_expr(&**cond_expr);
|
|
|
|
self.walk_block(&**then_blk);
|
2014-04-21 18:21:53 -05:00
|
|
|
for else_expr in opt_else_expr.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&**else_expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprMatch(ref discr, ref arms) => {
|
2014-04-21 18:21:53 -05:00
|
|
|
// treatment of the discriminant is handled while
|
|
|
|
// walking the arms:
|
2014-05-16 12:15:33 -05:00
|
|
|
self.walk_expr(&**discr);
|
|
|
|
let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
|
2014-04-21 18:21:53 -05:00
|
|
|
for arm in arms.iter() {
|
|
|
|
self.walk_arm(discr_cmt.clone(), arm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprVec(ref exprs) => {
|
|
|
|
self.consume_exprs(exprs);
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprAddrOf(m, ref base) => { // &base
|
2014-04-21 18:21:53 -05:00
|
|
|
// make sure that the thing we are pointing out stays valid
|
|
|
|
// for the lifetime `scope_r` of the resulting ptr:
|
|
|
|
let expr_ty = ty::expr_ty(self.tcx(), expr);
|
|
|
|
if !ty::type_is_bot(expr_ty) {
|
|
|
|
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
|
|
|
|
let bk = ty::BorrowKind::from_mutbl(m);
|
2014-05-16 12:15:33 -05:00
|
|
|
self.borrow_expr(&**base, r, bk, AddrOf);
|
2014-04-21 18:21:53 -05:00
|
|
|
} else {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.walk_expr(&**base);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprInlineAsm(ref ia) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
for &(_, ref input) in ia.inputs.iter() {
|
|
|
|
self.consume_expr(&**input);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-08-19 14:39:26 -05:00
|
|
|
for &(_, ref output, is_rw) in ia.outputs.iter() {
|
|
|
|
self.mutate_expr(expr, &**output,
|
|
|
|
if is_rw { WriteAndRead } else { JustWrite });
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprBreak(..) |
|
|
|
|
ast::ExprAgain(..) |
|
|
|
|
ast::ExprLit(..) => {}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprLoop(ref blk, _) => {
|
|
|
|
self.walk_block(&**blk);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-07-25 19:12:51 -05:00
|
|
|
ast::ExprWhile(ref cond_expr, ref blk, _) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&**cond_expr);
|
|
|
|
self.walk_block(&**blk);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-07-21 22:54:28 -05:00
|
|
|
ast::ExprForLoop(ref pat, ref head, ref blk, _) => {
|
|
|
|
// The pattern lives as long as the block.
|
|
|
|
debug!("walk_expr for loop case: blk id={}", blk.id);
|
2014-08-12 14:51:23 -05:00
|
|
|
self.consume_expr(&**head);
|
2014-07-21 22:54:28 -05:00
|
|
|
|
2014-08-12 14:51:23 -05:00
|
|
|
// Fetch the type of the value that the iteration yields to
|
|
|
|
// produce the pattern's categorized mutable type.
|
2014-08-25 15:30:49 -05:00
|
|
|
let pattern_type = return_if_err!(self.typer.node_ty(pat.id));
|
2014-08-12 14:51:23 -05:00
|
|
|
let pat_cmt = self.mc.cat_rvalue(pat.id,
|
|
|
|
pat.span,
|
|
|
|
ty::ReScope(blk.id),
|
|
|
|
pattern_type);
|
|
|
|
self.walk_pat(pat_cmt, pat.clone());
|
2014-07-21 22:54:28 -05:00
|
|
|
|
|
|
|
self.walk_block(&**blk);
|
|
|
|
}
|
2014-04-21 18:21:53 -05:00
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprUnary(_, ref lhs) => {
|
|
|
|
if !self.walk_overloaded_operator(expr, &**lhs, []) {
|
|
|
|
self.consume_expr(&**lhs);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprBinary(_, ref lhs, ref rhs) => {
|
|
|
|
if !self.walk_overloaded_operator(expr, &**lhs, [rhs.clone()]) {
|
|
|
|
self.consume_expr(&**lhs);
|
|
|
|
self.consume_expr(&**rhs);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprBlock(ref blk) => {
|
|
|
|
self.walk_block(&**blk);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprRet(ref opt_expr) => {
|
|
|
|
for expr in opt_expr.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&**expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprAssign(ref lhs, ref rhs) => {
|
|
|
|
self.mutate_expr(expr, &**lhs, JustWrite);
|
|
|
|
self.consume_expr(&**rhs);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprCast(ref base, _) => {
|
|
|
|
self.consume_expr(&**base);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprAssignOp(_, ref lhs, ref rhs) => {
|
2014-04-24 05:17:58 -05:00
|
|
|
// This will have to change if/when we support
|
|
|
|
// overloaded operators for `+=` and so forth.
|
2014-05-16 12:15:33 -05:00
|
|
|
self.mutate_expr(expr, &**lhs, WriteAndRead);
|
|
|
|
self.consume_expr(&**rhs);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprRepeat(ref base, ref count) => {
|
|
|
|
self.consume_expr(&**base);
|
|
|
|
self.consume_expr(&**count);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprFnBlock(..) |
|
2014-05-29 00:26:56 -05:00
|
|
|
ast::ExprUnboxedFn(..) |
|
2014-04-21 18:21:53 -05:00
|
|
|
ast::ExprProc(..) => {
|
|
|
|
self.walk_captures(expr)
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprBox(ref place, ref base) => {
|
|
|
|
self.consume_expr(&**place);
|
|
|
|
self.consume_expr(&**base);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprMac(..) => {
|
|
|
|
self.tcx().sess.span_bug(
|
|
|
|
expr.span,
|
|
|
|
"macro expression remains after expansion");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) {
|
|
|
|
let callee_ty = ty::expr_ty_adjusted(self.tcx(), callee);
|
|
|
|
debug!("walk_callee: callee={} callee_ty={}",
|
|
|
|
callee.repr(self.tcx()), callee_ty.repr(self.tcx()));
|
|
|
|
match ty::get(callee_ty).sty {
|
|
|
|
ty::ty_bare_fn(..) => {
|
|
|
|
self.consume_expr(callee);
|
|
|
|
}
|
|
|
|
ty::ty_closure(ref f) => {
|
|
|
|
match f.onceness {
|
|
|
|
ast::Many => {
|
|
|
|
self.borrow_expr(callee,
|
|
|
|
ty::ReScope(call.id),
|
|
|
|
ty::UniqueImmBorrow,
|
|
|
|
ClosureInvocation);
|
|
|
|
}
|
|
|
|
ast::Once => {
|
|
|
|
self.consume_expr(callee);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
2014-07-01 16:30:33 -05:00
|
|
|
let overloaded_call_type =
|
|
|
|
match self.tcx()
|
|
|
|
.method_map
|
|
|
|
.borrow()
|
|
|
|
.find(&MethodCall::expr(call.id)) {
|
|
|
|
Some(ref method_callee) => {
|
|
|
|
OverloadedCallType::from_method_origin(
|
|
|
|
self.tcx(),
|
|
|
|
&method_callee.origin)
|
2014-06-01 18:35:01 -05:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
self.tcx().sess.span_bug(
|
|
|
|
callee.span,
|
2014-06-08 23:00:52 -05:00
|
|
|
format!("unexpected callee type {}",
|
2014-07-01 16:30:33 -05:00
|
|
|
callee_ty.repr(self.tcx())).as_slice())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match overloaded_call_type {
|
|
|
|
FnMutOverloadedCall => {
|
|
|
|
self.borrow_expr(callee,
|
|
|
|
ty::ReScope(call.id),
|
|
|
|
ty::MutBorrow,
|
|
|
|
ClosureInvocation);
|
|
|
|
}
|
|
|
|
FnOverloadedCall => {
|
|
|
|
self.borrow_expr(callee,
|
|
|
|
ty::ReScope(call.id),
|
|
|
|
ty::ImmBorrow,
|
|
|
|
ClosureInvocation);
|
2014-06-01 18:35:01 -05:00
|
|
|
}
|
2014-07-01 16:30:33 -05:00
|
|
|
FnOnceOverloadedCall => self.consume_expr(callee),
|
2014-06-01 18:35:01 -05:00
|
|
|
}
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_stmt(&mut self, stmt: &ast::Stmt) {
|
|
|
|
match stmt.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::StmtDecl(ref decl, _) => {
|
2014-04-21 18:21:53 -05:00
|
|
|
match decl.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::DeclLocal(ref local) => {
|
|
|
|
self.walk_local(local.clone());
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::DeclItem(_) => {
|
|
|
|
// we don't visit nested items in this visitor,
|
|
|
|
// only the fn body we were given.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::StmtExpr(ref expr, _) |
|
|
|
|
ast::StmtSemi(ref expr, _) => {
|
|
|
|
self.consume_expr(&**expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ast::StmtMac(..) => {
|
2014-05-16 12:45:16 -05:00
|
|
|
self.tcx().sess.span_bug(stmt.span, "unexpanded stmt macro");
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn walk_local(&mut self, local: Gc<ast::Local>) {
|
2014-04-21 18:21:53 -05:00
|
|
|
match local.init {
|
|
|
|
None => {
|
|
|
|
let delegate = &mut self.delegate;
|
2014-05-16 12:15:33 -05:00
|
|
|
pat_util::pat_bindings(&self.typer.tcx().def_map, &*local.pat,
|
|
|
|
|_, id, span, _| {
|
2014-04-21 18:21:53 -05:00
|
|
|
delegate.decl_without_init(id, span);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
Some(ref expr) => {
|
2014-04-21 18:21:53 -05:00
|
|
|
// Variable declarations with
|
|
|
|
// initializers are considered
|
|
|
|
// "assigns", which is handled by
|
|
|
|
// `walk_pat`:
|
2014-05-16 12:15:33 -05:00
|
|
|
self.walk_expr(&**expr);
|
|
|
|
let init_cmt = return_if_err!(self.mc.cat_expr(&**expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.walk_pat(init_cmt, local.pat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_block(&mut self, blk: &ast::Block) {
|
|
|
|
/*!
|
|
|
|
* Indicates that the value of `blk` will be consumed,
|
|
|
|
* meaning either copied or moved depending on its type.
|
|
|
|
*/
|
|
|
|
|
|
|
|
debug!("walk_block(blk.id={:?})", blk.id);
|
|
|
|
|
|
|
|
for stmt in blk.stmts.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.walk_stmt(&**stmt);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for tail_expr in blk.expr.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&**tail_expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_struct_expr(&mut self,
|
|
|
|
_expr: &ast::Expr,
|
|
|
|
fields: &Vec<ast::Field>,
|
2014-05-16 12:15:33 -05:00
|
|
|
opt_with: Option<Gc<ast::Expr>>) {
|
2014-04-21 18:21:53 -05:00
|
|
|
// Consume the expressions supplying values for each field.
|
|
|
|
for field in fields.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&*field.expr);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let with_expr = match opt_with {
|
2014-05-16 12:15:33 -05:00
|
|
|
Some(ref w) => { w.clone() }
|
2014-04-21 18:21:53 -05:00
|
|
|
None => { return; }
|
|
|
|
};
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
let with_cmt = return_if_err!(self.mc.cat_expr(&*with_expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// Select just those fields of the `with`
|
|
|
|
// expression that will actually be used
|
|
|
|
let with_fields = match ty::get(with_cmt.ty).sty {
|
|
|
|
ty::ty_struct(did, ref substs) => {
|
|
|
|
ty::struct_fields(self.tcx(), did, substs)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.tcx().sess.span_bug(
|
|
|
|
with_expr.span,
|
2014-05-16 12:45:16 -05:00
|
|
|
"with expression doesn't evaluate to a struct");
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Consume those fields of the with expression that are needed.
|
|
|
|
for with_field in with_fields.iter() {
|
|
|
|
if !contains_field_named(with_field, fields) {
|
2014-05-16 12:15:33 -05:00
|
|
|
let cmt_field = self.mc.cat_field(&*with_expr,
|
2014-04-21 18:21:53 -05:00
|
|
|
with_cmt.clone(),
|
|
|
|
with_field.ident,
|
|
|
|
with_field.mt.ty);
|
|
|
|
self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contains_field_named(field: &ty::field,
|
|
|
|
fields: &Vec<ast::Field>)
|
|
|
|
-> bool
|
|
|
|
{
|
|
|
|
fields.iter().any(
|
|
|
|
|f| f.ident.node.name == field.ident.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke the appropriate delegate calls for anything that gets
|
|
|
|
// consumed or borrowed as part of the automatic adjustment
|
|
|
|
// process.
|
|
|
|
fn walk_adjustment(&mut self, expr: &ast::Expr) {
|
|
|
|
let typer = self.typer;
|
|
|
|
match typer.adjustments().borrow().find(&expr.id) {
|
|
|
|
None => { }
|
|
|
|
Some(adjustment) => {
|
|
|
|
match *adjustment {
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
ty::AutoAddEnv(..) => {
|
|
|
|
// Creating a closure consumes the input and stores it
|
|
|
|
// into the resulting rvalue.
|
|
|
|
debug!("walk_adjustment(AutoAddEnv)");
|
2014-04-21 18:21:53 -05:00
|
|
|
let cmt_unadjusted =
|
2014-04-30 19:22:36 -05:00
|
|
|
return_if_err!(self.mc.cat_expr_unadjusted(expr));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
|
|
|
|
}
|
|
|
|
ty::AutoDerefRef(ty::AutoDerefRef {
|
|
|
|
autoref: ref opt_autoref,
|
|
|
|
autoderefs: n
|
|
|
|
}) => {
|
|
|
|
self.walk_autoderefs(expr, n);
|
|
|
|
|
|
|
|
match *opt_autoref {
|
|
|
|
None => { }
|
|
|
|
Some(ref r) => {
|
|
|
|
self.walk_autoref(expr, r, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_autoderefs(&mut self,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
autoderefs: uint) {
|
|
|
|
/*!
|
|
|
|
* Autoderefs for overloaded Deref calls in fact reference
|
|
|
|
* their receiver. That is, if we have `(*x)` where `x` is of
|
|
|
|
* type `Rc<T>`, then this in fact is equivalent to
|
|
|
|
* `x.deref()`. Since `deref()` is declared with `&self`, this
|
|
|
|
* is an autoref of `x`.
|
|
|
|
*/
|
|
|
|
debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
|
|
|
|
|
|
|
|
for i in range(0, autoderefs) {
|
2014-06-11 17:01:48 -05:00
|
|
|
let deref_id = typeck::MethodCall::autoderef(expr.id, i);
|
2014-04-21 18:21:53 -05:00
|
|
|
match self.typer.node_method_ty(deref_id) {
|
|
|
|
None => {}
|
|
|
|
Some(method_ty) => {
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
|
2014-04-21 18:21:53 -05:00
|
|
|
let self_ty = *ty::ty_fn_args(method_ty).get(0);
|
|
|
|
let (m, r) = match ty::get(self_ty).sty {
|
|
|
|
ty::ty_rptr(r, ref m) => (m.mutbl, r),
|
|
|
|
_ => self.tcx().sess.span_bug(expr.span,
|
|
|
|
format!("bad overloaded deref type {}",
|
2014-05-16 12:45:16 -05:00
|
|
|
method_ty.repr(self.tcx())).as_slice())
|
2014-04-21 18:21:53 -05:00
|
|
|
};
|
|
|
|
let bk = ty::BorrowKind::from_mutbl(m);
|
|
|
|
self.delegate.borrow(expr.id, expr.span, cmt,
|
|
|
|
r, bk, AutoRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_autoref(&mut self,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
autoref: &ty::AutoRef,
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
n: uint) {
|
|
|
|
debug!("walk_autoref expr={}", expr.repr(self.tcx()));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
// Match for unique trait coercions first, since we don't need the
|
|
|
|
// call to cat_expr_autoderefd.
|
|
|
|
match *autoref {
|
|
|
|
ty::AutoUnsizeUniq(ty::UnsizeVtable(..)) |
|
|
|
|
ty::AutoUnsize(ty::UnsizeVtable(..)) => {
|
|
|
|
assert!(n == 1, format!("Expected exactly 1 deref with Uniq \
|
|
|
|
AutoRefs, found: {}", n));
|
|
|
|
let cmt_unadjusted =
|
|
|
|
return_if_err!(self.mc.cat_expr_unadjusted(expr));
|
|
|
|
self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2014-04-21 18:21:53 -05:00
|
|
|
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
let cmt_derefd = return_if_err!(
|
|
|
|
self.mc.cat_expr_autoderefd(expr, n));
|
|
|
|
debug!("walk_adjustment: cmt_derefd={}",
|
|
|
|
cmt_derefd.repr(self.tcx()));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
match *autoref {
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
ty::AutoPtr(r, m, _) => {
|
2014-04-21 18:21:53 -05:00
|
|
|
self.delegate.borrow(expr.id,
|
|
|
|
expr.span,
|
|
|
|
cmt_derefd,
|
|
|
|
r,
|
|
|
|
ty::BorrowKind::from_mutbl(m),
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 07:20:11 -05:00
|
|
|
AutoRef);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
2014-08-27 00:07:28 -05:00
|
|
|
ty::AutoUnsizeUniq(_) | ty::AutoUnsize(_) | ty::AutoUnsafe(..) => {}
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_overloaded_operator(&mut self,
|
|
|
|
expr: &ast::Expr,
|
|
|
|
receiver: &ast::Expr,
|
2014-05-16 12:15:33 -05:00
|
|
|
args: &[Gc<ast::Expr>])
|
2014-04-21 18:21:53 -05:00
|
|
|
-> bool
|
|
|
|
{
|
|
|
|
if !self.typer.is_method_call(expr.id) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.walk_expr(receiver);
|
|
|
|
|
|
|
|
// Arguments (but not receivers) to overloaded operator
|
|
|
|
// methods are implicitly autoref'd which sadly does not use
|
|
|
|
// adjustments, so we must hardcode the borrow here.
|
|
|
|
|
|
|
|
let r = ty::ReScope(expr.id);
|
|
|
|
let bk = ty::ImmBorrow;
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
for arg in args.iter() {
|
|
|
|
self.borrow_expr(&**arg, r, bk, OverloadedOperator);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_arm(&mut self, discr_cmt: mc::cmt, arm: &ast::Arm) {
|
|
|
|
for &pat in arm.pats.iter() {
|
|
|
|
self.walk_pat(discr_cmt.clone(), pat);
|
|
|
|
}
|
|
|
|
|
|
|
|
for guard in arm.guard.iter() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&**guard);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
self.consume_expr(&*arm.body);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn walk_pat(&mut self, cmt_discr: mc::cmt, pat: Gc<ast::Pat>) {
|
2014-04-21 18:21:53 -05:00
|
|
|
debug!("walk_pat cmt_discr={} pat={}", cmt_discr.repr(self.tcx()),
|
|
|
|
pat.repr(self.tcx()));
|
|
|
|
let mc = &self.mc;
|
|
|
|
let typer = self.typer;
|
|
|
|
let tcx = typer.tcx();
|
|
|
|
let def_map = &self.typer.tcx().def_map;
|
|
|
|
let delegate = &mut self.delegate;
|
2014-05-16 12:15:33 -05:00
|
|
|
return_if_err!(mc.cat_pattern(cmt_discr, &*pat, |mc, cmt_pat, pat| {
|
2014-04-21 18:21:53 -05:00
|
|
|
if pat_util::pat_is_binding(def_map, pat) {
|
|
|
|
let tcx = typer.tcx();
|
|
|
|
|
|
|
|
debug!("binding cmt_pat={} pat={}",
|
|
|
|
cmt_pat.repr(tcx),
|
|
|
|
pat.repr(tcx));
|
|
|
|
|
|
|
|
// pat_ty: the type of the binding being produced.
|
2014-08-25 15:30:49 -05:00
|
|
|
let pat_ty = return_if_err!(typer.node_ty(pat.id));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// Each match binding is effectively an assignment to the
|
|
|
|
// binding being produced.
|
|
|
|
let def = def_map.borrow().get_copy(&pat.id);
|
|
|
|
match mc.cat_def(pat.id, pat.span, pat_ty, def) {
|
|
|
|
Ok(binding_cmt) => {
|
2014-06-06 13:59:32 -05:00
|
|
|
delegate.mutate(pat.id, pat.span, binding_cmt, Init);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
Err(_) => { }
|
|
|
|
}
|
|
|
|
|
|
|
|
// It is also a borrow or copy/move of the value being matched.
|
|
|
|
match pat.node {
|
|
|
|
ast::PatIdent(ast::BindByRef(m), _, _) => {
|
|
|
|
let (r, bk) = {
|
|
|
|
(ty::ty_region(tcx, pat.span, pat_ty),
|
|
|
|
ty::BorrowKind::from_mutbl(m))
|
|
|
|
};
|
|
|
|
delegate.borrow(pat.id, pat.span, cmt_pat,
|
|
|
|
r, bk, RefBinding);
|
|
|
|
}
|
|
|
|
ast::PatIdent(ast::BindByValue(_), _, _) => {
|
2014-06-06 13:59:33 -05:00
|
|
|
let mode = copy_or_move(typer.tcx(), cmt_pat.ty, PatBindingMove);
|
2014-08-12 14:51:23 -05:00
|
|
|
debug!("walk_pat binding consuming pat");
|
2014-04-21 18:21:53 -05:00
|
|
|
delegate.consume_pat(pat, cmt_pat, mode);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
typer.tcx().sess.span_bug(
|
|
|
|
pat.span,
|
|
|
|
"binding pattern not an identifier");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match pat.node {
|
|
|
|
ast::PatVec(_, Some(slice_pat), _) => {
|
|
|
|
// The `slice_pat` here creates a slice into
|
|
|
|
// the original vector. This is effectively a
|
|
|
|
// borrow of the elements of the vector being
|
|
|
|
// matched.
|
|
|
|
|
|
|
|
let (slice_cmt, slice_mutbl, slice_r) = {
|
2014-05-16 12:15:33 -05:00
|
|
|
match mc.cat_slice_pattern(cmt_pat, &*slice_pat) {
|
2014-04-21 18:21:53 -05:00
|
|
|
Ok(v) => v,
|
|
|
|
Err(()) => {
|
|
|
|
tcx.sess.span_bug(slice_pat.span,
|
|
|
|
"Err from mc")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Note: We declare here that the borrow
|
|
|
|
// occurs upon entering the `[...]`
|
|
|
|
// pattern. This implies that something like
|
|
|
|
// `[a, ..b]` where `a` is a move is illegal,
|
|
|
|
// because the borrow is already in effect.
|
|
|
|
// In fact such a move would be safe-ish, but
|
|
|
|
// it effectively *requires* that we use the
|
|
|
|
// nulling out semantics to indicate when a
|
|
|
|
// value has been moved, which we are trying
|
|
|
|
// to move away from. Otherwise, how can we
|
|
|
|
// indicate that the first element in the
|
|
|
|
// vector has been moved? Eventually, we
|
|
|
|
// could perhaps modify this rule to permit
|
|
|
|
// `[..a, b]` where `b` is a move, because in
|
|
|
|
// that case we can adjust the length of the
|
|
|
|
// original vec accordingly, but we'd have to
|
|
|
|
// make trans do the right thing, and it would
|
|
|
|
// only work for `~` vectors. It seems simpler
|
|
|
|
// to just require that people call
|
|
|
|
// `vec.pop()` or `vec.unshift()`.
|
|
|
|
let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl);
|
|
|
|
delegate.borrow(pat.id, pat.span,
|
|
|
|
slice_cmt, slice_r,
|
|
|
|
slice_bk, RefBinding);
|
|
|
|
}
|
|
|
|
_ => { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_captures(&mut self, closure_expr: &ast::Expr) {
|
|
|
|
debug!("walk_captures({})", closure_expr.repr(self.tcx()));
|
|
|
|
|
|
|
|
let tcx = self.typer.tcx();
|
|
|
|
freevars::with_freevars(tcx, closure_expr.id, |freevars| {
|
|
|
|
match freevars::get_capture_mode(self.tcx(), closure_expr.id) {
|
|
|
|
freevars::CaptureByRef => {
|
|
|
|
self.walk_by_ref_captures(closure_expr, freevars);
|
|
|
|
}
|
|
|
|
freevars::CaptureByValue => {
|
|
|
|
self.walk_by_value_captures(closure_expr, freevars);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_by_ref_captures(&mut self,
|
|
|
|
closure_expr: &ast::Expr,
|
|
|
|
freevars: &[freevars::freevar_entry]) {
|
|
|
|
for freevar in freevars.iter() {
|
2014-05-14 14:31:30 -05:00
|
|
|
let id_var = freevar.def.def_id().node;
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
|
|
|
|
closure_expr.span,
|
|
|
|
freevar.def));
|
2014-04-21 18:21:53 -05:00
|
|
|
|
|
|
|
// Lookup the kind of borrow the callee requires, as
|
|
|
|
// inferred by regionbk
|
|
|
|
let upvar_id = ty::UpvarId { var_id: id_var,
|
|
|
|
closure_expr_id: closure_expr.id };
|
|
|
|
let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
|
|
|
|
.get_copy(&upvar_id);
|
|
|
|
|
|
|
|
self.delegate.borrow(closure_expr.id,
|
|
|
|
closure_expr.span,
|
|
|
|
cmt_var,
|
|
|
|
upvar_borrow.region,
|
|
|
|
upvar_borrow.kind,
|
|
|
|
ClosureCapture(freevar.span));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn walk_by_value_captures(&mut self,
|
|
|
|
closure_expr: &ast::Expr,
|
|
|
|
freevars: &[freevars::freevar_entry]) {
|
|
|
|
for freevar in freevars.iter() {
|
2014-04-30 19:22:36 -05:00
|
|
|
let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id,
|
|
|
|
closure_expr.span,
|
|
|
|
freevar.def));
|
2014-06-06 13:59:33 -05:00
|
|
|
let mode = copy_or_move(self.tcx(), cmt_var.ty, CaptureMove);
|
|
|
|
self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode);
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cat_captured_var(&mut self,
|
|
|
|
closure_id: ast::NodeId,
|
|
|
|
closure_span: Span,
|
2014-05-14 14:31:30 -05:00
|
|
|
upvar_def: def::Def)
|
2014-04-21 18:21:53 -05:00
|
|
|
-> mc::McResult<mc::cmt> {
|
|
|
|
// Create the cmt for the variable being borrowed, from the
|
|
|
|
// caller's perspective
|
2014-05-14 14:31:30 -05:00
|
|
|
let var_id = upvar_def.def_id().node;
|
2014-08-25 15:30:49 -05:00
|
|
|
let var_ty = try!(self.typer.node_ty(var_id));
|
2014-04-21 18:21:53 -05:00
|
|
|
self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-06 13:59:33 -05:00
|
|
|
fn copy_or_move(tcx: &ty::ctxt, ty: ty::t, move_reason: MoveReason) -> ConsumeMode {
|
|
|
|
if ty::type_moves_by_default(tcx, ty) { Move(move_reason) } else { Copy }
|
2014-04-21 18:21:53 -05:00
|
|
|
}
|
|
|
|
|