From f7abf47058e73ea4a95031fb80d92f636e5a18f9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 03:51:49 -0500 Subject: [PATCH 1/8] Re-introduce `McResult<>` as a way of aborting mem-categorization (and expr-use-visitor) early. Turns out I was wrong to remove this; it causes a lot of pain trying to run EUV etc during typeck without ICEing on erroneous programs. --- src/librustc/middle/expr_use_visitor.rs | 79 +++++----- src/librustc/middle/mem_categorization.rs | 167 +++++++++++----------- src/librustc/middle/ty.rs | 5 +- src/librustc_typeck/check/mod.rs | 23 ++- src/librustc_typeck/check/regionck.rs | 31 ++-- 5 files changed, 175 insertions(+), 130 deletions(-) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index d36c85342ce32..06815266266a5 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -300,6 +300,20 @@ pub struct ExprUseVisitor<'d,'t,'tcx:'t,TYPER:'t> { mc: mc::MemCategorizationContext<'t,TYPER>, delegate: &'d mut (Delegate<'tcx>+'d), param_env: &'t ParameterEnvironment<'tcx>, +// 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. +macro_rules! return_if_err { + ($inp: expr) => ( + match $inp { + Ok(v) => v, + Err(()) => return + } + ) } /// Whether the elements of an overloaded operation are passed by value or by reference @@ -332,7 +346,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { decl: &ast::FnDecl, body: &ast::Block) { for arg in decl.inputs.iter() { - let arg_ty = self.typer.node_ty(arg.pat.id); + let arg_ty = return_if_err!(self.typer.node_ty(arg.pat.id)); let fn_body_scope = region::CodeExtent::from_node_id(body.id); let arg_cmt = self.mc.cat_rvalue( @@ -369,7 +383,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { pub fn consume_expr(&mut self, expr: &ast::Expr) { debug!("consume_expr(expr={})", expr.repr(self.tcx())); - let cmt = self.mc.cat_expr(expr); + let cmt = return_if_err!(self.mc.cat_expr(expr)); self.delegate_consume(expr.id, expr.span, cmt); self.walk_expr(expr); } @@ -378,7 +392,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { assignment_expr: &ast::Expr, expr: &ast::Expr, mode: MutateMode) { - let cmt = self.mc.cat_expr(expr); + let cmt = return_if_err!(self.mc.cat_expr(expr)); self.delegate.mutate(assignment_expr.id, assignment_expr.span, cmt, mode); self.walk_expr(expr); } @@ -391,7 +405,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { debug!("borrow_expr(expr={}, r={}, bk={})", expr.repr(self.tcx()), r.repr(self.tcx()), bk.repr(self.tcx())); - let cmt = self.mc.cat_expr(expr); + let cmt = return_if_err!(self.mc.cat_expr(expr)); self.delegate.borrow(expr.id, expr.span, cmt, r, bk, cause); // Note: Unlike consume, we can ignore ExprParen. cat_expr @@ -491,7 +505,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } ast::ExprMatch(ref discr, ref arms, _) => { - let discr_cmt = self.mc.cat_expr(&**discr); + let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr)); self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant); // treatment of the discriminant is handled while walking the arms. @@ -509,7 +523,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { ast::ExprAddrOf(m, ref base) => { // &base // 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); + let expr_ty = return_if_err!(self.typer.node_ty(expr.id)); let r = ty::ty_region(self.tcx(), expr.span, expr_ty); let bk = ty::BorrowKind::from_mutbl(m); self.borrow_expr(&**base, r, bk, AddrOf); @@ -550,7 +564,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // Fetch the type of the value that the iteration yields to // produce the pattern's categorized mutable type. - let pattern_type = self.typer.node_ty(pat.id); + let pattern_type = return_if_err!(self.typer.node_ty(pat.id)); let blk_scope = region::CodeExtent::from_node_id(blk.id); let pat_cmt = self.mc.cat_rvalue(pat.id, pat.span, @@ -638,7 +652,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) { - let callee_ty = self.typer.expr_ty_adjusted(callee); + let callee_ty = return_if_err!(self.typer.expr_ty_adjusted(callee)); debug!("walk_callee: callee={} callee_ty={}", callee.repr(self.tcx()), callee_ty.repr(self.tcx())); let call_scope = region::CodeExtent::from_node_id(call.id); @@ -735,7 +749,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // "assigns", which is handled by // `walk_pat`: self.walk_expr(&**expr); - let init_cmt = self.mc.cat_expr(&**expr); + let init_cmt = return_if_err!(self.mc.cat_expr(&**expr)); self.walk_irrefutable_pat(init_cmt, &*local.pat); } } @@ -769,7 +783,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { None => { return; } }; - let with_cmt = self.mc.cat_expr(&*with_expr); + let with_cmt = return_if_err!(self.mc.cat_expr(&*with_expr)); // Select just those fields of the `with` // expression that will actually be used @@ -824,7 +838,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // rvalue. debug!("walk_adjustment(AutoAddEnv|AdjustReifyFnPointer)"); let cmt_unadjusted = - self.mc.cat_expr_unadjusted(expr); + return_if_err!(self.mc.cat_expr_unadjusted(expr)); self.delegate_consume(expr.id, expr.span, cmt_unadjusted); } ty::AdjustDerefRef(ty::AutoDerefRef { @@ -858,7 +872,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { match self.typer.node_method_ty(deref_id) { None => {} Some(method_ty) => { - let cmt = self.mc.cat_expr_autoderefd(expr, i); + let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i)); let self_ty = ty::ty_fn_args(method_ty)[0]; let (m, r) = match self_ty.sty { ty::ty_rptr(r, ref m) => (m.mutbl, r), @@ -888,14 +902,15 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { assert!(n == 1, format!("Expected exactly 1 deref with Uniq \ AutoRefs, found: {}", n)); let cmt_unadjusted = - self.mc.cat_expr_unadjusted(expr); + return_if_err!(self.mc.cat_expr_unadjusted(expr)); self.delegate_consume(expr.id, expr.span, cmt_unadjusted); return; } _ => {} } - let cmt_derefd = self.mc.cat_expr_autoderefd(expr, n); + let cmt_derefd = return_if_err!( + self.mc.cat_expr_autoderefd(expr, n)); debug!("walk_adjustment: cmt_derefd={}", cmt_derefd.repr(self.tcx())); @@ -988,7 +1003,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { mode: &mut TrackMatchMode) { debug!("determine_pat_move_mode cmt_discr={} pat={}", cmt_discr.repr(self.tcx()), pat.repr(self.tcx())); - self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| { + return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| { let tcx = self.typer.tcx(); let def_map = &self.typer.tcx().def_map; if pat_util::pat_is_binding(def_map, pat) { @@ -1011,7 +1026,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } } } - }); + })); } /// The core driver for walking a pattern; `match_mode` must be @@ -1028,8 +1043,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { let typer = self.typer; let def_map = &self.typer.tcx().def_map; let delegate = &mut self.delegate; - let param_env = self.param_env; - mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| { + return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| { if pat_util::pat_is_binding(def_map, pat) { let tcx = typer.tcx(); @@ -1039,7 +1053,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { match_mode); // pat_ty: the type of the binding being produced. - let pat_ty = typer.node_ty(pat.id); + let pat_ty = return_if_err!(typer.node_ty(pat.id)); // Each match binding is effectively an assignment to the // binding being produced. @@ -1080,7 +1094,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // matched. let (slice_cmt, slice_mutbl, slice_r) = - mc.cat_slice_pattern(cmt_pat, &**slice_pat); + return_if_err!(mc.cat_slice_pattern(cmt_pat, &**slice_pat)); // Note: We declare here that the borrow // occurs upon entering the `[...]` @@ -1110,13 +1124,13 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { _ => { } } } - }); + })); // Do a second pass over the pattern, calling `matched_pat` on // the interior nodes (enum variants and structs), as opposed // to the above loop's visit of than the bindings that form // the leaves of the pattern tree structure. - mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| { + return_if_err!(mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| { let def_map = def_map.borrow(); let tcx = typer.tcx(); @@ -1197,7 +1211,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // cases either. } } - }); + })); } fn walk_captures(&mut self, closure_expr: &ast::Expr) { @@ -1221,9 +1235,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { freevars: &[ty::Freevar]) { for freevar in freevars.iter() { let id_var = freevar.def.def_id().node; - let cmt_var = self.cat_captured_var(closure_expr.id, - closure_expr.span, - freevar.def); + let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, + closure_expr.span, + freevar.def)); // Lookup the kind of borrow the callee requires, as // inferred by regionbk @@ -1244,11 +1258,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { closure_expr: &ast::Expr, freevars: &[ty::Freevar]) { for freevar in freevars.iter() { - let cmt_var = self.cat_captured_var(closure_expr.id, - closure_expr.span, - freevar.def); - let mode = copy_or_move(self.tcx(), cmt_var.ty, - self.param_env, CaptureMove); + let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, + closure_expr.span, + freevar.def)); + let mode = copy_or_move(self.typer, &cmt_var, CaptureMove); self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode); } } @@ -1257,11 +1270,11 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { closure_id: ast::NodeId, closure_span: Span, upvar_def: def::Def) - -> mc::cmt<'tcx> { + -> mc::McResult> { // Create the cmt for the variable being borrowed, from the // caller's perspective let var_id = upvar_def.def_id().node; - let var_ty = self.typer.node_ty(var_id); + let var_ty = try!(self.typer.node_ty(var_id)); self.mc.cat_def(closure_id, closure_span, var_ty, upvar_def) } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 70942a950e324..dbe005ea63d5f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -75,7 +75,7 @@ use middle::def; use middle::region; use middle::ty::{mod, Ty}; use util::nodemap::{NodeMap}; -use util::ppaux::{ty_to_string, Repr}; +use util::ppaux::{Repr}; use syntax::ast::{MutImmutable, MutMutable}; use syntax::ast; @@ -264,6 +264,8 @@ pub struct MemCategorizationContext<'t,TYPER:'t> { impl<'t,TYPER:'t> Copy for MemCategorizationContext<'t,TYPER> {} +pub type McResult = Result; + /// The `Typer` trait provides the interface for the mem-categorization /// module to the results of the type check. It can be used to query /// the type assigned to an expression node, to inquire after adjustments, @@ -282,8 +284,10 @@ impl<'t,TYPER:'t> Copy for MemCategorizationContext<'t,TYPER> {} /// can be sure that only `Ok` results will occur. pub trait Typer<'tcx> : ty::UnboxedClosureTyper<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; - fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx>; - fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx>; + fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>; + fn node_ty(&self, id: ast::NodeId) -> McResult>; + fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult>; + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool; fn node_method_ty(&self, method_call: ty::MethodCall) -> Option>; fn node_method_origin(&self, method_call: ty::MethodCall) -> Option>; @@ -382,22 +386,22 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { self.typer.tcx() } - fn expr_ty(&self, expr: &ast::Expr) -> Ty<'tcx> { + fn expr_ty(&self, expr: &ast::Expr) -> McResult> { self.typer.node_ty(expr.id) } - fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> { - let unadjusted_ty = self.expr_ty(expr); - ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty, - self.typer.adjustments().borrow().get(&expr.id), - |method_call| self.typer.node_method_ty(method_call)) + fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult> { + let unadjusted_ty = try!(self.expr_ty(expr)); + Ok(ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty, + self.typer.adjustments().borrow().get(&expr.id), + |method_call| self.typer.node_method_ty(method_call))) } - fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { + fn node_ty(&self, id: ast::NodeId) -> McResult> { self.typer.node_ty(id) } - fn pat_ty(&self, pat: &ast::Pat) -> Ty<'tcx> { + fn pat_ty(&self, pat: &ast::Pat) -> McResult> { let tcx = self.typer.tcx(); let base_ty = self.typer.node_ty(pat.id); // FIXME (Issue #18207): This code detects whether we are @@ -420,7 +424,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ret_ty } - pub fn cat_expr(&self, expr: &ast::Expr) -> cmt<'tcx> { + pub fn cat_expr(&self, expr: &ast::Expr) -> McResult> { match self.typer.adjustments().borrow().get(&expr.id) { None => { // No adjustments. @@ -434,8 +438,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { expr.repr(self.tcx())); // Convert a bare fn to a closure by adding NULL env. // Result is an rvalue. - let expr_ty = self.expr_ty_adjusted(expr); - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + let expr_ty = try!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ty::AdjustDerefRef( @@ -445,8 +449,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { expr.repr(self.tcx())); // Equivalent to &*expr or something similar. // Result is an rvalue. - let expr_ty = self.expr_ty_adjusted(expr); - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + let expr_ty = try!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ty::AdjustDerefRef( @@ -463,46 +467,46 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint) - -> cmt<'tcx> { - let mut cmt = self.cat_expr_unadjusted(expr); + -> McResult> { + let mut cmt = try!(self.cat_expr_unadjusted(expr)); debug!("cat_expr_autoderefd: autoderefs={}, cmt={}", autoderefs, cmt.repr(self.tcx())); for deref in range(1u, autoderefs + 1) { - cmt = self.cat_deref(expr, cmt, deref, false); + cmt = try!(self.cat_deref(expr, cmt, deref, false)); } - return cmt; + return Ok(cmt); } - pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt<'tcx> { + pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> McResult> { debug!("cat_expr: id={} expr={}", expr.id, expr.repr(self.tcx())); - let expr_ty = self.expr_ty(expr); + let expr_ty = try!(self.expr_ty(expr)); match expr.node { ast::ExprUnary(ast::UnDeref, ref e_base) => { - let base_cmt = self.cat_expr(&**e_base); + let base_cmt = try!(self.cat_expr(&**e_base)); self.cat_deref(expr, base_cmt, 0, false) } ast::ExprField(ref base, f_name) => { - let base_cmt = self.cat_expr(&**base); + let base_cmt = try!(self.cat_expr(&**base)); debug!("cat_expr(cat_field): id={} expr={} base={}", expr.id, expr.repr(self.tcx()), base_cmt.repr(self.tcx())); - self.cat_field(expr, base_cmt, f_name.node.name, expr_ty) + Ok(self.cat_field(expr, base_cmt, f_name.node.name, expr_ty)) } ast::ExprTupField(ref base, idx) => { - let base_cmt = self.cat_expr(&**base); - self.cat_tup_field(expr, base_cmt, idx.node, expr_ty) + let base_cmt = try!(self.cat_expr(&**base)); + Ok(self.cat_tup_field(expr, base_cmt, idx.node, expr_ty)) } ast::ExprIndex(ref base, ref idx) => { match idx.node { ast::ExprRange(..) => { // Slicing syntax special case (KILLME). - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } _ => { let method_call = ty::MethodCall::expr(expr.id()); @@ -517,7 +521,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ret_ty), 1, true) } None => { - self.cat_index(expr, self.cat_expr(&**base)) + self.cat_index(expr, try!(self.cat_expr(&**base))) } } } @@ -545,7 +549,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprInlineAsm(..) | ast::ExprBox(..) | ast::ExprForLoop(..) => { - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ast::ExprIfLet(..) => { @@ -562,43 +566,43 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { span: Span, expr_ty: Ty<'tcx>, def: def::Def) - -> cmt<'tcx> { + -> McResult> { debug!("cat_def: id={} expr={} def={}", id, expr_ty.repr(self.tcx()), def); match def { def::DefStruct(..) | def::DefVariant(..) | def::DefFn(..) | def::DefStaticMethod(..) | def::DefConst(..) => { - self.cat_rvalue_node(id, span, expr_ty) + Ok(self.cat_rvalue_node(id, span, expr_ty)) } def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) | def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) | def::DefTyParam(..) | def::DefTyParamBinder(..) | def::DefRegion(_) | def::DefLabel(_) | def::DefSelfTy(..) | def::DefMethod(..) | def::DefAssociatedTy(..) | def::DefAssociatedPath(..)=> { - Rc::new(cmt_ { + Ok(Rc::new(cmt_ { id:id, span:span, cat:cat_static_item, mutbl: McImmutable, ty:expr_ty, note: NoteNone - }) + })) } def::DefStatic(_, mutbl) => { - Rc::new(cmt_ { + Ok(Rc::new(cmt_ { id:id, span:span, cat:cat_static_item, mutbl: if mutbl { McDeclared } else { McImmutable}, ty:expr_ty, note: NoteNone - }) + })) } def::DefUpvar(var_id, fn_node_id, _) => { - let ty = self.node_ty(fn_node_id); + let ty = try!(self.node_ty(fn_node_id)); match ty.sty { ty::ty_closure(ref closure_ty) => { // Translate old closure type info into unboxed @@ -635,14 +639,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { } def::DefLocal(vid) => { - Rc::new(cmt_ { + Ok(Rc::new(cmt_ { id: id, span: span, cat: cat_local(vid), mutbl: MutabilityCategory::from_local(self.tcx(), vid), ty: expr_ty, note: NoteNone - }) + })) } } } @@ -657,7 +661,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { kind: ty::UnboxedClosureKind, mode: ast::CaptureClause, is_unboxed: bool) - -> cmt<'tcx> { + -> McResult> { // An upvar can have up to 3 components. The base is a // `cat_upvar`. Next, we add a deref through the implicit // environment pointer with an anonymous free region 'env and @@ -679,7 +683,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { // FnOnce | copied | upvar -> &'up bk // old stack | N/A | upvar -> &'env mut -> &'up bk // old proc/once | copied | N/A - let var_ty = self.node_ty(var_id); + let var_ty = try!(self.node_ty(var_id)); let upvar_id = ty::UpvarId { var_id: var_id, closure_expr_id: fn_node_id }; @@ -727,7 +731,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }); // First, switch by capture mode - match mode { + Ok(match mode { ast::CaptureByValue => { let mut base = cmt_ { id: id, @@ -809,7 +813,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { note: NoteUpvarRef(upvar_id) }) } - } + }) } pub fn cat_rvalue_node(&self, @@ -882,7 +886,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { base_cmt: cmt<'tcx>, deref_cnt: uint, implicit: bool) - -> cmt<'tcx> { + -> McResult> { let adjustment = match self.typer.adjustments().borrow().get(&node.id()) { Some(adj) if ty::adjust_is_object(adj) => ty::AutoObject, _ if deref_cnt != 0 => ty::AutoDeref(deref_cnt), @@ -896,7 +900,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { let method_ty = self.typer.node_method_ty(method_call); debug!("cat_deref: method_call={} method_ty={}", - method_call, method_ty.map(|ty| ty.repr(self.tcx()))); + method_call, method_ty.map(|ty| ty.repr(self.tcx()))); let base_cmt = match method_ty { Some(method_ty) => { @@ -922,8 +926,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { deref_cnt: uint, deref_ty: Ty<'tcx>, implicit: bool) - -> cmt<'tcx> { - let (m, cat) = match deref_kind(self.tcx(), base_cmt.ty) { + -> McResult> + { + let (m, cat) = match try!(deref_kind(base_cmt.ty)) { deref_ptr(ptr) => { let ptr = if implicit { match ptr { @@ -943,20 +948,20 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { (base_cmt.mutbl.inherit(), cat_interior(base_cmt, interior)) } }; - Rc::new(cmt_ { + Ok(Rc::new(cmt_ { id: node.id(), span: node.span(), cat: cat, mutbl: m, ty: deref_ty, note: NoteNone - }) + })) } pub fn cat_index(&self, elt: &N, mut base_cmt: cmt<'tcx>) - -> cmt<'tcx> { + -> McResult> { //! Creates a cmt for an indexing operation (`[]`). //! //! One subtle aspect of indexing that may not be @@ -1021,15 +1026,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { fn deref_vec(&self, elt: &N, base_cmt: cmt<'tcx>) - -> cmt<'tcx> { - match deref_kind(self.tcx(), base_cmt.ty) { + -> McResult> + { + match try!(deref_kind(base_cmt.ty)) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr); // the deref is explicit in the resulting cmt - Rc::new(cmt_ { + Ok(Rc::new(cmt_ { id:elt.id(), span:elt.span(), cat:cat_deref(base_cmt.clone(), 0, ptr), @@ -1039,11 +1045,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { None => self.tcx().sess.bug("Found non-derefable type") }, note: NoteNone - }) + })) } deref_interior(_) => { - base_cmt + Ok(base_cmt) } } } @@ -1058,13 +1064,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { pub fn cat_slice_pattern(&self, vec_cmt: cmt<'tcx>, slice_pat: &ast::Pat) - -> (cmt<'tcx>, ast::Mutability, ty::Region) { - let slice_ty = self.node_ty(slice_pat.id); + -> McResult<(cmt<'tcx>, ast::Mutability, ty::Region)> { + let slice_ty = try!(self.node_ty(slice_pat.id)); let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(), slice_pat, slice_ty); - let cmt_slice = self.cat_index(slice_pat, self.deref_vec(slice_pat, vec_cmt)); - return (cmt_slice, slice_mutbl, slice_r); + let cmt_slice = try!(self.cat_index(slice_pat, try!(self.deref_vec(slice_pat, vec_cmt)))); + return Ok((cmt_slice, slice_mutbl, slice_r)); /// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b, /// ..ref c], then the type of `ref c` will be `&&[]`, so to extract the slice details we @@ -1119,15 +1125,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }) } - pub fn cat_pattern(&self, cmt: cmt<'tcx>, pat: &ast::Pat, mut op: F) where - F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat), + pub fn cat_pattern(&self, cmt: cmt<'tcx>, pat: &ast::Pat, mut op: F) -> McResult<()> + where F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat), { self.cat_pattern_(cmt, pat, &mut op) } // FIXME(#19596) This is a workaround, but there should be a better way to do this - fn cat_pattern_(&self, cmt: cmt<'tcx>, pat: &ast::Pat, op: &mut F) where - F: FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat), + fn cat_pattern_(&self, cmt: cmt<'tcx>, pat: &ast::Pat, op: &mut F) + -> McResult<()> + where F : FnMut(&MemCategorizationContext<'t, TYPER>, cmt<'tcx>, &ast::Pat), { // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. @@ -1208,29 +1215,29 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { Some(&def::DefVariant(..)) => { // variant(x, y, z) for (i, subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(&**subpat); // see (*2) + let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern_(subcmt, &**subpat, op); + try!(self.cat_pattern_(subcmt, &**subpat, op)); } } Some(&def::DefStruct(..)) => { for (i, subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(&**subpat); // see (*2) + let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2) let cmt_field = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern_(cmt_field, &**subpat, op); + try!(self.cat_pattern_(cmt_field, &**subpat, op)); } } Some(&def::DefConst(..)) => { for subpat in subpats.iter() { - self.cat_pattern_(cmt.clone(), &**subpat, op); + try!(self.cat_pattern_(cmt.clone(), &**subpat, op)); } } _ => { @@ -1242,7 +1249,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { } ast::PatIdent(_, _, Some(ref subpat)) => { - self.cat_pattern_(cmt, &**subpat, op); + try!(self.cat_pattern_(cmt, &**subpat, op)); } ast::PatIdent(_, _, None) => { @@ -1252,42 +1259,42 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::PatStruct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for fp in field_pats.iter() { - let field_ty = self.pat_ty(&*fp.node.pat); // see (*2) + let field_ty = try!(self.pat_ty(&*fp.node.pat)); // see (*2) let cmt_field = self.cat_field(pat, cmt.clone(), fp.node.ident.name, field_ty); - self.cat_pattern_(cmt_field, &*fp.node.pat, op); + try!(self.cat_pattern_(cmt_field, &*fp.node.pat, op)); } } ast::PatTup(ref subpats) => { // (p1, ..., pN) for (i, subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(&**subpat); // see (*2) + let subpat_ty = try!(self.pat_ty(&**subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt.clone(), subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern_(subcmt, &**subpat, op); + try!(self.cat_pattern_(subcmt, &**subpat, op)); } } ast::PatBox(ref subpat) | ast::PatRegion(ref subpat) => { // @p1, ~p1, ref p1 - let subcmt = self.cat_deref(pat, cmt, 0, false); - self.cat_pattern_(subcmt, &**subpat, op); + let subcmt = try!(self.cat_deref(pat, cmt, 0, false)); + try!(self.cat_pattern_(subcmt, &**subpat, op)); } ast::PatVec(ref before, ref slice, ref after) => { - let elt_cmt = self.cat_index(pat, self.deref_vec(pat, cmt)); + let elt_cmt = try!(self.cat_index(pat, try!(self.deref_vec(pat, cmt)))); for before_pat in before.iter() { - self.cat_pattern_(elt_cmt.clone(), &**before_pat, op); + try!(self.cat_pattern_(elt_cmt.clone(), &**before_pat, op)); } for slice_pat in slice.iter() { - let slice_ty = self.pat_ty(&**slice_pat); + let slice_ty = try!(self.pat_ty(&**slice_pat)); let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty); - self.cat_pattern_(slice_cmt, &**slice_pat, op); + try!(self.cat_pattern_(slice_cmt, &**slice_pat, op)); } for after_pat in after.iter() { - self.cat_pattern_(elt_cmt.clone(), &**after_pat, op); + try!(self.cat_pattern_(elt_cmt.clone(), &**after_pat, op)); } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 999bc23c27049..079972eff6791 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5708,7 +5708,10 @@ pub fn unboxed_closure_upvars<'tcx>(typer: &mc::Typer<'tcx>, freevars.iter() .map(|freevar| { let freevar_def_id = freevar.def.def_id(); - let freevar_ty = typer.node_ty(freevar_def_id.node); + let freevar_ty = match typer.node_ty(freevar_def_id.node) { + Ok(t) => { t } + Err(()) => { return None; } + }; let freevar_ty = freevar_ty.subst(tcx, substs); match capture_mode { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19ec85dc61eae..189798a1390c2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -289,12 +289,17 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> { self.ccx.tcx } fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { + fn node_ty(&self, id: ast::NodeId) -> McResult> { let ty = self.node_ty(id); - self.infcx().resolve_type_vars_if_possible(&ty) + self.resolve_type_vars_or_error(&ty) } - fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> { - let ty = self.expr_ty_adjusted(expr); - self.infcx().resolve_type_vars_if_possible(&ty) + fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult> { + let ty = self.adjust_expr_ty(expr, self.inh.adjustments.borrow().get(&expr.id)); + self.resolve_type_vars_or_error(&ty) + } + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { + let ty = self.infcx().resolve_type_vars_if_possible(&ty); + traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span) } fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { @@ -1671,6 +1676,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.ccx.tcx.sess.err_count() - self.err_count_on_creation } + /// Resolves all type variables in `t` and then, if any were left + /// unresolved, substitutes an error type. This is used after the + /// main checking when doing a second pass before writeback. The + /// justification is that writeback will produce an error for + /// these unconstrained type variables. + fn resolve_type_vars_or_error(&self, t: &Ty<'tcx>) -> mc::McResult> { + let t = self.infcx().resolve_type_vars_if_possible(t); + if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) } + } + pub fn tag(&self) -> String { format!("{}", self as *const FnCtxt) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index d01b79068aa2c..0631b0edb4fa7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -140,6 +140,10 @@ use std::collections::hash_map::Entry::{Vacant, Occupied}; use self::RepeatingScope::Repeating; use self::SubjectNode::Subject; +// a variation on try that just returns unit +macro_rules! ignore_err { + ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) +} /////////////////////////////////////////////////////////////////////////// // PUBLIC ENTRY POINTS @@ -1027,7 +1031,7 @@ fn constrain_callee(rcx: &mut Rcx, // While we're here, link the closure's region with a unique // immutable borrow (gathered later in borrowck) let mc = mc::MemCategorizationContext::new(rcx.fcx); - let expr_cmt = mc.cat_expr(callee_expr); + let expr_cmt = ignore_err!(mc.cat_expr(callee_expr)); link_region(rcx, callee_expr.span, call_region, ty::UniqueImmBorrow, expr_cmt); r @@ -1136,7 +1140,7 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, }; { let mc = mc::MemCategorizationContext::new(rcx.fcx); - let self_cmt = mc.cat_expr_autoderefd(deref_expr, i); + let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i)); link_region(rcx, deref_expr.span, *r, ty::BorrowKind::from_mutbl(m), self_cmt); } @@ -1232,7 +1236,7 @@ fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr, let cmt = { let mc = mc::MemCategorizationContext::new(rcx.fcx); - mc.cat_expr(base) + ignore_err!(mc.cat_expr(base)) }; link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt); } @@ -1247,7 +1251,7 @@ fn link_local(rcx: &Rcx, local: &ast::Local) { Some(ref expr) => &**expr, }; let mc = mc::MemCategorizationContext::new(rcx.fcx); - let discr_cmt = mc.cat_expr(init_expr); + let discr_cmt = ignore_err!(mc.cat_expr(init_expr)); link_pattern(rcx, mc, discr_cmt, &*local.pat); } @@ -1257,7 +1261,7 @@ fn link_local(rcx: &Rcx, local: &ast::Local) { fn link_match(rcx: &Rcx, discr: &ast::Expr, arms: &[ast::Arm]) { debug!("regionck::for_match()"); let mc = mc::MemCategorizationContext::new(rcx.fcx); - let discr_cmt = mc.cat_expr(discr); + let discr_cmt = ignore_err!(mc.cat_expr(discr)); debug!("discr_cmt={}", discr_cmt.repr(rcx.tcx())); for arm in arms.iter() { for root_pat in arm.pats.iter() { @@ -1303,11 +1307,14 @@ fn link_pattern<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, // `[_, ..slice, _]` pattern ast::PatVec(_, Some(ref slice_pat), _) => { - let (slice_cmt, slice_mutbl, slice_r) = - mc.cat_slice_pattern(sub_cmt, &**slice_pat); - link_region(rcx, sub_pat.span, slice_r, - ty::BorrowKind::from_mutbl(slice_mutbl), - slice_cmt); + match mc.cat_slice_pattern(sub_cmt, &**slice_pat) { + Ok((slice_cmt, slice_mutbl, slice_r)) => { + link_region(rcx, sub_pat.span, slice_r, + ty::BorrowKind::from_mutbl(slice_mutbl), + slice_cmt); + } + Err(()) => {} + } } _ => {} } @@ -1323,7 +1330,7 @@ fn link_autoref(rcx: &Rcx, debug!("link_autoref(autoref={})", autoref); let mc = mc::MemCategorizationContext::new(rcx.fcx); - let expr_cmt = mc.cat_expr_autoderefd(expr, autoderefs); + let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs)); debug!("expr_cmt={}", expr_cmt.repr(rcx.tcx())); match *autoref { @@ -1345,7 +1352,7 @@ fn link_by_ref(rcx: &Rcx, debug!("link_by_ref(expr={}, callee_scope={})", expr.repr(tcx), callee_scope); let mc = mc::MemCategorizationContext::new(rcx.fcx); - let expr_cmt = mc.cat_expr(expr); + let expr_cmt = ignore_err!(mc.cat_expr(expr)); let borrow_region = ty::ReScope(callee_scope); link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt); } From 9c54d862b858399c2990f9eaba963fb19bfa9c6a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 03:54:45 -0500 Subject: [PATCH 2/8] Stop calling `bug()` in various weird cases and instead generate `Err()`. --- src/librustc/middle/mem_categorization.rs | 56 +++++++++-------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index dbe005ea63d5f..59a9b73e978e6 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -195,51 +195,39 @@ pub enum deref_kind { // Categorizes a derefable type. Note that we include vectors and strings as // derefable (we model an index as the combination of a deref and then a // pointer adjustment). -pub fn opt_deref_kind(t: Ty) -> Option { +pub fn deref_kind(t: Ty) -> McResult { match t.sty { ty::ty_uniq(_) | ty::ty_closure(box ty::ClosureTy {store: ty::UniqTraitStore, ..}) => { - Some(deref_ptr(Unique)) + Ok(deref_ptr(Unique)) } ty::ty_rptr(r, mt) => { let kind = ty::BorrowKind::from_mutbl(mt.mutbl); - Some(deref_ptr(BorrowedPtr(kind, *r))) + Ok(deref_ptr(BorrowedPtr(kind, *r))) } ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(r, _), .. }) => { - Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r))) + Ok(deref_ptr(BorrowedPtr(ty::ImmBorrow, r))) } ty::ty_ptr(ref mt) => { - Some(deref_ptr(UnsafePtr(mt.mutbl))) + Ok(deref_ptr(UnsafePtr(mt.mutbl))) } ty::ty_enum(..) | ty::ty_struct(..) => { // newtype - Some(deref_interior(InteriorField(PositionalField(0)))) + Ok(deref_interior(InteriorField(PositionalField(0)))) } ty::ty_vec(_, _) | ty::ty_str => { - Some(deref_interior(InteriorElement(element_kind(t)))) + Ok(deref_interior(InteriorElement(element_kind(t)))) } - _ => None - } -} - -pub fn deref_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> deref_kind { - debug!("deref_kind {}", ty_to_string(tcx, t)); - match opt_deref_kind(t) { - Some(k) => k, - None => { - tcx.sess.bug( - format!("deref_kind() invoked on non-derefable type {}", - ty_to_string(tcx, t))[]); - } + _ => Err(()), } } @@ -403,7 +391,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { fn pat_ty(&self, pat: &ast::Pat) -> McResult> { let tcx = self.typer.tcx(); - let base_ty = self.typer.node_ty(pat.id); + let base_ty = try!(self.typer.node_ty(pat.id)); // FIXME (Issue #18207): This code detects whether we are // looking at a `ref x`, and if so, figures out what the type // *being borrowed* is. But ideally we would put in a more @@ -413,15 +401,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. - ty::deref(base_ty, false).unwrap_or_else(|| { - panic!("encountered BindByRef with non &-type"); - }).ty + match ty::deref(base_ty, false) { + Some(t) => t.ty, + None => { return Err(()); } + } } _ => base_ty, }; debug!("pat_ty(pat={}) base_ty={} ret_ty={}", pat.repr(tcx), base_ty.repr(tcx), ret_ty.repr(tcx)); - ret_ty + Ok(ret_ty) } pub fn cat_expr(&self, expr: &ast::Expr) -> McResult> { @@ -909,13 +898,13 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { } None => base_cmt }; - match ty::deref(base_cmt.ty, true) { + let base_cmt_ty = base_cmt.ty; + match ty::deref(base_cmt_ty, true) { Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty, implicit), None => { - self.tcx().sess.span_bug( - node.span(), - format!("Explicit deref of non-derefable type: {}", - base_cmt.ty.repr(self.tcx()))[]); + debug!("Explicit deref of non-derefable type: {}", + base_cmt_ty.repr(self.tcx())); + return Err(()); } } } @@ -992,17 +981,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { match ty::array_element_ty(self.tcx(), base_cmt.ty) { Some(ty) => ty, None => { - self.tcx().sess.span_bug( - elt.span(), - format!("Explicit index of non-index type `{}`", - base_cmt.ty.repr(self.tcx()))[]); + return Err(()); } } } }; let m = base_cmt.mutbl.inherit(); - return interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty); + return Ok(interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty)); fn interior<'tcx, N: ast_node>(elt: &N, of_cmt: cmt<'tcx>, From 429d9cce1b4b1f94672981986f049b52b341c9f7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 03:57:50 -0500 Subject: [PATCH 3/8] Be more tolerant of errors in EUV so we can run it during typeck. --- src/librustc/middle/expr_use_visitor.rs | 58 +++++++++++++++++-------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 06815266266a5..7dbf797e7392c 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -673,6 +673,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } } } + ty::ty_err => { } _ => { let overloaded_call_type = match self.typer.node_method_origin(MethodCall::expr(call.id)) { @@ -792,9 +793,17 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { ty::struct_fields(self.tcx(), did, substs) } _ => { - self.tcx().sess.span_bug( - with_expr.span, - "with expression doesn't evaluate to a struct"); + // the base expression should always evaluate to a + // struct; however, when EUV is run during typeck, it + // may not. This will generate an error earlier in typeck, + // so we can just ignore it. + if !self.tcx().sess.has_errors() { + self.tcx().sess.span_bug( + with_expr.span, + "with expression doesn't evaluate to a struct"); + } + assert!(self.tcx().sess.has_errors()); + vec!() } }; @@ -1004,8 +1013,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { debug!("determine_pat_move_mode cmt_discr={} pat={}", cmt_discr.repr(self.tcx()), pat.repr(self.tcx())); return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| { - let tcx = self.typer.tcx(); - let def_map = &self.typer.tcx().def_map; + let tcx = self.tcx(); + let def_map = &self.tcx().def_map; if pat_util::pat_is_binding(def_map, pat) { match pat.node { ast::PatIdent(ast::BindByRef(_), _, _) => @@ -1041,7 +1050,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { let mc = &self.mc; let typer = self.typer; - let def_map = &self.typer.tcx().def_map; + let def_map = &self.tcx().def_map; let delegate = &mut self.delegate; return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| { if pat_util::pat_is_binding(def_map, pat) { @@ -1058,8 +1067,12 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // Each match binding is effectively an assignment to the // binding being produced. let def = def_map.borrow()[pat.id].clone(); - let binding_cmt = mc.cat_def(pat.id, pat.span, pat_ty, def); - delegate.mutate(pat.id, pat.span, binding_cmt, Init); + match mc.cat_def(pat.id, pat.span, pat_ty, def) { + Ok(binding_cmt) => { + delegate.mutate(pat.id, pat.span, binding_cmt, Init); + } + Err(_) => { } + } // It is also a borrow or copy/move of the value being matched. match pat.node { @@ -1080,7 +1093,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { delegate.consume_pat(pat, cmt_pat, mode); } _ => { - typer.tcx().sess.span_bug( + tcx.sess.span_bug( pat.span, "binding pattern not an identifier"); } @@ -1181,17 +1194,29 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { // An enum's type -- should never be in a // pattern. - let msg = format!("Pattern has unexpected type: {}", def); - tcx.sess.span_bug(pat.span, msg[]) + if !tcx.sess.has_errors() { + let msg = format!("Pattern has unexpected type: {} and type {}", + def, + cmt_pat.ty.repr(tcx)); + tcx.sess.span_bug(pat.span, msg[]) + } } Some(def) => { // Remaining cases are e.g. DefFn, to // which identifiers within patterns - // should not resolve. - - let msg = format!("Pattern has unexpected def: {}", def); - tcx.sess.span_bug(pat.span, msg[]) + // should not resolve. However, we do + // encouter this when using the + // expr-use-visitor during typeck. So just + // ignore it, an error should have been + // reported. + + if !tcx.sess.has_errors() { + let msg = format!("Pattern has unexpected def: {} and type {}", + def, + cmt_pat.ty.repr(tcx)); + tcx.sess.span_bug(pat.span, msg[]) + } } } } @@ -1217,8 +1242,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { fn walk_captures(&mut self, closure_expr: &ast::Expr) { debug!("walk_captures({})", closure_expr.repr(self.tcx())); - let tcx = self.typer.tcx(); - ty::with_freevars(tcx, closure_expr.id, |freevars| { + ty::with_freevars(self.tcx(), closure_expr.id, |freevars| { match self.tcx().capture_mode(closure_expr.id) { ast::CaptureByRef => { self.walk_by_ref_captures(closure_expr, freevars); From 83ef3042de889db07b819fc6b2802cd0ace7e21c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:01:30 -0500 Subject: [PATCH 4/8] Modify `type_known_to_meet_builtin_bound` so that it doesn't suppress overflow, which should always result in an error. NB. Some of the hunks in this commit rely on a later commit which adds `tcx` into `param_env` and modifies `ParameterEnvironment` to implement `Typer`. --- src/librustc/lint/builtin.rs | 10 +- src/librustc/middle/check_match.rs | 4 +- src/librustc/middle/check_rvalues.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 25 ++--- src/librustc/middle/intrinsicck.rs | 12 ++- src/librustc/middle/traits/error_reporting.rs | 8 +- src/librustc/middle/traits/mod.rs | 91 ++++++++++++++++--- src/librustc/middle/ty.rs | 50 +++++----- src/librustc_trans/trans/_match.rs | 5 +- src/librustc_trans/trans/common.rs | 4 + src/librustc_trans/trans/datum.rs | 5 +- src/librustc_typeck/check/mod.rs | 8 +- src/librustc_typeck/coherence/mod.rs | 2 +- 13 files changed, 149 insertions(+), 77 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index c314fba91d59e..53249c724627e 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1612,15 +1612,11 @@ impl LintPass for MissingCopyImplementations { } _ => return, }; - let parameter_environment = ty::empty_parameter_environment(); - if !ty::type_moves_by_default(cx.tcx, - ty, - ¶meter_environment) { + let parameter_environment = ty::empty_parameter_environment(cx.tcx); + if !ty::type_moves_by_default(¶meter_environment, item.span, ty) { return } - if ty::can_type_implement_copy(cx.tcx, - ty, - ¶meter_environment).is_ok() { + if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() { cx.span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, "type could implement `Copy`; consider adding `impl \ diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d16224ec5b8ac..7952c99dd79b6 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -1032,9 +1032,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, match p.node { ast::PatIdent(ast::BindByValue(_), _, ref sub) => { let pat_ty = ty::node_id_to_type(tcx, p.id); - if ty::type_moves_by_default(tcx, - pat_ty, - &cx.param_env) { + if ty::type_moves_by_default(&cx.param_env, pat.span, pat_ty) { check_move(p, sub.as_ref().map(|p| &**p)); } } diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 3b4ea5234f43f..1a7a642f82afc 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -60,7 +60,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> { cmt: mc::cmt<'tcx>, _: euv::ConsumeMode) { debug!("consume; cmt: {}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty)); - if !ty::type_is_sized(self.tcx, cmt.ty, self.param_env) { + if !ty::type_is_sized(self.param_env, span, cmt.ty) { span_err!(self.tcx.sess, span, E0161, "cannot move a value of type {0}: the size of {0} cannot be statically determined", ty_to_string(self.tcx, cmt.ty)); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7dbf797e7392c..b2d866cf5861d 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -367,10 +367,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { consume_id: ast::NodeId, consume_span: Span, cmt: mc::cmt<'tcx>) { - let mode = copy_or_move(self.tcx(), - cmt.ty, - self.param_env, - DirectRefMove); + let mode = copy_or_move(self.typer, &cmt, DirectRefMove); self.delegate.consume(consume_id, consume_span, cmt, mode); } @@ -1020,10 +1017,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { ast::PatIdent(ast::BindByRef(_), _, _) => mode.lub(BorrowingMatch), ast::PatIdent(ast::BindByValue(_), _, _) => { - match copy_or_move(tcx, - cmt_pat.ty, - self.param_env, - PatBindingMove) { + match copy_or_move(self.typer, &cmt_pat, PatBindingMove) { Copy => mode.lub(CopyingMatch), Move(_) => mode.lub(MovingMatch), } @@ -1085,10 +1079,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { r, bk, RefBinding); } ast::PatIdent(ast::BindByValue(_), _, _) => { - let mode = copy_or_move(typer.tcx(), - cmt_pat.ty, - param_env, - PatBindingMove); + let mode = copy_or_move(typer, &cmt_pat, PatBindingMove); debug!("walk_pat binding consuming pat"); delegate.consume_pat(pat, cmt_pat, mode); } @@ -1303,12 +1294,12 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { } } -fn copy_or_move<'tcx>(tcx: &ty::ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>, +fn copy_or_move<'tcx>(typer: &mc::Typer<'tcx>, + cmt: &mc::cmt<'tcx>, move_reason: MoveReason) - -> ConsumeMode { - if ty::type_moves_by_default(tcx, ty, param_env) { + -> ConsumeMode +{ + if typer.type_moves_by_default(cmt.span, cmt.ty) { Move(move_reason) } else { Copy diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index df06b3b7789c9..89ffcbf37a938 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -170,6 +170,7 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { let mut substs = param_env.free_substs.clone(); self.with_each_combination( + span, param_env, param_env.free_substs.types.iter_enumerated(), &mut substs, @@ -187,7 +188,8 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { } fn with_each_combination(&self, - param_env: &ty::ParameterEnvironment<'tcx>, + span: Span, + param_env: &ty::ParameterEnvironment<'a,'tcx>, mut types_in_scope: EnumeratedItems>, substs: &mut Substs<'tcx>, callback: &mut FnMut(&Substs<'tcx>)) @@ -210,15 +212,17 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { debug!("with_each_combination: space={}, index={}, param_ty={}", space, index, param_ty.repr(self.tcx)); - if !ty::type_is_sized(self.tcx, param_ty, param_env) { + if !ty::type_is_sized(param_env, span, param_ty) { debug!("with_each_combination: param_ty is not known to be sized"); substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty; - self.with_each_combination(param_env, types_in_scope.clone(), substs, callback); + self.with_each_combination(span, param_env, types_in_scope.clone(), + substs, callback); } substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty; - self.with_each_combination(param_env, types_in_scope, substs, callback); + self.with_each_combination(span, param_env, types_in_scope, + substs, callback); } } } diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index a0413701abcad..05ea2f9a7d258 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -77,7 +77,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, "overflow evaluating the requirement `{}`", predicate.user_string(infcx.tcx)).as_slice()); - suggest_new_overflow_limit(infcx, obligation.cause.span); + suggest_new_overflow_limit(infcx.tcx, obligation.cause.span); note_obligation_cause(infcx, obligation); } @@ -332,10 +332,10 @@ fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, } } -pub fn suggest_new_overflow_limit(infcx: &InferCtxt, span: Span) { - let current_limit = infcx.tcx.sess.recursion_limit.get(); +pub fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) { + let current_limit = tcx.sess.recursion_limit.get(); let suggested_limit = current_limit * 2; - infcx.tcx.sess.span_note( + tcx.sess.span_note( span, format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index c83898bcd8ade..1a1d52a047c84 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -15,6 +15,7 @@ pub use self::FulfillmentErrorCode::*; pub use self::Vtable::*; pub use self::ObligationCauseCode::*; +use middle::mem_categorization::Typer; use middle::subst; use middle::ty::{mod, Ty}; use middle::infer::InferCtxt; @@ -22,9 +23,10 @@ use std::slice::Iter; use std::rc::Rc; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; -use util::ppaux::Repr; +use util::ppaux::{Repr, UserString}; pub use self::error_reporting::report_fulfillment_errors; +pub use self::error_reporting::suggest_new_overflow_limit; pub use self::coherence::orphan_check; pub use self::coherence::OrphanCheckErr; pub use self::fulfill::{FulfillmentContext, RegionObligation}; @@ -288,11 +290,12 @@ pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>, /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). -pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, - ty: Ty<'tcx>, - bound: ty::BuiltinBound) - -> bool +pub fn evaluate_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, + typer: &ty::UnboxedClosureTyper<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound, + span: Span) + -> SelectionResult<'tcx, ()> { debug!("type_known_to_meet_builtin_bound(ty={}, bound={})", ty.repr(infcx.tcx), @@ -300,17 +303,49 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, let mut fulfill_cx = FulfillmentContext::new(); - // We can use dummy values here because we won't report any errors - // that result nor will we pay any mind to region obligations that arise - // (there shouldn't really be any anyhow). - let cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID); + // We can use a dummy node-id here because we won't pay any mind + // to region obligations that arise (there shouldn't really be any + // anyhow). + let cause = ObligationCause::misc(span, ast::DUMMY_NODE_ID); fulfill_cx.register_builtin_bound(infcx, ty, bound, cause); // Note: we only assume something is `Copy` if we can // *definitively* show that it implements `Copy`. Otherwise, // assume it is move; linear is always ok. - let result = fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx).is_ok(); + let result = match fulfill_cx.select_all_or_error(infcx, typer) { + Ok(()) => Ok(Some(())), // Success, we know it implements Copy. + Err(errors) => { + // Check if overflow occurred anywhere and propagate that. + if errors.iter().any( + |err| match err.code { CodeSelectionError(Overflow) => true, _ => false }) + { + return Err(Overflow); + } + + // Otherwise, if there were any hard errors, propagate an + // arbitrary one of those. If no hard errors at all, + // report ambiguity. + let sel_error = + errors.iter() + .filter_map(|err| { + match err.code { + CodeAmbiguity => None, + CodeSelectionError(ref e) => Some(e.clone()), + CodeProjectionError(_) => { + infcx.tcx.sess.span_bug( + span, + "projection error while selecting?") + } + } + }) + .next(); + match sel_error { + None => { Ok(None) } + Some(e) => { Err(e) } + } + } + }; debug!("type_known_to_meet_builtin_bound: ty={} bound={} result={}", ty.repr(infcx.tcx), @@ -320,6 +355,40 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, result } +pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, + typer: &ty::UnboxedClosureTyper<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound, + span: Span) + -> bool +{ + match evaluate_builtin_bound(infcx, typer, ty, bound, span) { + Ok(Some(())) => { + // definitely impl'd + true + } + Ok(None) => { + // ambiguous: if coherence check was successful, shouldn't + // happen, but we might have reported an error and been + // soldering on, so just treat this like not implemented + false + } + Err(Overflow) => { + infcx.tcx.sess.span_err( + span, + format!("overflow evaluating whether `{}` is `{}`", + ty.user_string(infcx.tcx), + bound.user_string(infcx.tcx))[]); + suggest_new_overflow_limit(infcx.tcx, span); + false + } + Err(_) => { + // other errors: not implemented. + false + } + } +} + impl<'tcx,O> Obligation<'tcx,O> { pub fn new(cause: ObligationCause<'tcx>, trait_ref: O) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 079972eff6791..fd32e798689ed 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3548,11 +3548,12 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, } } - let infcx = infer::new_infer_ctxt(cx); - let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound); + let infcx = infer::new_infer_ctxt(param_env.tcx); + + let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound, span); debug!("type_impls_bound({}, {}) = {}", - ty_to_string(cx, ty), + ty.repr(param_env.tcx), bound, is_impld); @@ -3564,20 +3565,22 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, is_impld } -pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> bool +pub fn type_moves_by_default<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, + span: Span, + ty: Ty<'tcx>) + -> bool { - !type_impls_bound(cx, &cx.type_impls_copy_cache, param_env, ty, ty::BoundCopy) + let tcx = param_env.tcx; + !type_impls_bound(param_env, &tcx.type_impls_copy_cache, ty, ty::BoundCopy, span) } -pub fn type_is_sized<'tcx>(cx: &ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> bool +pub fn type_is_sized<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, + span: Span, + ty: Ty<'tcx>) + -> bool { - type_impls_bound(cx, &cx.type_impls_sized_cache, param_env, ty, ty::BoundSized) + let tcx = param_env.tcx; + type_impls_bound(param_env, &tcx.type_impls_sized_cache, ty, ty::BoundSized, span) } pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { @@ -6562,6 +6565,10 @@ impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { -> ast::CaptureClause { self.capture_modes.borrow()[closure_expr_id].clone() } + + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { + type_moves_by_default(self, span, ty) + } } impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> { @@ -6944,15 +6951,18 @@ pub enum CopyImplementationError { TypeIsStructural, } -pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>, - self_type: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> Result<(),CopyImplementationError> { +pub fn can_type_implement_copy<'a,'tcx>(param_env: &ParameterEnvironment<'a, 'tcx>, + span: Span, + self_type: Ty<'tcx>) + -> Result<(),CopyImplementationError> +{ + let tcx = param_env.tcx; + match self_type.sty { ty::ty_struct(struct_did, substs) => { let fields = ty::struct_fields(tcx, struct_did, substs); for field in fields.iter() { - if type_moves_by_default(tcx, field.mt.ty, param_env) { + if type_moves_by_default(param_env, span, field.mt.ty) { return Err(FieldDoesNotImplementCopy(field.name)) } } @@ -6963,9 +6973,7 @@ pub fn can_type_implement_copy<'tcx>(tcx: &ctxt<'tcx>, for variant_arg_type in variant.args.iter() { let substd_arg_type = variant_arg_type.subst(tcx, substs); - if type_moves_by_default(tcx, - substd_arg_type, - param_env) { + if type_moves_by_default(param_env, span, substd_arg_type) { return Err(VariantDoesNotImplementCopy(variant.name)) } } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index fc68d1d3258e1..0b1dfcb378ba0 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -1327,9 +1327,8 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat, let trmode; match bm { ast::BindByValue(_) - if !ty::type_moves_by_default(tcx, - variable_ty, - ¶m_env) || reassigned => { + if !ty::type_moves_by_default(¶m_env, span, variable_ty) || reassigned => + { llmatch = alloca_no_lifetime(bcx, llvariable_ty.ptr_to(), "__llmatch"); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index aa88224088063..660b7aeeb001b 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -625,6 +625,10 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { -> ast::CaptureClause { self.tcx().capture_modes.borrow()[closure_expr_id].clone() } + + fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { + self.param_env().type_moves_by_default(span, ty) + } } impl<'blk, 'tcx> ty::UnboxedClosureTyper<'tcx> for BlockS<'blk, 'tcx> { diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index 83bf06383a89c..0afe3a8ef8d4e 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -543,8 +543,9 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> { * affine values (since they must never be duplicated). */ - let param_env = ty::empty_parameter_environment(); - assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty, ¶m_env)); + assert!(!ty::type_moves_by_default(&ty::empty_parameter_environment(bcx.tcx()), + DUMMY_SP, + self.ty)); self.shallow_copy_raw(bcx, dst) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 189798a1390c2..37ec24ae23b46 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1462,7 +1462,7 @@ fn check_cast(fcx: &FnCtxt, return } - if !fcx.type_is_known_to_be_sized(t_1) { + if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) { let tstr = fcx.infcx().ty_to_string(t_1); fcx.type_error_message(span, |actual| { format!("cast to unsized type: `{}` as `{}`", actual, tstr) @@ -1981,13 +1981,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn type_is_known_to_be_sized(&self, - ty: Ty<'tcx>) + ty: Ty<'tcx>, + span: Span) -> bool { traits::type_known_to_meet_builtin_bound(self.infcx(), self.param_env(), ty, - ty::BoundSized) + ty::BoundSized, + span) } pub fn register_builtin_bound(&self, diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 79e1efa618f53..bb308198330f3 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -482,7 +482,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("check_implementations_of_copy: self_type={} (free)", self_type.repr(tcx)); - match ty::can_type_implement_copy(tcx, self_type, ¶m_env) { + match ty::can_type_implement_copy(¶m_env, span, self_type) { Ok(()) => {} Err(ty::FieldDoesNotImplementCopy(name)) => { tcx.sess From 7474be066022d2cd0b0695a466651570c00f3700 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:09:35 -0500 Subject: [PATCH 5/8] Make `ty::ParameterEnvironment`, not `ty::ctxt`, implement `Typer` and `UnboxedClosureTyper`. This requires adding a `tcx` field to `ParameterEnvironment` but generally simplifies everything since we only need to pass along an `UnboxedClosureTyper` or `Typer`. --- src/librustc/middle/check_match.rs | 7 +- src/librustc/middle/check_rvalues.rs | 4 +- src/librustc/middle/check_static.rs | 10 +- src/librustc/middle/expr_use_visitor.rs | 9 +- src/librustc/middle/intrinsicck.rs | 2 +- src/librustc/middle/mem_categorization.rs | 144 +++++++++--------- src/librustc/middle/traits/coherence.rs | 4 +- src/librustc/middle/traits/fulfill.rs | 12 +- src/librustc/middle/traits/select.rs | 23 ++- src/librustc/middle/ty.rs | 123 ++++++++++----- src/librustc_borrowck/borrowck/check_loans.rs | 6 +- .../borrowck/gather_loans/mod.rs | 10 +- src/librustc_borrowck/borrowck/mod.rs | 70 +++++---- src/librustc_trans/trans/_match.rs | 9 +- src/librustc_trans/trans/adt.rs | 5 +- src/librustc_trans/trans/base.rs | 4 +- src/librustc_trans/trans/common.rs | 50 +++--- src/librustc_trans/trans/datum.rs | 1 + src/librustc_trans/trans/monomorphize.rs | 7 +- src/librustc_typeck/check/assoc.rs | 3 +- src/librustc_typeck/check/method/mod.rs | 4 +- src/librustc_typeck/check/method/probe.rs | 4 +- src/librustc_typeck/check/mod.rs | 28 ++-- src/librustc_typeck/check/vtable.rs | 8 +- 24 files changed, 288 insertions(+), 259 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 7952c99dd79b6..9cb8674c3e1b7 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -99,7 +99,7 @@ impl<'a> FromIterator> for Matrix<'a> { pub struct MatchCheckCtxt<'a, 'tcx: 'a> { pub tcx: &'a ty::ctxt<'tcx>, - pub param_env: ParameterEnvironment<'tcx>, + pub param_env: ParameterEnvironment<'a, 'tcx>, } #[deriving(Clone, PartialEq)] @@ -148,7 +148,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> { pub fn check_crate(tcx: &ty::ctxt) { visit::walk_crate(&mut MatchCheckCtxt { tcx: tcx, - param_env: ty::empty_parameter_environment(), + param_env: ty::empty_parameter_environment(tcx), }, tcx.map.krate()); tcx.sess.abort_if_errors(); } @@ -1061,8 +1061,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, cx: cx, }; let mut visitor = ExprUseVisitor::new(&mut checker, - checker.cx.tcx, - &cx.param_env); + &checker.cx.param_env); visitor.walk_expr(guard); } diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 1a7a642f82afc..c383b1579ef84 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> { { let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_env }; - let mut euv = euv::ExprUseVisitor::new(&mut delegate, self.tcx, ¶m_env); + let mut euv = euv::ExprUseVisitor::new(&mut delegate, ¶m_env); euv.walk_fn(fd, b); } visit::walk_fn(self, fk, fd, b, s) @@ -50,7 +50,7 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> { struct RvalueContextDelegate<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, - param_env: &'a ty::ParameterEnvironment<'tcx>, + param_env: &'a ty::ParameterEnvironment<'a,'tcx>, } impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> { diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index fb20df020acf8..9c9e68002c980 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -54,7 +54,7 @@ struct CheckStaticVisitor<'a, 'tcx: 'a> { } struct GlobalVisitor<'a,'b,'tcx:'a+'b>( - euv::ExprUseVisitor<'a,'b,'tcx,ty::ctxt<'tcx>>); + euv::ExprUseVisitor<'a,'b,'tcx,ty::ParameterEnvironment<'b,'tcx>>); struct GlobalChecker { static_consumptions: NodeSet, const_borrows: NodeSet, @@ -70,8 +70,8 @@ pub fn check_crate(tcx: &ty::ctxt) { static_local_borrows: NodeSet::new(), }; { - let param_env = ty::empty_parameter_environment(); - let visitor = euv::ExprUseVisitor::new(&mut checker, tcx, ¶m_env); + let param_env = ty::empty_parameter_environment(tcx); + let visitor = euv::ExprUseVisitor::new(&mut checker, ¶m_env); visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate()); } visit::walk_crate(&mut CheckStaticVisitor { @@ -121,8 +121,8 @@ impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> { let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic); fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); - let env = ty::empty_parameter_environment(); - match fulfill_cx.select_all_or_error(&infcx, &env, self.tcx) { + let env = ty::empty_parameter_environment(self.tcx); + match fulfill_cx.select_all_or_error(&infcx, &env) { Ok(()) => { }, Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index b2d866cf5861d..f5cf4af1230a4 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -23,7 +23,7 @@ use self::OverloadedCallType::*; use middle::{def, region, pat_util}; use middle::mem_categorization as mc; use middle::mem_categorization::Typer; -use middle::ty::{mod, ParameterEnvironment, Ty}; +use middle::ty::{mod}; use middle::ty::{MethodCall, MethodObject, MethodTraitObject}; use middle::ty::{MethodOrigin, MethodParam, MethodTypeParam}; use middle::ty::{MethodStatic, MethodStaticUnboxedClosure}; @@ -299,7 +299,8 @@ pub struct ExprUseVisitor<'d,'t,'tcx:'t,TYPER:'t> { typer: &'t TYPER, mc: mc::MemCategorizationContext<'t,TYPER>, delegate: &'d mut (Delegate<'tcx>+'d), - param_env: &'t ParameterEnvironment<'tcx>, +} + // 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 @@ -324,14 +325,12 @@ enum PassArgs { impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { pub fn new(delegate: &'d mut Delegate<'tcx>, - typer: &'t TYPER, - param_env: &'t ParameterEnvironment<'tcx>) + typer: &'t TYPER) -> ExprUseVisitor<'d,'t,'tcx,TYPER> { ExprUseVisitor { typer: typer, mc: mc::MemCategorizationContext::new(typer), delegate: delegate, - param_env: param_env, } } diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 89ffcbf37a938..2962b7b7c8e48 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -41,7 +41,7 @@ struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> { // environments for each function we encounter. When we find a // call to `transmute`, we can check it in the context of the top // of the stack (which ought not to be empty). - param_envs: Vec>, + param_envs: Vec>, // Dummy sized/unsized types that use to substitute for type // parameters in order to estimate how big a type will be for any diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 59a9b73e978e6..722fe82d41c32 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -272,7 +272,6 @@ pub type McResult = Result; /// can be sure that only `Ok` results will occur. pub trait Typer<'tcx> : ty::UnboxedClosureTyper<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; - fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>; fn node_ty(&self, id: ast::NodeId) -> McResult>; fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult>; fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool; @@ -1292,77 +1291,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { self.tcx().sess.span_bug(pat.span, "unexpanded macro"); } } - } - pub fn cmt_to_string(&self, cmt: &cmt_<'tcx>) -> String { - fn upvar_to_string(upvar: &Upvar, is_copy: bool) -> String { - if upvar.is_unboxed { - let kind = match upvar.kind { - ty::FnUnboxedClosureKind => "Fn", - ty::FnMutUnboxedClosureKind => "FnMut", - ty::FnOnceUnboxedClosureKind => "FnOnce" - }; - format!("captured outer variable in an `{}` closure", kind) - } else { - (match (upvar.kind, is_copy) { - (ty::FnOnceUnboxedClosureKind, true) => "captured outer variable in a proc", - _ => "captured outer variable" - }).to_string() - } - } - - match cmt.cat { - cat_static_item => { - "static item".to_string() - } - cat_rvalue(..) => { - "non-lvalue".to_string() - } - cat_local(vid) => { - match self.tcx().map.find(vid) { - Some(ast_map::NodeArg(_)) => { - "argument".to_string() - } - _ => "local variable".to_string() - } - } - cat_deref(_, _, pk) => { - let upvar = cmt.upvar(); - match upvar.as_ref().map(|i| &i.cat) { - Some(&cat_upvar(ref var)) => { - upvar_to_string(var, false) - } - Some(_) => unreachable!(), - None => { - match pk { - Implicit(..) => { - "dereference (dereference is implicit, due to indexing)".to_string() - } - Unique => format!("dereference of `{}`", ptr_sigil(pk)), - _ => format!("dereference of `{}`-pointer", ptr_sigil(pk)) - } - } - } - } - cat_interior(_, InteriorField(NamedField(_))) => { - "field".to_string() - } - cat_interior(_, InteriorField(PositionalField(_))) => { - "anonymous field".to_string() - } - cat_interior(_, InteriorElement(VecElement)) => { - "vec content".to_string() - } - cat_interior(_, InteriorElement(OtherElement)) => { - "indexed content".to_string() - } - cat_upvar(ref var) => { - upvar_to_string(var, true) - } - cat_downcast(ref cmt, _) => { - self.cmt_to_string(&**cmt) - } - } + Ok(()) } } @@ -1474,6 +1404,78 @@ impl<'tcx> cmt_<'tcx> { NoteNone => None } } + + + pub fn descriptive_string(&self, tcx: &ty::ctxt) -> String { + fn upvar_to_string(upvar: &Upvar, is_copy: bool) -> String { + if upvar.is_unboxed { + let kind = match upvar.kind { + ty::FnUnboxedClosureKind => "Fn", + ty::FnMutUnboxedClosureKind => "FnMut", + ty::FnOnceUnboxedClosureKind => "FnOnce" + }; + format!("captured outer variable in an `{}` closure", kind) + } else { + (match (upvar.kind, is_copy) { + (ty::FnOnceUnboxedClosureKind, true) => "captured outer variable in a proc", + _ => "captured outer variable" + }).to_string() + } + } + + match self.cat { + cat_static_item => { + "static item".to_string() + } + cat_rvalue(..) => { + "non-lvalue".to_string() + } + cat_local(vid) => { + match tcx.map.find(vid) { + Some(ast_map::NodeArg(_)) => { + "argument".to_string() + } + _ => "local variable".to_string() + } + } + cat_deref(_, _, pk) => { + let upvar = self.upvar(); + match upvar.as_ref().map(|i| &i.cat) { + Some(&cat_upvar(ref var)) => { + upvar_to_string(var, false) + } + Some(_) => unreachable!(), + None => { + match pk { + Implicit(..) => { + "dereference (dereference is implicit, due to indexing)".to_string() + } + Unique => format!("dereference of `{}`", ptr_sigil(pk)), + _ => format!("dereference of `{}`-pointer", ptr_sigil(pk)) + } + } + } + } + cat_interior(_, InteriorField(NamedField(_))) => { + "field".to_string() + } + cat_interior(_, InteriorField(PositionalField(_))) => { + "anonymous field".to_string() + } + cat_interior(_, InteriorElement(VecElement)) => { + "vec content".to_string() + } + cat_interior(_, InteriorElement(OtherElement)) => { + "indexed content".to_string() + } + cat_upvar(ref var) => { + upvar_to_string(var, true) + } + cat_downcast(ref cmt, _) => { + cmt.descriptive_string(tcx) + } + } + } } impl<'tcx> Repr<'tcx> for cmt_<'tcx> { diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 4aff36c262495..6a8b6d49cc0c5 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -42,8 +42,8 @@ pub fn impl_can_satisfy(infcx: &InferCtxt, // Determine whether `impl2` can provide an implementation for those // same types. - let param_env = ty::empty_parameter_environment(); - let mut selcx = SelectionContext::intercrate(infcx, ¶m_env, infcx.tcx); + let param_env = ty::empty_parameter_environment(infcx.tcx); + let mut selcx = SelectionContext::intercrate(infcx, ¶m_env); let obligation = Obligation::new(ObligationCause::dummy(), ty::Binder(ty::TraitPredicate { trait_ref: Rc::new(impl1_trait_ref), diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 2dbb15b215ed0..e8a22e3d1d8f8 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -109,7 +109,6 @@ impl<'tcx> FulfillmentContext<'tcx> { /// `projection_ty` again. pub fn normalize_projection_type<'a>(&mut self, infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, typer: &ty::UnboxedClosureTyper<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>) @@ -122,7 +121,7 @@ impl<'tcx> FulfillmentContext<'tcx> { // FIXME(#20304) -- cache - let mut selcx = SelectionContext::new(infcx, param_env, typer); + let mut selcx = SelectionContext::new(infcx, typer); let normalized = project::normalize_projection_type(&mut selcx, projection_ty, cause, 0); for obligation in normalized.obligations.into_iter() { @@ -186,11 +185,10 @@ impl<'tcx> FulfillmentContext<'tcx> { pub fn select_all_or_error<'a>(&mut self, infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, typer: &ty::UnboxedClosureTyper<'tcx>) -> Result<(),Vec>> { - try!(self.select_where_possible(infcx, param_env, typer)); + try!(self.select_where_possible(infcx, typer)); // Anything left is ambiguous. let errors: Vec = @@ -212,21 +210,19 @@ impl<'tcx> FulfillmentContext<'tcx> { /// results in `O(n^2)` performance (#18208). pub fn select_new_obligations<'a>(&mut self, infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, typer: &ty::UnboxedClosureTyper<'tcx>) -> Result<(),Vec>> { - let mut selcx = SelectionContext::new(infcx, param_env, typer); + let mut selcx = SelectionContext::new(infcx, typer); self.select(&mut selcx, true) } pub fn select_where_possible<'a>(&mut self, infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, typer: &ty::UnboxedClosureTyper<'tcx>) -> Result<(),Vec>> { - let mut selcx = SelectionContext::new(infcx, param_env, typer); + let mut selcx = SelectionContext::new(infcx, typer); self.select(&mut selcx, false) } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index d09f2a250b033..7da33babaeb60 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -46,7 +46,6 @@ use util::ppaux::Repr; pub struct SelectionContext<'cx, 'tcx:'cx> { infcx: &'cx InferCtxt<'cx, 'tcx>, - param_env: &'cx ty::ParameterEnvironment<'tcx>, closure_typer: &'cx (ty::UnboxedClosureTyper<'tcx>+'cx), /// Freshener used specifically for skolemizing entries on the @@ -181,12 +180,10 @@ enum EvaluationResult<'tcx> { impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, - param_env: &'cx ty::ParameterEnvironment<'tcx>, closure_typer: &'cx ty::UnboxedClosureTyper<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, - param_env: param_env, closure_typer: closure_typer, freshener: infcx.freshener(), intercrate: false, @@ -194,12 +191,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>, - param_env: &'cx ty::ParameterEnvironment<'tcx>, closure_typer: &'cx ty::UnboxedClosureTyper<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, - param_env: param_env, closure_typer: closure_typer, freshener: infcx.freshener(), intercrate: true, @@ -210,14 +205,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx } - pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'tcx> { - self.param_env - } - pub fn tcx(&self) -> &'cx ty::ctxt<'tcx> { self.infcx.tcx } + pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'cx, 'tcx> { + self.closure_typer.param_env() + } + /////////////////////////////////////////////////////////////////////////// // Selection // @@ -650,7 +645,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // it's not worth going to more trouble to increase the // hit-rate I don't think. if self.intercrate { - return &self.param_env.selection_cache; + return &self.param_env().selection_cache; } // If the trait refers to any parameters in scope, then use @@ -659,7 +654,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cache_fresh_trait_pred.0.input_types().iter().any( |&t| ty::type_has_self(t) || ty::type_has_params(t)) { - return &self.param_env.selection_cache; + return &self.param_env().selection_cache; } // If the trait refers to unbound type variables, and there @@ -668,11 +663,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // common case, then we can use the global environment. // See the discussion in doc.rs for more details. if - !self.param_env.caller_bounds.is_empty() && + !self.param_env().caller_bounds.is_empty() && cache_fresh_trait_pred.0.input_types().iter().any( |&t| ty::type_has_ty_infer(t)) { - return &self.param_env.selection_cache; + return &self.param_env().selection_cache; } // Otherwise, we can use the global cache. @@ -902,7 +897,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.repr(self.tcx())); let caller_trait_refs: Vec<_> = - self.param_env.caller_bounds.predicates.iter() + self.param_env().caller_bounds.predicates.iter() .filter_map(|o| o.to_opt_poly_trait_ref()) .collect(); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index fd32e798689ed..c720032bef264 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2058,7 +2058,9 @@ impl<'tcx> TraitRef<'tcx> { /// future I hope to refine the representation of types so as to make /// more distinctions clearer. #[deriving(Clone)] -pub struct ParameterEnvironment<'tcx> { +pub struct ParameterEnvironment<'a, 'tcx:'a> { + pub tcx: &'a ctxt<'tcx>, + /// A substitution that can be applied to move from /// the "outer" view of a type or method to the "inner" view. /// In general, this means converting from bound parameters to @@ -2082,8 +2084,8 @@ pub struct ParameterEnvironment<'tcx> { pub selection_cache: traits::SelectionCache<'tcx>, } -impl<'tcx> ParameterEnvironment<'tcx> { - pub fn for_item(cx: &ctxt<'tcx>, id: NodeId) -> ParameterEnvironment<'tcx> { +impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> { + pub fn for_item(cx: &'a ctxt<'tcx>, id: NodeId) -> ParameterEnvironment<'a, 'tcx> { match cx.map.find(id) { Some(ast_map::NodeImplItem(ref impl_item)) => { match **impl_item { @@ -2272,6 +2274,8 @@ impl UnboxedClosureKind { } pub trait UnboxedClosureTyper<'tcx> { + fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>; + fn unboxed_closure_kind(&self, def_id: ast::DefId) -> ty::UnboxedClosureKind; @@ -2424,6 +2428,21 @@ impl<'tcx> ctxt<'tcx> { self.region_interner.borrow_mut().insert(region, region); region } + + pub fn unboxed_closure_kind(&self, + def_id: ast::DefId) + -> ty::UnboxedClosureKind + { + self.unboxed_closures.borrow()[def_id].kind + } + + pub fn unboxed_closure_type(&self, + def_id: ast::DefId, + substs: &subst::Substs<'tcx>) + -> ty::ClosureTy<'tcx> + { + self.unboxed_closures.borrow()[def_id].closure_type.subst(self, substs) + } } // Interns a type/name combination, stores the resulting box in cx.interner, @@ -3377,7 +3396,8 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { ty_unboxed_closure(did, r, substs) => { // FIXME(#14449): `borrowed_contents` below assumes `&mut` // unboxed closure. - let upvars = unboxed_closure_upvars(cx, did, substs).unwrap(); + let param_env = ty::empty_parameter_environment(cx); + let upvars = unboxed_closure_upvars(¶m_env, did, substs).unwrap(); TypeContents::union(upvars.as_slice(), |f| tc_ty(cx, f.ty, cache)) | borrowed_contents(*r, MutMutable) @@ -3526,12 +3546,12 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } } -fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, - cache: &RefCell,bool>>, - param_env: &ParameterEnvironment<'tcx>, - ty: Ty<'tcx>, - bound: ty::BuiltinBound) - -> bool +fn type_impls_bound<'a,'tcx>(param_env: &ParameterEnvironment<'a,'tcx>, + cache: &RefCell,bool>>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound, + span: Span) + -> bool { assert!(!ty::type_needs_infer(ty)); @@ -3540,7 +3560,7 @@ fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, None => {} Some(&result) => { debug!("type_impls_bound({}, {}) = {} (cached)", - ty_to_string(cx, ty), + ty.repr(param_env.tcx), bound, result); return result @@ -3625,8 +3645,6 @@ pub fn is_instantiable<'tcx>(cx: &ctxt<'tcx>, r_ty: Ty<'tcx>) -> bool { ty_str | ty_bare_fn(..) | ty_closure(_) | - ty_infer(_) | - ty_err | ty_param(_) | ty_projection(_) | ty_vec(_, None) => { @@ -3659,9 +3677,12 @@ pub fn is_instantiable<'tcx>(cx: &ctxt<'tcx>, r_ty: Ty<'tcx>) -> bool { r } - ty_unboxed_closure(did, _, substs) => { - let upvars = unboxed_closure_upvars(cx, did, substs).unwrap(); - upvars.iter().any(|f| type_requires(cx, seen, r_ty, f.ty)) + ty_err | + ty_infer(_) | + ty_unboxed_closure(..) => { + // this check is run on type definitions, so we don't expect to see + // inference by-products or unboxed closure types + cx.sess.bug(format!("requires check invoked on inapplicable type: {}", ty)[]) } ty_tup(ref ts) => { @@ -3751,9 +3772,10 @@ pub fn is_type_representable<'tcx>(cx: &ctxt<'tcx>, sp: Span, ty: Ty<'tcx>) find_nonrepresentable(cx, sp, seen, iter) } - ty_unboxed_closure(did, _, substs) => { - let upvars = unboxed_closure_upvars(cx, did, substs).unwrap(); - find_nonrepresentable(cx, sp, seen, upvars.iter().map(|f| f.ty)) + ty_unboxed_closure(..) => { + // this check is run on type definitions, so we don't expect to see + // unboxed closure types + cx.sess.bug(format!("requires check invoked on inapplicable type: {}", ty)[]) } _ => Representable, } @@ -6377,19 +6399,20 @@ impl Variance { /// Construct a parameter environment suitable for static contexts or other contexts where there /// are no free type/lifetime parameters in scope. -pub fn empty_parameter_environment<'tcx>() -> ParameterEnvironment<'tcx> { - ty::ParameterEnvironment { free_substs: Substs::empty(), +pub fn empty_parameter_environment<'a,'tcx>(cx: &'a ctxt<'tcx>) -> ParameterEnvironment<'a,'tcx> { + ty::ParameterEnvironment { tcx: cx, + free_substs: Substs::empty(), caller_bounds: GenericBounds::empty(), implicit_region_bound: ty::ReEmpty, selection_cache: traits::SelectionCache::new(), } } /// See `ParameterEnvironment` struct def'n for details -pub fn construct_parameter_environment<'tcx>( - tcx: &ctxt<'tcx>, +pub fn construct_parameter_environment<'a,'tcx>( + tcx: &'a ctxt<'tcx>, generics: &ty::Generics<'tcx>, free_id: ast::NodeId) - -> ParameterEnvironment<'tcx> + -> ParameterEnvironment<'a, 'tcx> { // @@ -6432,6 +6455,7 @@ pub fn construct_parameter_environment<'tcx>( bounds.repr(tcx)); return ty::ParameterEnvironment { + tcx: tcx, free_substs: free_substs, implicit_region_bound: ty::ReScope(free_id_scope), caller_bounds: bounds, @@ -6522,48 +6546,59 @@ impl BorrowKind { } } -impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { - fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { - self +impl<'tcx> ctxt<'tcx> { + pub fn capture_mode(&self, closure_expr_id: ast::NodeId) + -> ast::CaptureClause { + self.capture_modes.borrow()[closure_expr_id].clone() + } + + pub fn is_method_call(&self, expr_id: ast::NodeId) -> bool { + self.method_map.borrow().contains_key(&MethodCall::expr(expr_id)) } +} - fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { - ty::node_id_to_type(self, id) +impl<'a,'tcx> mc::Typer<'tcx> for ParameterEnvironment<'a,'tcx> { + fn tcx(&self) -> &ty::ctxt<'tcx> { + self.tcx } - fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> { - ty::expr_ty_adjusted(self, expr) + fn node_ty(&self, id: ast::NodeId) -> mc::McResult> { + Ok(ty::node_id_to_type(self.tcx, id)) + } + + fn expr_ty_adjusted(&self, expr: &ast::Expr) -> mc::McResult> { + Ok(ty::expr_ty_adjusted(self.tcx, expr)) } fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { - self.method_map.borrow().get(&method_call).map(|method| method.ty) + self.tcx.method_map.borrow().get(&method_call).map(|method| method.ty) } fn node_method_origin(&self, method_call: ty::MethodCall) -> Option> { - self.method_map.borrow().get(&method_call).map(|method| method.origin.clone()) + self.tcx.method_map.borrow().get(&method_call).map(|method| method.origin.clone()) } - fn adjustments<'a>(&'a self) -> &'a RefCell>> { - &self.adjustments + fn adjustments(&self) -> &RefCell>> { + &self.tcx.adjustments } fn is_method_call(&self, id: ast::NodeId) -> bool { - self.method_map.borrow().contains_key(&MethodCall::expr(id)) + self.tcx.is_method_call(id) } fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { - self.region_maps.temporary_scope(rvalue_id) + self.tcx.region_maps.temporary_scope(rvalue_id) } fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option { - Some(self.upvar_borrow_map.borrow()[upvar_id].clone()) + Some(self.tcx.upvar_borrow_map.borrow()[upvar_id].clone()) } fn capture_mode(&self, closure_expr_id: ast::NodeId) -> ast::CaptureClause { - self.capture_modes.borrow()[closure_expr_id].clone() + self.tcx.capture_mode(closure_expr_id) } fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { @@ -6571,12 +6606,16 @@ impl<'tcx> mc::Typer<'tcx> for ty::ctxt<'tcx> { } } -impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> { +impl<'a,'tcx> UnboxedClosureTyper<'tcx> for ty::ParameterEnvironment<'a,'tcx> { + fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> { + self + } + fn unboxed_closure_kind(&self, def_id: ast::DefId) -> ty::UnboxedClosureKind { - self.unboxed_closures.borrow()[def_id].kind + self.tcx.unboxed_closure_kind(def_id) } fn unboxed_closure_type(&self, @@ -6584,7 +6623,7 @@ impl<'tcx> UnboxedClosureTyper<'tcx> for ty::ctxt<'tcx> { substs: &subst::Substs<'tcx>) -> ty::ClosureTy<'tcx> { - self.unboxed_closures.borrow()[def_id].closure_type.subst(self, substs) + self.tcx.unboxed_closure_type(def_id, substs) } fn unboxed_closure_upvars(&self, diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 2062685f4c866..17a3f4a88e5c0 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -87,7 +87,7 @@ struct CheckLoanCtxt<'a, 'tcx: 'a> { dfcx_loans: &'a LoanDataFlow<'a, 'tcx>, move_data: move_data::FlowedMoveData<'a, 'tcx>, all_loans: &'a [Loan<'tcx>], - param_env: &'a ty::ParameterEnvironment<'tcx>, + param_env: &'a ty::ParameterEnvironment<'a, 'tcx>, } impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> { @@ -208,9 +208,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, }; { - let mut euv = euv::ExprUseVisitor::new(&mut clcx, - bccx.tcx, - ¶m_env); + let mut euv = euv::ExprUseVisitor::new(&mut clcx, ¶m_env); euv.walk_fn(decl, body); } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 7854c8acb4796..1e9e5b22aa0ed 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -51,9 +51,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); { - let mut euv = euv::ExprUseVisitor::new(&mut glcx, - bccx.tcx, - ¶m_env); + let mut euv = euv::ExprUseVisitor::new(&mut glcx, ¶m_env); euv.walk_fn(decl, body); } @@ -485,13 +483,15 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { /// This visitor walks static initializer's expressions and makes /// sure the loans being taken are sound. struct StaticInitializerCtxt<'a, 'tcx: 'a> { - bccx: &'a BorrowckCtxt<'a, 'tcx> + bccx: &'a BorrowckCtxt<'a, 'tcx>, } impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> { fn visit_expr(&mut self, ex: &Expr) { if let ast::ExprAddrOf(mutbl, ref base) = ex.node { - let base_cmt = self.bccx.cat_expr(&**base); + let param_env = ty::empty_parameter_environment(self.bccx.tcx); + let mc = mc::MemCategorizationContext::new(¶m_env); + let base_cmt = mc.cat_expr(&**base).unwrap(); let borrow_kind = ty::BorrowKind::from_mutbl(mutbl); // Check that we don't allow borrows of unsafe static items. if check_aliasability(self.bccx, ex.span, euv::AddrOf, diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index c27b7b30e1345..d81974d1ae038 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -511,14 +511,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { self.tcx.region_maps.is_subregion_of(r_sub, r_sup) } - pub fn mc(&self) -> mc::MemCategorizationContext<'a, ty::ctxt<'tcx>> { - mc::MemCategorizationContext::new(self.tcx) - } - - pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt<'tcx> { - self.mc().cat_expr(expr) - } - pub fn report(&self, err: BckError<'tcx>) { self.span_err( err.span, @@ -526,13 +518,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { self.note_and_explain_bckerr(err); } - pub fn report_use_of_moved_value(&self, - use_span: Span, - use_kind: MovedValueUseKind, - lp: &LoanPath<'tcx>, - the_move: &move_data::Move, - moved_lp: &LoanPath<'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>) { + pub fn report_use_of_moved_value<'b>(&self, + use_span: Span, + use_kind: MovedValueUseKind, + lp: &LoanPath<'tcx>, + the_move: &move_data::Move, + moved_lp: &LoanPath<'tcx>, + param_env: &ty::ParameterEnvironment<'b,'tcx>) { let verb = match use_kind { MovedInUse => "use", MovedInCapture => "capture", @@ -608,8 +600,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { r)[]) } }; - let (suggestion, _) = move_suggestion(self.tcx, param_env, expr_ty, - ("moved by default", "")); + let (suggestion, _) = + move_suggestion(param_env, expr_span, expr_ty, ("moved by default", "")); self.tcx.sess.span_note( expr_span, format!("`{}` moved here{} because it has type `{}`, which is {}", @@ -646,11 +638,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { r)[]) } }; - let (suggestion, help) = move_suggestion(self.tcx, - param_env, - expr_ty, - ("moved by default", "make a copy and \ - capture that instead to override")); + let (suggestion, help) = + move_suggestion(param_env, + expr_span, + expr_ty, + ("moved by default", + "make a copy and capture that instead to override")); self.tcx.sess.span_note( expr_span, format!("`{}` moved into closure environment here{} because it \ @@ -663,22 +656,27 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } - fn move_suggestion<'tcx>(tcx: &ty::ctxt<'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, - ty: Ty<'tcx>, - default_msgs: (&'static str, &'static str)) - -> (&'static str, &'static str) { + fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>, + span: Span, + ty: Ty<'tcx>, + default_msgs: (&'static str, &'static str)) + -> (&'static str, &'static str) { match ty.sty { ty::ty_closure(box ty::ClosureTy { - store: ty::RegionTraitStore(..), - .. - }) => + store: ty::RegionTraitStore(..), + .. + }) => { ("a non-copyable stack closure", - "capture it in a new closure, e.g. `|x| f(x)`, to override"), - _ if ty::type_moves_by_default(tcx, ty, param_env) => - ("non-copyable", - "perhaps you meant to use `clone()`?"), - _ => default_msgs, + "capture it in a new closure, e.g. `|x| f(x)`, to override") + } + _ => { + if ty::type_moves_by_default(param_env, span, ty) { + ("non-copyable", + "perhaps you meant to use `clone()`?") + } else { + default_msgs + } + } } } } @@ -991,7 +989,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String { - self.mc().cmt_to_string(cmt) + cmt.descriptive_string(self.tcx) } } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 0b1dfcb378ba0..19781a51d578b 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -542,7 +542,7 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>( check_match::Constructor::Variant(def_id) }; - let param_env = ty::empty_parameter_environment(); + let param_env = ty::empty_parameter_environment(bcx.tcx()); let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx(), param_env: param_env, @@ -1008,7 +1008,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx(), - param_env: ty::empty_parameter_environment(), + param_env: ty::empty_parameter_environment(bcx.tcx()), }; let adt_vals = if any_irrefutable_adt_pat(bcx.tcx(), m, col) { let repr = adt::represent_type(bcx.ccx(), left_ty); @@ -1262,8 +1262,7 @@ fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool reassigned: false }; { - let param_env = ty::empty_parameter_environment(); - let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx, ¶m_env); + let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx); visitor.walk_expr(body); } rc.reassigned @@ -1321,7 +1320,7 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat, let variable_ty = node_id_type(bcx, p_id); let llvariable_ty = type_of::type_of(ccx, variable_ty); let tcx = bcx.tcx(); - let param_env = ty::empty_parameter_environment(); + let param_env = ty::empty_parameter_environment(tcx); let llmatch; let trmode; diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 24a3bb42c90f7..85a7ec3306950 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -61,7 +61,7 @@ use trans::datum; use trans::machine; use trans::type_::Type; use trans::type_of; -use middle::ty::{mod, Ty}; +use middle::ty::{mod, Ty, UnboxedClosureTyper}; use middle::ty::Disr; use syntax::ast; use syntax::attr; @@ -168,7 +168,8 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, Univariant(mk_struct(cx, ftys[], packed, t), dtor) } ty::ty_unboxed_closure(def_id, _, substs) => { - let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs).unwrap(); + let typer = NormalizingUnboxedClosureTyper::new(cx.tcx()); + let upvars = typer.unboxed_closure_upvars(def_id, substs).unwrap(); let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); Univariant(mk_struct(cx, upvar_types[], false, t), false) } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index bf53885e9e50d..4c29467d93a44 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -731,7 +731,8 @@ pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, } ty::ty_unboxed_closure(def_id, _, substs) => { let repr = adt::represent_type(cx.ccx(), t); - let upvars = ty::unboxed_closure_upvars(cx.tcx(), def_id, substs).unwrap(); + let typer = common::NormalizingUnboxedClosureTyper::new(cx.tcx()); + let upvars = typer.unboxed_closure_upvars(def_id, substs).unwrap(); for (i, upvar) in upvars.iter().enumerate() { let llupvar = adt::trans_field_ptr(cx, &*repr, data_ptr, 0, i); cx = f(cx, llupvar, upvar.ty); @@ -1451,6 +1452,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, llfn: llfndecl, llenv: None, llretslotptr: Cell::new(None), + param_env: ty::empty_parameter_environment(ccx.tcx()), alloca_insert_pt: Cell::new(None), llreturn: Cell::new(None), needs_ret_allocas: nested_returns, diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 660b7aeeb001b..6efdcc2f0fa0f 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -302,6 +302,9 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // section of the executable we're generating. pub llfn: ValueRef, + // always an empty parameter-environment + pub param_env: ty::ParameterEnvironment<'a, 'tcx>, + // The environment argument in a closure. pub llenv: Option, @@ -579,12 +582,12 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { self.tcx() } - fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { - node_id_type(self, id) + fn node_ty(&self, id: ast::NodeId) -> mc::McResult> { + Ok(node_id_type(self, id)) } - fn expr_ty_adjusted(&self, expr: &ast::Expr) -> Ty<'tcx> { - expr_ty_adjusted(self, expr) + fn expr_ty_adjusted(&self, expr: &ast::Expr) -> mc::McResult> { + Ok(expr_ty_adjusted(self, expr)) } fn node_method_ty(&self, method_call: ty::MethodCall) -> Option> { @@ -627,11 +630,15 @@ impl<'blk, 'tcx> mc::Typer<'tcx> for BlockS<'blk, 'tcx> { } fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool { - self.param_env().type_moves_by_default(span, ty) + self.fcx.param_env.type_moves_by_default(span, ty) } } impl<'blk, 'tcx> ty::UnboxedClosureTyper<'tcx> for BlockS<'blk, 'tcx> { + fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx> { + &self.fcx.param_env + } + fn unboxed_closure_kind(&self, def_id: ast::DefId) -> ty::UnboxedClosureKind @@ -945,14 +952,10 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id()); let infcx = infer::new_infer_ctxt(tcx); - // Parameter environment is used to give details about type parameters, - // but since we are in trans, everything is fully monomorphized. - let param_env = ty::empty_parameter_environment(); - // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx); - let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env, &typer); + let typer = NormalizingUnboxedClosureTyper::new(tcx); + let mut selcx = traits::SelectionContext::new(&infcx, &typer); let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), trait_ref.to_poly_trait_predicate()); let selection = match selcx.select(&obligation) { @@ -987,7 +990,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let vtable = selection.map_move_nested(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - let vtable = drain_fulfillment_cx(span, &infcx, ¶m_env, &mut fulfill_cx, &vtable); + let vtable = drain_fulfillment_cx(span, &infcx, &mut fulfill_cx, &vtable); info!("Cache miss: {}", trait_ref.repr(ccx.tcx())); ccx.trait_cache().borrow_mut().insert(trait_ref, @@ -997,21 +1000,27 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } pub struct NormalizingUnboxedClosureTyper<'a,'tcx:'a> { - tcx: &'a ty::ctxt<'tcx> + param_env: ty::ParameterEnvironment<'a, 'tcx> } impl<'a,'tcx> NormalizingUnboxedClosureTyper<'a,'tcx> { pub fn new(tcx: &'a ty::ctxt<'tcx>) -> NormalizingUnboxedClosureTyper<'a,'tcx> { - NormalizingUnboxedClosureTyper { tcx: tcx } + // Parameter environment is used to give details about type parameters, + // but since we are in trans, everything is fully monomorphized. + NormalizingUnboxedClosureTyper { param_env: ty::empty_parameter_environment(tcx) } } } impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<'a,'tcx> { + fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> { + &self.param_env + } + fn unboxed_closure_kind(&self, def_id: ast::DefId) -> ty::UnboxedClosureKind { - self.tcx.unboxed_closure_kind(def_id) + self.param_env.tcx.unboxed_closure_kind(def_id) } fn unboxed_closure_type(&self, @@ -1021,8 +1030,8 @@ impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<' { // the substitutions in `substs` are already monomorphized, // but we still must normalize associated types - let closure_ty = self.tcx.unboxed_closure_type(def_id, substs); - monomorphize::normalize_associated_type(self.tcx, &closure_ty) + let closure_ty = self.param_env.tcx.unboxed_closure_type(def_id, substs); + monomorphize::normalize_associated_type(self.param_env.tcx, &closure_ty) } fn unboxed_closure_upvars(&self, @@ -1032,14 +1041,13 @@ impl<'a,'tcx> ty::UnboxedClosureTyper<'tcx> for NormalizingUnboxedClosureTyper<' { // the substitutions in `substs` are already monomorphized, // but we still must normalize associated types - let result = ty::unboxed_closure_upvars(self.tcx, def_id, substs); - monomorphize::normalize_associated_type(self.tcx, &result) + let result = ty::unboxed_closure_upvars(&self.param_env, def_id, substs); + monomorphize::normalize_associated_type(self.param_env.tcx, &result) } } pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span, infcx: &infer::InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, fulfill_cx: &mut traits::FulfillmentContext<'tcx>, result: &T) -> T @@ -1052,7 +1060,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span, // contains unbound type parameters. It could be a slight // optimization to stop iterating early. let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx); - match fulfill_cx.select_all_or_error(infcx, param_env, &typer) { + match fulfill_cx.select_all_or_error(infcx, &typer) { Ok(()) => { } Err(errors) => { if errors.iter().all(|e| e.is_overflow()) { diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index 0afe3a8ef8d4e..72074040a2c67 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -28,6 +28,7 @@ use util::ppaux::{ty_to_string}; use std::fmt; use syntax::ast; +use syntax::codemap::DUMMY_SP; /// A `Datum` encapsulates the result of evaluating an expression. It /// describes where the value is stored, what Rust type the value has, diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 7c8ba08d98750..cc259e6765c34 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -322,9 +322,8 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T // FIXME(#20304) -- cache let infcx = infer::new_infer_ctxt(tcx); - let param_env = ty::empty_parameter_environment(); - let typer = NormalizingUnboxedClosureTyper::new(infcx.tcx); - let mut selcx = traits::SelectionContext::new(&infcx, ¶m_env, &typer); + let typer = NormalizingUnboxedClosureTyper::new(tcx); + let mut selcx = traits::SelectionContext::new(&infcx, &typer); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value); @@ -337,7 +336,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T for obligation in obligations.into_iter() { fulfill_cx.register_predicate_obligation(&infcx, obligation); } - let result = drain_fulfillment_cx(DUMMY_SP, &infcx, ¶m_env, &mut fulfill_cx, &result); + let result = drain_fulfillment_cx(DUMMY_SP, &infcx, &mut fulfill_cx, &result); result } diff --git a/src/librustc_typeck/check/assoc.rs b/src/librustc_typeck/check/assoc.rs index 5e9843d0e00bf..6950850e5f3c1 100644 --- a/src/librustc_typeck/check/assoc.rs +++ b/src/librustc_typeck/check/assoc.rs @@ -18,7 +18,6 @@ use syntax::codemap::Span; use util::ppaux::Repr; pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, - param_env: &ty::ParameterEnvironment<'tcx>, typer: &(ty::UnboxedClosureTyper<'tcx>+'a), fulfillment_cx: &mut FulfillmentContext<'tcx>, span: Span, @@ -28,7 +27,7 @@ pub fn normalize_associated_types_in<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, where T : TypeFoldable<'tcx> + HasProjectionTypes + Clone + Repr<'tcx> { debug!("normalize_associated_types_in(value={})", value.repr(infcx.tcx)); - let mut selcx = SelectionContext::new(infcx, param_env, typer); + let mut selcx = SelectionContext::new(infcx, typer); let cause = ObligationCause::new(span, body_id, MiscObligation); let Normalized { value: result, obligations } = traits::normalize(&mut selcx, cause, value); debug!("normalize_associated_types_in: result={} predicates={}", diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 4e6593dedddf7..92b8c2bbcf718 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -169,9 +169,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>, poly_trait_ref.as_predicate()); // Now we want to know if this can be matched - let mut selcx = traits::SelectionContext::new(fcx.infcx(), - &fcx.inh.param_env, - fcx); + let mut selcx = traits::SelectionContext::new(fcx.infcx(), fcx); if !selcx.evaluate_obligation(&obligation) { debug!("--> Cannot match obligation"); return None; // Cannot be matched, no such method resolution is possible. diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 52860abb6f938..cd97d89b2465a 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -788,9 +788,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { debug!("impl_obligations={}", obligations.repr(self.tcx())); // Evaluate those obligations to see if they might possibly hold. - let mut selcx = traits::SelectionContext::new(self.infcx(), - &self.fcx.inh.param_env, - self.fcx); + let mut selcx = traits::SelectionContext::new(self.infcx(), self.fcx); obligations.all(|o| selcx.evaluate_obligation(o)) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 37ec24ae23b46..ed4c55cea5db3 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -87,6 +87,7 @@ use middle::{const_eval, def}; use middle::infer; use middle::lang_items::IteratorItem; use middle::mem_categorization as mc; +use middle::mem_categorization::McResult; use middle::pat_util::{mod, pat_id_map}; use middle::region::CodeExtent; use middle::subst::{mod, Subst, Substs, VecPerParamSpace, ParamSpace}; @@ -146,7 +147,7 @@ mod callee; pub struct Inherited<'a, 'tcx: 'a> { infcx: infer::InferCtxt<'a, 'tcx>, locals: RefCell>>, - param_env: ty::ParameterEnvironment<'tcx>, + param_env: ty::ParameterEnvironment<'a, 'tcx>, // Temporary tables: node_types: RefCell>>, @@ -288,7 +289,6 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> { fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx } - fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> { fn node_ty(&self, id: ast::NodeId) -> McResult> { let ty = self.node_ty(id); self.resolve_type_vars_or_error(&ty) @@ -322,7 +322,7 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> { self.inh.method_map.borrow().contains_key(&ty::MethodCall::expr(id)) } fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option { - self.tcx().temporary_scope(rvalue_id) + self.param_env().temporary_scope(rvalue_id) } fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option { self.inh.upvar_borrow_map.borrow().get(&upvar_id).cloned() @@ -334,6 +334,10 @@ impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> { } impl<'a, 'tcx> ty::UnboxedClosureTyper<'tcx> for FnCtxt<'a, 'tcx> { + fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> { + &self.inh.param_env + } + fn unboxed_closure_kind(&self, def_id: ast::DefId) -> ty::UnboxedClosureKind @@ -360,7 +364,7 @@ impl<'a, 'tcx> ty::UnboxedClosureTyper<'tcx> for FnCtxt<'a, 'tcx> { impl<'a, 'tcx> Inherited<'a, 'tcx> { fn new(tcx: &'a ty::ctxt<'tcx>, - param_env: ty::ParameterEnvironment<'tcx>) + param_env: ty::ParameterEnvironment<'a, 'tcx>) -> Inherited<'a, 'tcx> { Inherited { infcx: infer::new_infer_ctxt(tcx), @@ -388,7 +392,6 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { { let mut fulfillment_cx = self.fulfillment_cx.borrow_mut(); assoc::normalize_associated_types_in(&self.infcx, - &self.param_env, typer, &mut *fulfillment_cx, span, body_id, @@ -418,7 +421,7 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>) -> Inherited<'a, 'tcx> { // It's kind of a kludge to manufacture a fake function context // and statement context, but we might as well do write the code only once - let param_env = ty::empty_parameter_environment(); + let param_env = ty::empty_parameter_environment(ccx.tcx); Inherited::new(ccx.tcx, param_env) } @@ -462,7 +465,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, body: &ast::Block, id: ast::NodeId, raw_fty: Ty<'tcx>, - param_env: ty::ParameterEnvironment<'tcx>) { + param_env: ty::ParameterEnvironment<'a, 'tcx>) { match raw_fty.sty { ty::ty_bare_fn(_, ref fn_ty) => { let inh = Inherited::new(ccx.tcx, param_env); @@ -473,7 +476,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let fn_sig = liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig); let fn_sig = - inh.normalize_associated_types_in(ccx.tcx, body.span, body.id, &fn_sig); + inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig); let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig, decl, id, body, &inh); @@ -1225,7 +1228,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, let impl_sig = assoc::normalize_associated_types_in(&infcx, &impl_param_env, - infcx.tcx, &mut fulfillment_cx, impl_m_span, impl_m_body_id, @@ -1246,7 +1248,6 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, let trait_sig = assoc::normalize_associated_types_in(&infcx, &impl_param_env, - infcx.tcx, &mut fulfillment_cx, impl_m_span, impl_m_body_id, @@ -1282,7 +1283,7 @@ fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, // Run the fulfillment context to completion to accommodate any // associated type normalizations that may have occurred. - match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env, tcx) { + match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env) { Ok(()) => { } Err(errors) => { traits::report_fulfillment_errors(&infcx, &errors); @@ -1660,11 +1661,11 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx } - pub fn infcx(&self) -> &infer::InferCtxt<'a, 'tcx> { + pub fn infcx(&self) -> &infer::InferCtxt<'a,'tcx> { &self.inh.infcx } - pub fn param_env(&self) -> &ty::ParameterEnvironment<'tcx> { + pub fn param_env(&self) -> &ty::ParameterEnvironment<'a,'tcx> { &self.inh.param_env } @@ -1835,7 +1836,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.inh.fulfillment_cx .borrow_mut() .normalize_projection_type(self.infcx(), - &self.inh.param_env, self, ty::ProjectionTy { trait_ref: trait_ref, diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 1ef6c11403218..8566d1f1e12b9 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -287,9 +287,7 @@ pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { fcx.default_type_parameters(); let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut(); - let r = fulfillment_cx.select_all_or_error(fcx.infcx(), - &fcx.inh.param_env, - fcx); + let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx); match r { Ok(()) => { } Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); } @@ -302,7 +300,7 @@ pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) match fcx.inh.fulfillment_cx .borrow_mut() - .select_where_possible(fcx.infcx(), &fcx.inh.param_env, fcx) + .select_where_possible(fcx.infcx(), fcx) { Ok(()) => { } Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); } @@ -316,7 +314,7 @@ pub fn select_new_fcx_obligations(fcx: &FnCtxt) { match fcx.inh.fulfillment_cx .borrow_mut() - .select_new_obligations(fcx.infcx(), &fcx.inh.param_env, fcx) + .select_new_obligations(fcx.infcx(), fcx) { Ok(()) => { } Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); } From ecd9c10e1aa88f2ec3251c33903d9c2622b0bf37 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:12:25 -0500 Subject: [PATCH 6/8] Move upvar checking into its own pre-pass that occurs before regionck and which uses EUV. For now, upvar inference is not any smarter than it ever was, but regionck is simpler because it doesn't have to do as many things at once. --- src/librustc/middle/def.rs | 6 + src/librustc_typeck/check/mod.rs | 2 + src/librustc_typeck/check/regionck.rs | 343 +---------------------- src/librustc_typeck/check/upvar.rs | 373 ++++++++++++++++++++++++++ 4 files changed, 385 insertions(+), 339 deletions(-) create mode 100644 src/librustc_typeck/check/upvar.rs diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs index ff1ee39496626..acbb7d567dcea 100644 --- a/src/librustc/middle/def.rs +++ b/src/librustc/middle/def.rs @@ -126,6 +126,12 @@ impl TraitItemKind { } impl Def { + pub fn local_node_id(&self) -> ast::NodeId { + let def_id = self.def_id(); + assert_eq!(def_id.krate, ast::LOCAL_CRATE); + def_id.node + } + pub fn def_id(&self) -> ast::DefId { match *self { DefFn(id, _) | DefStaticMethod(id, _) | DefMod(id) | diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ed4c55cea5db3..82525b71052d2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -130,6 +130,7 @@ pub mod regionmanip; pub mod regionck; pub mod demand; pub mod method; +mod upvar; pub mod wf; mod closure; mod callee; @@ -482,6 +483,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, decl, id, body, &inh); vtable::select_all_fcx_obligations_or_error(&fcx); + upvar::closure_analyze_fn(&fcx, id, decl, body); regionck::regionck_fn(&fcx, id, decl, body); writeback::resolve_type_vars_in_fn(&fcx, decl, body); } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 0631b0edb4fa7..e97a29ec458c7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -81,38 +81,6 @@ //! traversed. This is essentially the same as the ownership //! relation, except that a borrowed pointer never owns its //! contents. -//! -//! ### Inferring borrow kinds for upvars -//! -//! Whenever there is a closure expression, we need to determine how each -//! upvar is used. We do this by initially assigning each upvar an -//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then -//! "escalating" the kind as needed. The borrow kind proceeds according to -//! the following lattice: -//! -//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow -//! -//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we -//! will promote its borrow kind to mutable borrow. If we see an `&mut x` -//! we'll do the same. Naturally, this applies not just to the upvar, but -//! to everything owned by `x`, so the result is the same for something -//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a -//! struct). These adjustments are performed in -//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code -//! from there). -//! -//! The fact that we are inferring borrow kinds as we go results in a -//! semi-hacky interaction with mem-categorization. In particular, -//! mem-categorization will query the current borrow kind as it -//! categorizes, and we'll return the *current* value, but this may get -//! adjusted later. Therefore, in this module, we generally ignore the -//! borrow kind (and derived mutabilities) that are returned from -//! mem-categorization, since they may be inaccurate. (Another option -//! would be to use a unification scheme, where instead of returning a -//! concrete borrow kind like `ty::ImmBorrow`, we return a -//! `ty::InferBorrow(upvar_id)` or something like that, but this would -//! then mean that all later passes would have to check for these figments -//! and report an error, and it just seems like more mess in the end.) use astconv::AstConv; use check::FnCtxt; @@ -126,7 +94,6 @@ use middle::ty::{ReScope}; use middle::ty::{mod, Ty, MethodCall}; use middle::infer; use middle::pat_util; -use util::nodemap::{FnvHashMap}; use util::ppaux::{ty_to_string, Repr}; use syntax::{ast, ast_util}; @@ -134,9 +101,6 @@ use syntax::codemap::Span; use syntax::visit; use syntax::visit::Visitor; -use std::cell::{RefCell}; -use std::collections::hash_map::Entry::{Vacant, Occupied}; - use self::RepeatingScope::Repeating; use self::SubjectNode::Subject; @@ -197,19 +161,6 @@ pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, /////////////////////////////////////////////////////////////////////////// // INTERNALS -// Stores parameters for a potential call to link_region() -// to perform if an upvar reference is marked unique/mutable after -// it has already been processed before. -struct MaybeLink<'tcx> { - span: Span, - borrow_region: ty::Region, - borrow_kind: ty::BorrowKind, - borrow_cmt: mc::cmt<'tcx> -} - -// A map associating an upvar ID to a vector of the above -type MaybeLinkMap<'tcx> = RefCell>>>; - pub struct Rcx<'a, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'tcx>, @@ -220,10 +171,6 @@ pub struct Rcx<'a, 'tcx: 'a> { // id of AST node being analyzed (the subject of the analysis). subject: SubjectNode, - - // Possible region links we will establish if an upvar - // turns out to be unique/mutable - maybe_links: MaybeLinkMap<'tcx> } /// Returns the validity region of `def` -- that is, how long is `def` valid? @@ -258,8 +205,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { Rcx { fcx: fcx, repeating_scope: initial_repeating_scope, subject: subject, - region_param_pairs: Vec::new(), - maybe_links: RefCell::new(FnvHashMap::new()) } + region_param_pairs: Vec::new() } } pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { @@ -590,19 +536,12 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr); } - ast::ExprAssign(ref lhs, _) => { - adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs); - visit::walk_expr(rcx, expr); - } - ast::ExprAssignOp(_, ref lhs, ref rhs) => { if has_method_map { constrain_call(rcx, expr, Some(&**lhs), Some(&**rhs).into_iter(), true); } - adjust_borrow_kind_for_assignment_lhs(rcx, &**lhs); - visit::walk_expr(rcx, expr); } @@ -838,22 +777,6 @@ fn check_expr_fn_block(rcx: &mut Rcx, visit::walk_expr(rcx, expr); rcx.set_repeating_scope(repeating_scope); - match function_type.sty { - ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => { - ty::with_freevars(tcx, expr.id, |freevars| { - propagate_upupvar_borrow_kind(rcx, expr, freevars); - }) - } - ty::ty_unboxed_closure(..) => { - if tcx.capture_modes.borrow()[expr.id].clone() == ast::CaptureByRef { - ty::with_freevars(tcx, expr.id, |freevars| { - propagate_upupvar_borrow_kind(rcx, expr, freevars); - }); - } - } - _ => {} - } - match function_type.sty { ty::ty_closure(box ty::ClosureTy {ref bounds, ..}) => { ty::with_freevars(tcx, expr.id, |freevars| { @@ -930,7 +853,6 @@ fn check_expr_fn_block(rcx: &mut Rcx, freevars: &[ty::Freevar]) { let tcx = rcx.fcx.ccx.tcx; - let infcx = rcx.fcx.infcx(); debug!("constrain_free_variables({}, {})", region_bound.repr(tcx), expr.repr(tcx)); for freevar in freevars.iter() { @@ -946,20 +868,10 @@ fn check_expr_fn_block(rcx: &mut Rcx, let upvar_id = ty::UpvarId { var_id: var_node_id, closure_expr_id: expr.id }; - // Create a region variable to represent this borrow. This borrow - // must outlive the region on the closure. - let origin = infer::UpvarRegion(upvar_id, expr.span); - let freevar_region = infcx.next_region_var(origin); - rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), - region_bound, freevar_region); + let upvar_borrow = rcx.fcx.inh.upvar_borrow_map.borrow()[upvar_id]; - // Create a UpvarBorrow entry. Note that we begin with a - // const borrow_kind, but change it to either mut or - // immutable as dictated by the uses. - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, - region: freevar_region }; - rcx.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id, - upvar_borrow); + rcx.fcx.mk_subr(infer::FreeVariable(freevar.span, var_node_id), + region_bound, upvar_borrow.region); // Guarantee that the closure does not outlive the variable itself. let enclosing_region = region_of_def(rcx.fcx, def); @@ -968,52 +880,6 @@ fn check_expr_fn_block(rcx: &mut Rcx, region_bound, enclosing_region); } } - - fn propagate_upupvar_borrow_kind(rcx: &mut Rcx, - expr: &ast::Expr, - freevars: &[ty::Freevar]) { - let tcx = rcx.fcx.ccx.tcx; - debug!("propagate_upupvar_borrow_kind({})", expr.repr(tcx)); - for freevar in freevars.iter() { - // Because of the semi-hokey way that we are doing - // borrow_kind inference, we need to check for - // indirect dependencies, like so: - // - // let mut x = 0; - // outer_call(|| { - // inner_call(|| { - // x = 1; - // }); - // }); - // - // Here, the `inner_call` is basically "reborrowing" the - // outer pointer. With no other changes, `inner_call` - // would infer that it requires a mutable borrow, but - // `outer_call` would infer that a const borrow is - // sufficient. This is because we haven't linked the - // borrow_kind of the borrow that occurs in the inner - // closure to the borrow_kind of the borrow in the outer - // closure. Note that regions *are* naturally linked - // because we have a proper inference scheme there. - // - // Anyway, for borrow_kind, we basically go back over now - // after checking the inner closure (and hence - // determining the final borrow_kind) and propagate that as - // a constraint on the outer closure. - if let def::DefUpvar(var_id, outer_closure_id, _) = freevar.def { - // thing being captured is itself an upvar: - let outer_upvar_id = ty::UpvarId { - var_id: var_id, - closure_expr_id: outer_closure_id }; - let inner_upvar_id = ty::UpvarId { - var_id: var_id, - closure_expr_id: expr.id }; - link_upvar_borrow_kind_for_nested_closures(rcx, - inner_upvar_id, - outer_upvar_id); - } - } - } } fn constrain_callee(rcx: &mut Rcx, @@ -1491,14 +1357,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, rcx.fcx.inh.upvar_borrow_map.borrow_mut(); match upvar_borrow_map.get_mut(upvar_id) { Some(upvar_borrow) => { - // Adjust mutability that we infer for the upvar - // so it can accommodate being borrowed with - // mutability `kind`: - adjust_upvar_borrow_kind_for_loan(rcx, - *upvar_id, - upvar_borrow, - borrow_kind); - // The mutability of the upvar may have been modified // by the above adjustment, so update our local variable. ref_kind = upvar_borrow.kind; @@ -1583,27 +1441,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, // else the user is borrowed imm memory as mut memory, // which means they'll get an error downstream in borrowck // anyhow.) - // - // If mutability was inferred from an upvar, we may be - // forced to revisit this decision later if processing - // another borrow or nested closure ends up converting the - // upvar borrow kind to mutable/unique. Record the - // information needed to perform the recursive link in the - // maybe link map. - if let mc::NoteUpvarRef(upvar_id) = note { - let link = MaybeLink { - span: span, - borrow_region: borrow_region, - borrow_kind: new_borrow_kind, - borrow_cmt: ref_cmt - }; - - match rcx.maybe_links.borrow_mut().entry(upvar_id) { - Vacant(entry) => { entry.set(vec![link]); } - Occupied(entry) => { entry.into_mut().push(link); } - } - } - return None; } @@ -1615,178 +1452,6 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, } } -/// Adjusts the inferred borrow_kind as needed to account for upvars that are assigned to in an -/// assignment expression. -fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, - lhs: &ast::Expr) { - let mc = mc::MemCategorizationContext::new(rcx.fcx); - let cmt = mc.cat_expr(lhs); - adjust_upvar_borrow_kind_for_mut(rcx, cmt); -} - -/// Indicates that `cmt` is being directly mutated (e.g., assigned to). If cmt contains any by-ref -/// upvars, this implies that those upvars must be borrowed using an `&mut` borow. -fn adjust_upvar_borrow_kind_for_mut<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, - cmt: mc::cmt<'tcx>) { - let mut cmt = cmt; - loop { - debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", - cmt.repr(rcx.tcx())); - - match cmt.cat.clone() { - mc::cat_deref(base, _, mc::Unique) | - mc::cat_interior(base, _) | - mc::cat_downcast(base, _) => { - // Interior or owned data is mutable if base is - // mutable, so iterate to the base. - cmt = base; - continue; - } - - mc::cat_deref(base, _, mc::BorrowedPtr(..)) | - mc::cat_deref(base, _, mc::Implicit(..)) => { - if let mc::NoteUpvarRef(ref upvar_id) = cmt.note { - // if this is an implicit deref of an - // upvar, then we need to modify the - // borrow_kind of the upvar to make sure it - // is inferred to mutable if necessary - let mut upvar_borrow_map = - rcx.fcx.inh.upvar_borrow_map.borrow_mut(); - let ub = &mut (*upvar_borrow_map)[*upvar_id]; - return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::MutBorrow); - } - - // assignment to deref of an `&mut` - // borrowed pointer implies that the - // pointer itself must be unique, but not - // necessarily *mutable* - return adjust_upvar_borrow_kind_for_unique(rcx, base); - } - - mc::cat_deref(_, _, mc::UnsafePtr(..)) | - mc::cat_static_item | - mc::cat_rvalue(_) | - mc::cat_local(_) | - mc::cat_upvar(..) => { - return; - } - } - } -} - -fn adjust_upvar_borrow_kind_for_unique<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, cmt: mc::cmt<'tcx>) { - let mut cmt = cmt; - loop { - debug!("adjust_upvar_borrow_kind_for_unique(cmt={})", - cmt.repr(rcx.tcx())); - - match cmt.cat.clone() { - mc::cat_deref(base, _, mc::Unique) | - mc::cat_interior(base, _) | - mc::cat_downcast(base, _) => { - // Interior or owned data is unique if base is - // unique. - cmt = base; - continue; - } - - mc::cat_deref(base, _, mc::BorrowedPtr(..)) | - mc::cat_deref(base, _, mc::Implicit(..)) => { - if let mc::NoteUpvarRef(ref upvar_id) = cmt.note { - // if this is an implicit deref of an - // upvar, then we need to modify the - // borrow_kind of the upvar to make sure it - // is inferred to unique if necessary - let mut ub = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); - let ub = &mut (*ub)[*upvar_id]; - return adjust_upvar_borrow_kind(rcx, *upvar_id, ub, ty::UniqueImmBorrow); - } - - // for a borrowed pointer to be unique, its - // base must be unique - return adjust_upvar_borrow_kind_for_unique(rcx, base); - } - - mc::cat_deref(_, _, mc::UnsafePtr(..)) | - mc::cat_static_item | - mc::cat_rvalue(_) | - mc::cat_local(_) | - mc::cat_upvar(..) => { - return; - } - } - } -} - -/// Indicates that the borrow_kind of `outer_upvar_id` must permit a reborrowing with the -/// borrow_kind of `inner_upvar_id`. This occurs in nested closures, see comment above at the call -/// to this function. -fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx, - inner_upvar_id: ty::UpvarId, - outer_upvar_id: ty::UpvarId) { - debug!("link_upvar_borrow_kind: inner_upvar_id={} outer_upvar_id={}", - inner_upvar_id, outer_upvar_id); - - let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); - let inner_borrow = upvar_borrow_map[inner_upvar_id].clone(); - match upvar_borrow_map.get_mut(&outer_upvar_id) { - Some(outer_borrow) => { - adjust_upvar_borrow_kind(rcx, outer_upvar_id, outer_borrow, inner_borrow.kind); - } - None => { /* outer closure is not a stack closure */ } - } -} - -fn adjust_upvar_borrow_kind_for_loan(rcx: &Rcx, - upvar_id: ty::UpvarId, - upvar_borrow: &mut ty::UpvarBorrow, - kind: ty::BorrowKind) { - debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={} kind={} -> {}", - upvar_id, upvar_borrow.kind, kind); - - adjust_upvar_borrow_kind(rcx, upvar_id, upvar_borrow, kind) -} - -/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind -/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed -/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by -/// some particular use. -fn adjust_upvar_borrow_kind(rcx: &Rcx, - upvar_id: ty::UpvarId, - upvar_borrow: &mut ty::UpvarBorrow, - kind: ty::BorrowKind) { - debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})", - upvar_id, upvar_borrow.kind, kind); - - match (upvar_borrow.kind, kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow) | - (ty::ImmBorrow, ty::MutBorrow) | - (ty::UniqueImmBorrow, ty::MutBorrow) => { - upvar_borrow.kind = kind; - - // Check if there are any region links we now need to - // establish due to adjusting the borrow kind of the upvar - match rcx.maybe_links.borrow_mut().entry(upvar_id) { - Occupied(entry) => { - for MaybeLink { span, borrow_region, - borrow_kind, borrow_cmt } in entry.take().into_iter() - { - link_region(rcx, span, borrow_region, borrow_kind, borrow_cmt); - } - } - Vacant(_) => {} - } - } - // Take LHS: - (ty::ImmBorrow, ty::ImmBorrow) | - (ty::UniqueImmBorrow, ty::ImmBorrow) | - (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | - (ty::MutBorrow, _) => { - } - } -} - /// Ensures that all borrowed data reachable via `ty` outlives `region`. fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, origin: infer::SubregionOrigin<'tcx>, diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs new file mode 100644 index 0000000000000..a4c3550fcd676 --- /dev/null +++ b/src/librustc_typeck/check/upvar.rs @@ -0,0 +1,373 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! ### Inferring borrow kinds for upvars +//! +//! Whenever there is a closure expression, we need to determine how each +//! upvar is used. We do this by initially assigning each upvar an +//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then +//! "escalating" the kind as needed. The borrow kind proceeds according to +//! the following lattice: +//! +//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow +//! +//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we +//! will promote its borrow kind to mutable borrow. If we see an `&mut x` +//! we'll do the same. Naturally, this applies not just to the upvar, but +//! to everything owned by `x`, so the result is the same for something +//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a +//! struct). These adjustments are performed in +//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code +//! from there). +//! +//! The fact that we are inferring borrow kinds as we go results in a +//! semi-hacky interaction with mem-categorization. In particular, +//! mem-categorization will query the current borrow kind as it +//! categorizes, and we'll return the *current* value, but this may get +//! adjusted later. Therefore, in this module, we generally ignore the +//! borrow kind (and derived mutabilities) that are returned from +//! mem-categorization, since they may be inaccurate. (Another option +//! would be to use a unification scheme, where instead of returning a +//! concrete borrow kind like `ty::ImmBorrow`, we return a +//! `ty::InferBorrow(upvar_id)` or something like that, but this would +//! then mean that all later passes would have to check for these figments +//! and report an error, and it just seems like more mess in the end.) + +use super::FnCtxt; + +use middle::expr_use_visitor as euv; +use middle::mem_categorization as mc; +use middle::ty::{mod}; +use middle::infer::{InferCtxt, UpvarRegion}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::visit::{mod, Visitor}; +use util::ppaux::Repr; + +/////////////////////////////////////////////////////////////////////////// +// PUBLIC ENTRY POINTS + +pub fn closure_analyze_fn(fcx: &FnCtxt, + _id: ast::NodeId, + decl: &ast::FnDecl, + body: &ast::Block) { + let mut seed = SeedBorrowKind::new(fcx); + seed.visit_block(body); + + let mut adjust = AdjustBorrowKind::new(fcx); + adjust.analyze_fn(decl, body); +} + +/////////////////////////////////////////////////////////////////////////// +// SEED BORROW KIND + +struct SeedBorrowKind<'a,'tcx:'a> { + fcx: &'a FnCtxt<'a,'tcx>, +} + +impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { + fn visit_expr(&mut self, expr: &ast::Expr) { + match expr.node { + ast::ExprClosure(cc, _, _, ref body) => { + self.check_closure(expr, cc, &**body); + } + + _ => { } + } + + visit::walk_expr(self, expr); + } + + fn visit_fn(&mut self, + fn_kind: visit::FnKind<'v>, + decl: &'v ast::FnDecl, + block: &'v ast::Block, + span: Span, + _id: ast::NodeId) + { + match fn_kind { + visit::FkItemFn(..) | visit::FkMethod(..) => { + // ignore nested fn items + } + visit::FkFnBlock => { + visit::walk_fn(self, fn_kind, decl, block, span); + } + } + } +} + +impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { + fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> { + SeedBorrowKind { fcx: fcx } + } + + fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.fcx.tcx() + } + + fn infcx(&self) -> &'a InferCtxt<'a,'tcx> { + self.fcx.infcx() + } + + fn check_closure(&mut self, + expr: &ast::Expr, + capture_clause: ast::CaptureClause, + _body: &ast::Block) + { + let is_old_skool_closure = match self.fcx.expr_ty(expr).sty { + ty::ty_closure(..) => true, + _ => false, + }; + + match capture_clause { + ast::CaptureByValue if !is_old_skool_closure => { + } + _ => { + ty::with_freevars(self.tcx(), expr.id, |freevars| { + for freevar in freevars.iter() { + let var_node_id = freevar.def.local_node_id(); + let upvar_id = ty::UpvarId { var_id: var_node_id, + closure_expr_id: expr.id }; + debug!("seed upvar_id {}", upvar_id); + let origin = UpvarRegion(upvar_id, expr.span); + let freevar_region = self.infcx().next_region_var(origin); + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, + region: freevar_region }; + self.fcx.inh.upvar_borrow_map.borrow_mut().insert(upvar_id, + upvar_borrow); + } + }); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// ADJUST BORROW KIND + +struct AdjustBorrowKind<'a,'tcx:'a> { + fcx: &'a FnCtxt<'a,'tcx> +} + +impl<'a,'tcx> AdjustBorrowKind<'a,'tcx>{ + fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> { + AdjustBorrowKind { fcx: fcx } + } + + fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.fcx.tcx() + } + + fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) { + /*! + * Analysis starting point. + */ + + self.visit_block(body); + + debug!("analyzing fn body with id {}", body.id); + + let mut euv = euv::ExprUseVisitor::new(self, self.fcx); + euv.walk_fn(decl, body); + } + + /// Indicates that `cmt` is being directly mutated (e.g., assigned + /// to). If cmt contains any by-ref upvars, this implies that + /// those upvars must be borrowed using an `&mut` borow. + fn adjust_upvar_borrow_kind_for_mut(&mut self, cmt: mc::cmt<'tcx>) { + debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", + cmt.repr(self.tcx())); + + match cmt.cat.clone() { + mc::cat_deref(base, _, mc::Unique) | + mc::cat_interior(base, _) | + mc::cat_downcast(base, _) => { + // Interior or owned data is mutable if base is + // mutable, so iterate to the base. + self.adjust_upvar_borrow_kind_for_mut(base); + } + + mc::cat_deref(base, _, mc::BorrowedPtr(..)) | + mc::cat_deref(base, _, mc::Implicit(..)) => { + if let mc::NoteUpvarRef(upvar_id) = cmt.note { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to mutable if necessary + let mut upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = &mut upvar_borrow_map[upvar_id]; + self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow); + } else { + // assignment to deref of an `&mut` + // borrowed pointer implies that the + // pointer itself must be unique, but not + // necessarily *mutable* + self.adjust_upvar_borrow_kind_for_unique(base); + } + } + + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_local(_) | + mc::cat_upvar(..) => { + return; + } + } + } + + fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) { + debug!("adjust_upvar_borrow_kind_for_unique(cmt={})", + cmt.repr(self.tcx())); + + match cmt.cat.clone() { + mc::cat_deref(base, _, mc::Unique) | + mc::cat_interior(base, _) | + mc::cat_downcast(base, _) => { + // Interior or owned data is unique if base is + // unique. + self.adjust_upvar_borrow_kind_for_unique(base); + } + + mc::cat_deref(base, _, mc::BorrowedPtr(..)) | + mc::cat_deref(base, _, mc::Implicit(..)) => { + if let mc::NoteUpvarRef(upvar_id) = cmt.note { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to unique if necessary + let mut ub = self.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = &mut ub[upvar_id]; + self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow); + } else { + // for a borrowed pointer to be unique, its + // base must be unique + self.adjust_upvar_borrow_kind_for_unique(base); + } + } + + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_local(_) | + mc::cat_upvar(..) => { + } + } + } + + /// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind + /// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed + /// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by + /// some particular use. + fn adjust_upvar_borrow_kind(&self, + upvar_id: ty::UpvarId, + upvar_borrow: &mut ty::UpvarBorrow, + kind: ty::BorrowKind) { + debug!("adjust_upvar_borrow_kind: id={} kind=({} -> {})", + upvar_id, upvar_borrow.kind, kind); + + match (upvar_borrow.kind, kind) { + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow) | + (ty::ImmBorrow, ty::MutBorrow) | + (ty::UniqueImmBorrow, ty::MutBorrow) => { + upvar_borrow.kind = kind; + } + // Take LHS: + (ty::ImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | + (ty::MutBorrow, _) => { + } + } + } +} + +impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> { + fn visit_fn(&mut self, + fn_kind: visit::FnKind<'v>, + decl: &'v ast::FnDecl, + body: &'v ast::Block, + span: Span, + _id: ast::NodeId) + { + match fn_kind { + visit::FkItemFn(..) | visit::FkMethod(..) => { + // ignore nested fn items + } + visit::FkFnBlock => { + self.analyze_fn(decl, body); + visit::walk_fn(self, fn_kind, decl, body, span); + } + } + } +} + +impl<'a,'tcx> euv::Delegate<'tcx> for AdjustBorrowKind<'a,'tcx> { + fn consume(&mut self, + _consume_id: ast::NodeId, + _consume_span: Span, + _cmt: mc::cmt<'tcx>, + _mode: euv::ConsumeMode) + {} + + fn matched_pat(&mut self, + _matched_pat: &ast::Pat, + _cmt: mc::cmt<'tcx>, + _mode: euv::MatchMode) + {} + + fn consume_pat(&mut self, + _consume_pat: &ast::Pat, + _cmt: mc::cmt<'tcx>, + _mode: euv::ConsumeMode) + {} + + fn borrow(&mut self, + borrow_id: ast::NodeId, + _borrow_span: Span, + cmt: mc::cmt<'tcx>, + _loan_region: ty::Region, + bk: ty::BorrowKind, + _loan_cause: euv::LoanCause) + { + debug!("borrow(borrow_id={}, cmt={}, bk={})", + borrow_id, cmt.repr(self.tcx()), bk); + + match bk { + ty::ImmBorrow => { } + ty::UniqueImmBorrow => { + self.adjust_upvar_borrow_kind_for_unique(cmt); + } + ty::MutBorrow => { + self.adjust_upvar_borrow_kind_for_mut(cmt); + } + } + } + + fn decl_without_init(&mut self, + _id: ast::NodeId, + _span: Span) + {} + + fn mutate(&mut self, + _assignment_id: ast::NodeId, + _assignment_span: Span, + assignee_cmt: mc::cmt<'tcx>, + _mode: euv::MutateMode) + { + debug!("mutate(assignee_cmt={})", + assignee_cmt.repr(self.tcx())); + + self.adjust_upvar_borrow_kind_for_mut(assignee_cmt); + } +} + + From 09a7bc55dbd2bd4e05253b2bd72e5ecfb5cf7dd2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:14:21 -0500 Subject: [PATCH 7/8] Running EUV requires the Copy lang item, so adjust privacy tests. --- src/test/compile-fail/privacy1.rs | 3 +++ src/test/compile-fail/privacy4.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs index 50261839b16c6..41621a934d17a 100644 --- a/src/test/compile-fail/privacy1.rs +++ b/src/test/compile-fail/privacy1.rs @@ -14,6 +14,9 @@ #[lang="sized"] pub trait Sized {} +#[lang="copy"] +pub trait Copy {} + mod bar { // shouldn't bring in too much pub use self::glob::*; diff --git a/src/test/compile-fail/privacy4.rs b/src/test/compile-fail/privacy4.rs index b65ad2a2e6aae..70e7e2df98a69 100644 --- a/src/test/compile-fail/privacy4.rs +++ b/src/test/compile-fail/privacy4.rs @@ -12,6 +12,7 @@ #![no_std] // makes debugging this test *a lot* easier (during resolve) #[lang = "sized"] pub trait Sized for Sized? {} +#[lang="copy"] pub trait Copy {} // Test to make sure that private items imported through globs remain private // when they're used. From cbeff8b8b3bd28e99a3a034a41c8bdcd830fa884 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 04:15:00 -0500 Subject: [PATCH 8/8] "Fallout": Slightly different errors are generated in this edge case. --- src/test/compile-fail/borrowck-closures-mut-of-imm.rs | 1 + src/test/compile-fail/issue-17551.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs index 6360a9135005c..8163df5e967cd 100644 --- a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs +++ b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs @@ -24,6 +24,7 @@ fn a(x: &int) { //~^ ERROR cannot borrow let c2 = || set(&mut *x); //~^ ERROR cannot borrow + //~| ERROR closure requires unique access } fn main() { diff --git a/src/test/compile-fail/issue-17551.rs b/src/test/compile-fail/issue-17551.rs index e7f61a4f3ff5f..3889b6f4f7dce 100644 --- a/src/test/compile-fail/issue-17551.rs +++ b/src/test/compile-fail/issue-17551.rs @@ -13,6 +13,6 @@ struct B; fn main() { - let foo = B; //~ ERROR unable to infer enough type information - let closure = |:| foo; + let foo = B; + let closure = |:| foo; //~ ERROR unable to infer enough type information }