diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index f9774e83e8a1c..d6593f30b2ce9 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -134,6 +134,7 @@ pub mod util { pub mod common; pub mod ppaux; pub mod nodemap; + pub mod snapshot_vec; } pub mod lib { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 605811555a168..886c1a3b3ebff 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -692,11 +692,12 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { * `x.deref()`. Since `deref()` is declared with `&self`, this * is an autoref of `x`. */ - debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs); + debug!("walk_autoderefs expr={} autoderefs={}", + expr.repr(self.tcx()), autoderefs); for i in range(0, autoderefs) { let deref_id = typeck::MethodCall::autoderef(expr.id, i); - match self.typer.node_method_ty(deref_id) { + match self.typer.node_method_return_ty(deref_id) { None => {} Some(method_ty) => { let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i)); @@ -705,7 +706,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { ty::ty_rptr(r, ref m) => (m.mutbl, r), _ => self.tcx().sess.span_bug(expr.span, format!("bad overloaded deref type {}", - method_ty.repr(self.tcx())).as_slice()) + method_ty.repr(self.tcx())).as_slice()) }; let bk = ty::BorrowKind::from_mutbl(m); self.delegate.borrow(expr.id, expr.span, cmt, diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index e2e3081eb2d16..b0904aa1513a9 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -11,13 +11,14 @@ use middle::freevars::freevar_entry; use middle::freevars; +use mc = middle::mem_categorization; use middle::subst; use middle::ty; use middle::ty_fold; use middle::ty_fold::TypeFoldable; use middle::typeck; use middle::typeck::{MethodCall, NoAdjustment}; -use util::ppaux::{Repr, ty_to_string}; +use util::ppaux::{ty_to_string}; use util::ppaux::UserString; use syntax::ast::*; @@ -26,6 +27,7 @@ use syntax::codemap::Span; use syntax::print::pprust::{expr_to_string, ident_to_string}; use syntax::{visit}; use syntax::visit::Visitor; +use std::rc::Rc; // Kind analysis pass. // @@ -134,10 +136,11 @@ fn check_impl_of_trait(cx: &mut Context, it: &Item, trait_ref: &TraitRef, self_t // If this trait has builtin-kind supertraits, meet them. let self_ty: ty::t = ty::node_id_to_type(cx.tcx, it.id); debug!("checking impl with self type {}", ty::get(self_ty).sty); - check_builtin_bounds(cx, self_ty, trait_def.bounds, |missing| { + check_builtin_bounds(cx, self_ty, trait_def.bounds, [], |missing| { span_err!(cx.tcx.sess, self_type.span, E0142, "the type `{}', which does not fulfill `{}`, cannot implement this trait", - ty_to_string(cx.tcx, self_ty), missing.user_string(cx.tcx)); + ty_to_string(cx.tcx, self_ty), + missing.user_string(cx.tcx)); span_note!(cx.tcx.sess, self_type.span, "types implementing this trait must fulfill `{}`", trait_def.bounds.user_string(cx.tcx)); @@ -261,7 +264,20 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { debug!("kind::check_expr({})", expr_to_string(e)); // Handle any kind bounds on type parameters - check_bounds_on_type_parameters(cx, e); + mc::each_type_parameters_and_def(cx.tcx, e, |type_param_ty, type_param_def| { + check_typaram_bounds(cx, e.span, type_param_ty, type_param_def) + }); + + // If this node is a method call (or overloaded op), check the + // vtable. + { + let vtable_map = cx.tcx.vtable_map.borrow(); + let vtable_res = match vtable_map.find(&MethodCall::expr(e.id)) { + None => return, + Some(vtable_res) => vtable_res, + }; + check_type_parameter_bounds_in_vtable_result(cx, e.span, vtable_res); + } match e.node { ExprBox(ref loc, ref interior) => { @@ -322,88 +338,6 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { visit::walk_expr(cx, e, ()); } -fn check_bounds_on_type_parameters(cx: &mut Context, e: &Expr) { - let method_map = cx.tcx.method_map.borrow(); - let method_call = typeck::MethodCall::expr(e.id); - let method = method_map.find(&method_call); - - // Find the values that were provided (if any) - let item_substs = cx.tcx.item_substs.borrow(); - let (types, is_object_call) = match method { - Some(method) => { - let is_object_call = match method.origin { - typeck::MethodObject(..) => true, - typeck::MethodStatic(..) | - typeck::MethodStaticUnboxedClosure(..) | - typeck::MethodParam(..) => false - }; - (&method.substs.types, is_object_call) - } - None => { - match item_substs.find(&e.id) { - None => { return; } - Some(s) => { (&s.substs.types, false) } - } - } - }; - - // Find the relevant type parameter definitions - let def_map = cx.tcx.def_map.borrow(); - let type_param_defs = match e.node { - ExprPath(_) => { - let did = def_map.get_copy(&e.id).def_id(); - ty::lookup_item_type(cx.tcx, did).generics.types.clone() - } - _ => { - // Type substitutions should only occur on paths and - // method calls, so this needs to be a method call. - - // Even though the callee_id may have been the id with - // node_type_substs, e.id is correct here. - match method { - Some(method) => { - ty::method_call_type_param_defs(cx.tcx, method.origin) - } - None => { - cx.tcx.sess.span_bug(e.span, - "non path/method call expr has type substs??"); - } - } - } - }; - - // Check that the value provided for each definition meets the - // kind requirements - for type_param_def in type_param_defs.iter() { - let ty = *types.get(type_param_def.space, type_param_def.index); - - // If this is a call to an object method (`foo.bar()` where - // `foo` has a type like `Trait`), then the self type is - // unknown (after all, this is a virtual call). In that case, - // we will have put a ty_err in the substitutions, and we can - // just skip over validating the bounds (because the bounds - // would have been enforced when the object instance was - // created). - if is_object_call && type_param_def.space == subst::SelfSpace { - assert_eq!(type_param_def.index, 0); - assert!(ty::type_is_error(ty)); - continue; - } - - debug!("type_param_def space={} index={} ty={}", - type_param_def.space, type_param_def.index, ty.repr(cx.tcx)); - check_typaram_bounds(cx, e.span, ty, type_param_def) - } - - // Check the vtable. - let vtable_map = cx.tcx.vtable_map.borrow(); - let vtable_res = match vtable_map.find(&method_call) { - None => return, - Some(vtable_res) => vtable_res, - }; - check_type_parameter_bounds_in_vtable_result(cx, e.span, vtable_res); -} - fn check_type_parameter_bounds_in_vtable_result( cx: &mut Context, span: Span, @@ -490,14 +424,15 @@ fn check_ty(cx: &mut Context, aty: &Ty) { pub fn check_builtin_bounds(cx: &Context, ty: ty::t, bounds: ty::BuiltinBounds, + traits: &[Rc], any_missing: |ty::BuiltinBounds|) { let kind = ty::type_contents(cx.tcx, ty); let mut missing = ty::empty_builtin_bounds(); - for bound in bounds.iter() { + ty::each_inherited_builtin_bound(cx.tcx, bounds, traits, |bound| { if !kind.meets_bound(cx.tcx, bound) { missing.add(bound); } - } + }); if !missing.is_empty() { any_missing(missing); } @@ -507,9 +442,12 @@ pub fn check_typaram_bounds(cx: &Context, sp: Span, ty: ty::t, type_param_def: &ty::TypeParameterDef) { + debug!("check_typaram_bounds(ty={}, type_param_def={}, sp={})", + ty.repr(cx.tcx), type_param_def.repr(cx.tcx), sp.repr(cx.tcx)); check_builtin_bounds(cx, ty, type_param_def.bounds.builtin_bounds, + type_param_def.bounds.trait_bounds.as_slice(), |missing| { span_err!(cx.tcx.sess, sp, E0144, "instantiating a type parameter with an incompatible type \ @@ -522,7 +460,7 @@ pub fn check_typaram_bounds(cx: &Context, pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t, bounds: ty::BuiltinBounds, referenced_ty: Option) { - check_builtin_bounds(cx, ty, bounds, |missing| { + check_builtin_bounds(cx, ty, bounds, [], |missing| { // Will be Some if the freevar is implicitly borrowed (stack closure). // Emit a less mysterious error message in this case. match referenced_ty { @@ -547,7 +485,7 @@ pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t, pub fn check_trait_cast_bounds(cx: &Context, sp: Span, ty: ty::t, bounds: ty::BuiltinBounds) { - check_builtin_bounds(cx, ty, bounds, |missing| { + check_builtin_bounds(cx, ty, bounds, [], |missing| { span_err!(cx.tcx.sess, sp, E0147, "cannot pack type `{}`, which does not fulfill `{}`, as a trait bounded by {}", ty_to_string(cx.tcx, ty), diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a690d5882141f..18fe47443e4ad 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -63,6 +63,7 @@ #![allow(non_camel_case_types)] use middle::def; +use middle::subst::Substs; use middle::ty; use middle::typeck; use util::nodemap::NodeMap; @@ -265,7 +266,10 @@ pub type McResult = Result; pub trait Typer { fn tcx<'a>(&'a self) -> &'a ty::ctxt; fn node_ty(&self, id: ast::NodeId) -> McResult; - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option; + fn node_method_return_ty(&self, method_call: typeck::MethodCall) -> Option; + fn node_method_callee(&self, method_call: typeck::MethodCall) + -> Option; + fn node_substs(&self, expr_id: ast::NodeId) -> Option; fn adjustments<'a>(&'a self) -> &'a RefCell>; fn is_method_call(&self, id: ast::NodeId) -> bool; fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option; @@ -361,9 +365,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult { let unadjusted_ty = if_ok!(self.expr_ty(expr)); - Ok(ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty, - self.typer.adjustments().borrow().find(&expr.id), - |method_call| self.typer.node_method_ty(method_call))) + Ok(ty::adjust_ty( + self.tcx(), expr.span, expr.id, unadjusted_ty, + self.typer.adjustments().borrow().find(&expr.id), + |method_call| self.typer.node_method_return_ty(method_call))) } fn node_ty(&self, id: ast::NodeId) -> McResult { @@ -445,7 +450,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { ast::ExprIndex(ref base, _) => { let method_call = typeck::MethodCall::expr(expr.id()); - match self.typer.node_method_ty(method_call) { + match self.typer.node_method_return_ty(method_call) { Some(method_ty) => { // If this is an index implemented by a method call, then it will // include an implicit deref of the result. @@ -734,10 +739,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { expr_id: node.id(), adjustment: adjustment }; - let method_ty = self.typer.node_method_ty(method_call); + let method_ty = self.typer.node_method_return_ty(method_call); debug!("cat_deref: method_call={:?} method_ty={}", - method_call, method_ty.map(|ty| ty.repr(self.tcx()))); + method_call, method_ty.repr(self.tcx())); let base_cmt = match method_ty { Some(method_ty) => { @@ -830,7 +835,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { //! the implicit index deref, if any (see above) let method_call = typeck::MethodCall::expr(elt.id()); - let method_ty = self.typer.node_method_ty(method_call); + let method_ty = self.typer.node_method_return_ty(method_call); let element_ty = match method_ty { Some(method_ty) => { @@ -1380,3 +1385,96 @@ fn element_kind(t: ty::t) -> ElementKind { _ => OtherElement } } + +/////////////////////////////////////////////////////////////////////////// + +pub fn each_type_parameters_and_def(typer: &TYPER, + expr: &ast::Expr, + op: |ty::t, &ty::TypeParameterDef|) +{ + /*! + * Given an expression `expr`, identifies any type parameters whose + * values are supplied as part of this expression and walks over them, + * passing a reference to the type which was used to instantiate the + * parameter as well as the definition of the parameter itself. + * + * For example: + * + * fn foo(t: T) { } + * foo(22_u) + * ^~~ <-- expr + * + * If invoked on the path node referencing `foo` as part of the + * call, it would call back with `(uint, T:Bound1)`. + */ + + debug!("each_type_parameters_and_def(expr={})", + expr.repr(typer.tcx())); + + // If this is a path, it (could be) a reference to a generic item. + // We must check extract the types provided and match them against + // the type parameter definitions from the item. + // + // Otherwise, check if this node is a method call (either + // overloaded or explicit). If so, extract type parameters defined + // on method (including those inherited from impl) and match them + // to those values provided in the method call. + let tcx = typer.tcx(); + let method_callee = + typer.node_method_callee(typeck::MethodCall::expr(expr.id)); + let (substs, type_param_defs) = match method_callee { + Some(typeck::MethodCallee { substs, origin, ty: _ }) => { + // Method call: + let defs = ty::method_call_type_param_defs(tcx, origin); + (substs, defs) + } + + None => { + debug!("each_type_parameters_and_def(expr.id={}) -- not method call", + expr.id); + + // Reference to generic item? + match expr.node { + ast::ExprPath(_) => { } // Yes, continue. + _ => { return; } // No, done. + } + + debug!("each_type_parameters_and_def(expr.id={}) -- is path", + expr.id); + + let substs = match typer.node_substs(expr.id) { + Some(s) => s, + None => { return; } // No parameters supplied, not generic item. + }; + + let did = tcx.def_map.borrow().get_copy(&expr.id).def_id(); + let defs = ty::lookup_item_type(tcx, did).generics.types.clone(); + (substs, defs) + } + }; + + debug!("each_type_parameters_and_def(expr={},substs={})", + expr.repr(typer.tcx()), + substs.repr(typer.tcx())); + + // Check that the value provided for each definition meets the + // kind requirements + for type_param_def in type_param_defs.iter() { + let ty = *substs.types.get(type_param_def.space, type_param_def.index); + + // Note: during type checking, error types can normally occur. + // Even afterwards, if this is a call to an object method + // (`foo.bar()` where `foo` has a type like `Trait`), then the + // self type is unknown (after all, this is a virtual + // call). In that case, we will have put a ty_err in the + // substitutions and we can just skip over validating the + // bounds (because the bounds would have been enforced when + // the object instance was created). So either way, ignore + // ty_err. + if !ty::type_is_error(ty) { + debug!("type_param_def space={} index={} ty={}", + type_param_def.space, type_param_def.index, ty.repr(tcx)); + op(ty, type_param_def) + } + } +} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 0f5af4421a5e8..cb0d638505692 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -28,7 +28,6 @@ use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::stability; use middle::ty; use middle::typeck; -use middle::typeck::MethodCall; use middle::ty_fold; use middle::ty_fold::{TypeFoldable,TypeFolder}; use middle; @@ -2241,25 +2240,6 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { }; }); return tc; - - // Iterates over all builtin bounds on the type parameter def, including - // those inherited from traits with builtin-kind-supertraits. - fn each_inherited_builtin_bound(cx: &ctxt, - bounds: BuiltinBounds, - traits: &[Rc], - f: |BuiltinBound|) { - for bound in bounds.iter() { - f(bound); - } - - each_bound_trait_and_supertraits(cx, traits, |trait_ref| { - let trait_def = lookup_trait_def(cx, trait_ref.def_id); - for bound in trait_def.bounds.iter() { - f(bound); - } - true - }); - } } } @@ -4370,6 +4350,25 @@ pub fn each_bound_trait_and_supertraits(tcx: &ctxt, return true; } +// Iterates over all builtin bounds on the type parameter def, including +// those inherited from traits with builtin-kind-supertraits. +pub fn each_inherited_builtin_bound(cx: &ctxt, + bounds: BuiltinBounds, + traits: &[Rc], + f: |BuiltinBound|) { + for bound in bounds.iter() { + f(bound); + } + + each_bound_trait_and_supertraits(cx, traits, |trait_ref| { + let trait_def = lookup_trait_def(cx, trait_ref.def_id); + for bound in trait_def.bounds.iter() { + f(bound); + } + true + }); +} + pub fn get_tydesc_ty(tcx: &ctxt) -> Result { tcx.lang_items.require(TyDescStructLangItem).map(|tydesc_lang_item| { tcx.intrinsic_defs.borrow().find_copy(&tydesc_lang_item) @@ -4843,8 +4842,27 @@ impl mc::Typer for ty::ctxt { Ok(ty::node_id_to_type(self, id)) } - fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option { - self.method_map.borrow().find(&method_call).map(|method| method.ty) + fn node_method_return_ty(&self, method_call: typeck::MethodCall) + -> Option { + self.method_map + .borrow() + .find(&method_call) + .map(|method| method.ty) + } + + fn node_method_callee(&self, method_call: typeck::MethodCall) + -> Option { + self.method_map + .borrow() + .find(&method_call) + .map(|method| (*method).clone()) + } + + fn node_substs(&self, node_id: ast::NodeId) -> Option { + self.item_substs + .borrow() + .find(&node_id) + .map(|item_substs| item_substs.substs.clone()) } fn adjustments<'a>(&'a self) -> &'a RefCell> { diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index d0431de81a359..d98d267c208ef 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -122,6 +122,7 @@ use middle::def; use middle::def::{DefArg, DefBinding, DefLocal, DefUpvar}; use middle::freevars; use mc = middle::mem_categorization; +use middle::subst::Substs; use middle::ty::{ReScope}; use middle::ty; use middle::typeck::astconv::AstConv; @@ -130,7 +131,7 @@ use middle::typeck::check::regionmanip::relate_nested_regions; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; use middle::typeck::infer; -use middle::typeck::MethodCall; +use middle::typeck::{MethodCall, MethodCallee}; use middle::pat_util; use util::nodemap::NodeMap; use util::ppaux::{ty_to_string, region_to_string, Repr}; @@ -241,6 +242,12 @@ impl<'a> Rcx<'a> { self.resolve_type(t) } + /// Try to resolve the type for the given node. + fn resolve_substs(&self, substs: &Substs) -> Substs { + let types = substs.types.map(|&t| self.resolve_type(t)); + Substs::new(types, substs.regions().clone()) + } + fn resolve_method_type(&self, method_call: MethodCall) -> Option { let method_ty = self.fcx.inh.method_map.borrow() .find(&method_call).map(|method| method.ty); @@ -248,7 +255,7 @@ impl<'a> Rcx<'a> { } /// Try to resolve the type for the given node. - pub fn resolve_expr_type_adjusted(&mut self, expr: &ast::Expr) -> ty::t { + pub fn resolve_expr_type_adjusted(&self, expr: &ast::Expr) -> ty::t { let ty_unadjusted = self.resolve_node_type(expr.id); if ty::type_is_error(ty_unadjusted) || ty::type_is_bot(ty_unadjusted) { ty_unadjusted @@ -271,10 +278,30 @@ impl<'fcx> mc::Typer for Rcx<'fcx> { if ty::type_is_error(t) {Err(())} else {Ok(t)} } - fn node_method_ty(&self, method_call: MethodCall) -> Option { + fn node_method_return_ty(&self, method_call: MethodCall) -> Option { self.resolve_method_type(method_call) } + fn node_method_callee(&self, method_call: MethodCall) -> Option { + self.fcx.inh.method_map + .borrow() + .find(&method_call) + .map(|method| { + MethodCallee { + origin: method.origin.clone(), + ty: self.resolve_type(method.ty), + substs: self.resolve_substs(&method.substs) + } + }) + } + + fn node_substs(&self, node_id: ast::NodeId) -> Option { + self.fcx.inh.item_substs + .borrow() + .find(&node_id) + .map(|item_substs| self.resolve_substs(&item_substs.substs)) + } + fn adjustments<'a>(&'a self) -> &'a RefCell> { &self.fcx.inh.adjustments } @@ -441,6 +468,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } } + constrain_regions_in_type_params(rcx, expr); + match expr.node { ast::ExprCall(ref callee, ref args) => { if has_method_map { @@ -637,8 +666,8 @@ fn check_expr_fn_block(rcx: &mut Rcx, let tcx = rcx.fcx.tcx(); let function_type = rcx.resolve_node_type(expr.id); match ty::get(function_type).sty { - ty::ty_closure(box ty::ClosureTy { - store: ty::RegionTraitStore(region, _), ..}) => { + ty::ty_closure(box ty::ClosureTy {store: ty::RegionTraitStore(region, _), + ..}) => { freevars::with_freevars(tcx, expr.id, |freevars| { if freevars.is_empty() { // No free variables means that the environment @@ -655,6 +684,14 @@ fn check_expr_fn_block(rcx: &mut Rcx, } }); } + + ty::ty_closure(box ty::ClosureTy {store: ty::UniqTraitStore, + ..}) => { + freevars::with_freevars(tcx, expr.id, |freevars| { + constrain_free_variables_in_proc(rcx, expr, freevars); + }); + } + _ => () } @@ -674,6 +711,25 @@ fn check_expr_fn_block(rcx: &mut Rcx, _ => () } + fn constrain_free_variables_in_proc(rcx: &mut Rcx, + expr: &ast::Expr, + freevars: &[freevars::freevar_entry]) { + /*! + * There is currently a rule that procs that they can only + * close over static things. + */ + + let tcx = rcx.fcx.ccx.tcx; + debug!("constrain_free_variables_in_proc({})", expr.repr(tcx)); + for freevar in freevars.iter() { + let var_def_id = freevar.def.def_id(); + assert!(var_def_id.krate == ast::LOCAL_CRATE); + constrain_regions_in_type_of_node( + rcx, var_def_id.node, ty::ReStatic, + infer::ProcCapture(expr.span, var_def_id.node)); + } + } + fn constrain_free_variables(rcx: &mut Rcx, region: ty::Region, expr: &ast::Expr, @@ -1008,10 +1064,11 @@ fn constrain_regions_in_type_of_node( } fn constrain_regions_in_type( - rcx: &mut Rcx, + rcx: &Rcx, minimum_lifetime: ty::Region, origin: infer::SubregionOrigin, - ty: ty::t) { + ty: ty::t) +{ /*! * Requires that any regions which appear in `ty` must be * superregions of `minimum_lifetime`. Also enforces the constraint @@ -1054,6 +1111,38 @@ fn constrain_regions_in_type( }); } +fn constrain_regions_in_type_params( + rcx: &Rcx, + expr: &ast::Expr) +{ + mc::each_type_parameters_and_def(rcx, expr, |type_param_ty, type_param_def| { + let mut constrained = false; + debug!("type_param_ty={} type_param_def={}", + type_param_ty.repr(rcx.tcx()), + type_param_def.repr(rcx.tcx())); + ty::each_inherited_builtin_bound( + rcx.tcx(), + type_param_def.bounds.builtin_bounds, + type_param_def.bounds.trait_bounds.as_slice(), + |bound| { + match bound { + ty::BoundStatic | ty::BoundSend => { + if !constrained { + let origin = + infer::TypeParameterBound( + type_param_def.ident, + expr.span); + constrain_regions_in_type(rcx, ty::ReStatic, + origin, type_param_ty); + constrained = true; + } + } + ty::BoundCopy | ty::BoundShare | ty::BoundSized => { } + } + }); + }); +} + fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr, mutability: ast::Mutability, base: &ast::Expr) { /*! @@ -1190,8 +1279,8 @@ fn link_by_ref(rcx: &Rcx, expr.repr(tcx), callee_scope); let mc = mc::MemCategorizationContext::new(rcx); let expr_cmt = ignore_err!(mc.cat_expr(expr)); - let region_min = ty::ReScope(callee_scope); - link_region(rcx, expr.span, region_min, ty::ImmBorrow, expr_cmt); + let borrow_region = ty::ReScope(callee_scope); + link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt); } fn link_region_from_node_type(rcx: &Rcx, @@ -1217,102 +1306,54 @@ fn link_region_from_node_type(rcx: &Rcx, fn link_region(rcx: &Rcx, span: Span, - region_min: ty::Region, - kind: ty::BorrowKind, - cmt_borrowed: mc::cmt) { + borrow_region: ty::Region, + borrow_kind: ty::BorrowKind, + borrow_cmt: mc::cmt) { /*! - * Informs the inference engine that a borrow of `cmt` - * must have the borrow kind `kind` and lifetime `region_min`. - * If `cmt` is a deref of a region pointer with - * lifetime `r_borrowed`, this will add the constraint that - * `region_min <= r_borrowed`. + * Informs the inference engine that `borrow_cmt` is being + * borrowed with kind `borrow_kind` and lifetime `borrow_region`. + * In order to ensure borrowck is satisfied, this may create + * constraints between regions, as explained in + * `link_reborrowed_region()`. */ - // Iterate through all the things that must be live at least - // for the lifetime `region_min` for the borrow to be valid: - let mut cmt_borrowed = cmt_borrowed; + let mut borrow_cmt = borrow_cmt; + let mut borrow_kind = borrow_kind; + loop { - debug!("link_region(region_min={}, kind={}, cmt_borrowed={})", - region_min.repr(rcx.tcx()), - kind.repr(rcx.tcx()), - cmt_borrowed.repr(rcx.tcx())); - match cmt_borrowed.cat.clone() { - mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) | - mc::cat_deref(base, _, mc::Implicit(_, r_borrowed)) => { - // References to an upvar `x` are translated to - // `*x`, since that is what happens in the - // underlying machine. We detect such references - // and treat them slightly differently, both to - // offer better error messages and because we need - // to infer the kind of borrow (mut, const, etc) - // to use for each upvar. - let cause = match base.cat { - mc::cat_upvar(ref upvar_id, _) => { - match rcx.fcx.inh.upvar_borrow_map.borrow_mut() - .find_mut(upvar_id) { - Some(upvar_borrow) => { - debug!("link_region: {} <= {}", - region_min.repr(rcx.tcx()), - upvar_borrow.region.repr(rcx.tcx())); - adjust_upvar_borrow_kind_for_loan( - *upvar_id, - upvar_borrow, - kind); - infer::ReborrowUpvar(span, *upvar_id) - } - None => { - rcx.tcx().sess.span_bug( - span, - format!("Illegal upvar id: {}", - upvar_id.repr( - rcx.tcx())).as_slice()); - } - } + debug!("link_region(borrow_region={}, borrow_kind={}, borrow_cmt={})", + borrow_region.repr(rcx.tcx()), + borrow_kind.repr(rcx.tcx()), + borrow_cmt.repr(rcx.tcx())); + match borrow_cmt.cat.clone() { + mc::cat_deref(ref_cmt, _, + mc::Implicit(ref_kind, ref_region)) | + mc::cat_deref(ref_cmt, _, + mc::BorrowedPtr(ref_kind, ref_region)) => { + match link_reborrowed_region(rcx, span, + borrow_region, borrow_kind, + ref_cmt, ref_region, ref_kind) { + Some((c, k)) => { + borrow_cmt = c; + borrow_kind = k; } - - _ => { - infer::Reborrow(span) + None => { + return; } - }; - - debug!("link_region: {} <= {}", - region_min.repr(rcx.tcx()), - r_borrowed.repr(rcx.tcx())); - rcx.fcx.mk_subr(true, cause, region_min, r_borrowed); - - if kind != ty::ImmBorrow { - // If this is a mutable borrow, then the thing - // being borrowed will have to be unique. - // In user code, this means it must be an `&mut` - // borrow, but for an upvar, we might opt - // for an immutable-unique borrow. - adjust_upvar_borrow_kind_for_unique(rcx, base); } - - // Borrowing an `&mut` pointee for `region_min` is - // only valid if the pointer resides in a unique - // location which is itself valid for - // `region_min`. We don't care about the unique - // part, but we may need to influence the - // inference to ensure that the location remains - // valid. - // - // FIXME(#8624) fixing borrowck will require this - // if m == ast::m_mutbl { - // cmt_borrowed = cmt_base; - // } else { - // return; - // } - return; } + mc::cat_discr(cmt_base, _) | mc::cat_downcast(cmt_base) | mc::cat_deref(cmt_base, _, mc::GcPtr(..)) | mc::cat_deref(cmt_base, _, mc::OwnedPtr) | mc::cat_interior(cmt_base, _) => { - // Interior or owned data requires its base to be valid - cmt_borrowed = cmt_base; + // Borrowing interior or owned data requires the base + // to be valid and borrowable in the same fashion. + borrow_cmt = cmt_base; + borrow_kind = borrow_kind; } + mc::cat_deref(_, _, mc::UnsafePtr(..)) | mc::cat_static_item | mc::cat_copied_upvar(..) | @@ -1328,6 +1369,154 @@ fn link_region(rcx: &Rcx, } } +fn link_reborrowed_region(rcx: &Rcx, + span: Span, + borrow_region: ty::Region, + borrow_kind: ty::BorrowKind, + ref_cmt: mc::cmt, + ref_region: ty::Region, + ref_kind: ty::BorrowKind) + -> Option<(mc::cmt, ty::BorrowKind)> +{ + /*! + * This is the most complicated case: the path being borrowed is + * itself the referent of a borrowed pointer. Let me give an + * example fragment of code to make clear(er) the situation: + * + * let r: &'a mut T = ...; // the original reference "r" has lifetime 'a + * ... + * &'z *r // the reborrow has lifetime 'z + * + * Now, in this case, our primary job is to add the inference + * constraint that `'z <= 'a`. Given this setup, let's clarify the + * parameters in (roughly) terms of the example: + * + * A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T` + * borrow_region ^~ ref_region ^~ + * borrow_kind ^~ ref_kind ^~ + * ref_cmt ^ + * + * Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc). + * + * Unfortunately, there are some complications beyond the simple + * scenario I just painted: + * + * 1. The reference `r` might in fact be a "by-ref" upvar. In that + * case, we have two jobs. First, we are inferring whether this reference + * should be an `&T`, `&mut T`, or `&uniq T` reference, and we must + * adjust that based on this borrow (e.g., if this is an `&mut` borrow, + * then `r` must be an `&mut` reference). Second, whenever we link + * two regions (here, `'z <= 'a`), we supply a *cause*, and in this + * case we adjust the cause to indicate that the reference being + * "reborrowed" is itself an upvar. This provides a nicer error message + * should something go wrong. + * + * 2. There may in fact be more levels of reborrowing. In the + * example, I said the borrow was like `&'z *r`, but it might + * in fact be a borrow like `&'z **q` where `q` has type `&'a + * &'b mut T`. In that case, we want to ensure that `'z <= 'a` + * and `'z <= 'b`. This is explained more below. + * + * The return value of this function indicates whether we need to + * recurse and process `ref_cmt` (see case 2 above). + */ + + // Detect references to an upvar `x`: + let cause = match ref_cmt.cat { + mc::cat_upvar(ref upvar_id, _) => { + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + match upvar_borrow_map.find_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(*upvar_id, + upvar_borrow, + borrow_kind); + + infer::ReborrowUpvar(span, *upvar_id) + } + None => { + rcx.tcx().sess.span_bug( + span, + format!("Illegal upvar id: {}", + upvar_id.repr( + rcx.tcx())).as_slice()); + } + } + } + + _ => { + infer::Reborrow(span) + } + }; + + debug!("link_reborrowed_region: {} <= {}", + borrow_region.repr(rcx.tcx()), + ref_region.repr(rcx.tcx())); + rcx.fcx.mk_subr(true, cause, borrow_region, ref_region); + + // Decide whether we need to recurse and link any regions within + // the `ref_cmt`. This is concerned for the case where the value + // being reborrowed is in fact a borrowed pointer found within + // another borrowed pointer. For example: + // + // let p: &'b &'a mut T = ...; + // ... + // &'z **p + // + // What makes this case particularly tricky is that, if the data + // being borrowed is a `&mut` or `&uniq` borrow, borrowck requires + // not only that `'z <= 'a`, (as before) but also `'z <= 'b` + // (otherwise the user might mutate through the `&mut T` reference + // after `'b` expires and invalidate the borrow we are looking at + // now). + // + // So let's re-examine our parameters in light of this more + // complicated (possible) scenario: + // + // A borrow of: `& 'z bk * * p` where `p` has type `&'b bk & 'a bk T` + // borrow_region ^~ ref_region ^~ + // borrow_kind ^~ ref_kind ^~ + // ref_cmt ^~~ + // + // (Note that since we have not examined `ref_cmt.cat`, we don't + // know whether this scenario has occurred; but I wanted to show + // how all the types get adjusted.) + match ref_kind { + ty::ImmBorrow => { + // The reference being reborrowed is a sharable ref of + // type `&'a T`. In this case, it doesn't matter where we + // *found* the `&T` pointer, the memory it references will + // be valid and immutable for `'a`. So we can stop here. + // + // (Note that the `borrow_kind` must also be ImmBorrow or + // else the user is borrowed imm memory as mut memory, + // which means they'll get an error downstream in borrowck + // anyhow.) + return None; + } + + ty::MutBorrow | ty::UniqueImmBorrow => { + // The reference being reborrowed is either an `&mut T` or + // `&uniq T`. This is the case where recursion is needed. + // + // One interesting twist is that we can weaken the borrow + // kind when we recurse: to reborrow an `&mut` referent as + // mutable, borrowck requires a unique path to the `&mut` + // reference but not necessarily a *mutable* path. + let new_borrow_kind = match borrow_kind { + ty::ImmBorrow => + ty::ImmBorrow, + ty::MutBorrow | ty::UniqueImmBorrow => + ty::UniqueImmBorrow + }; + return Some((ref_cmt, new_borrow_kind)); + } + } +} + fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, lhs: &ast::Expr) { /*! @@ -1343,6 +1532,12 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx, cmt: mc::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. + */ + let mut cmt = cmt; loop { debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 03890250f7701..740271298d24b 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -257,7 +257,7 @@ impl<'f> Coerce<'f> { let a_borrowed = ty::mk_rptr(self.get_ref().infcx.tcx, r_borrow, mt {ty: inner_ty, mutbl: mt_b.mutbl}); - if_ok!(sub.tys(a_borrowed, b)); + try!(sub.tys(a_borrowed, b)); Ok(Some(AutoDerefRef(AutoDerefRef { autoderefs: 1, autoref: Some(AutoPtr(r_borrow, mt_b.mutbl)) @@ -309,7 +309,7 @@ impl<'f> Coerce<'f> { let a_borrowed = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow, mt {ty: ty_inner, mutbl: mutbl_b}); - if_ok!(sub.tys(a_borrowed, b)); + try!(sub.tys(a_borrowed, b)); Ok(Some(AutoDerefRef(AutoDerefRef { autoderefs: 0, autoref: Some(AutoBorrowVec(r_borrow, mutbl_b)) @@ -350,7 +350,7 @@ impl<'f> Coerce<'f> { } }; - if_ok!(self.subtype(a_borrowed, b)); + try!(self.subtype(a_borrowed, b)); Ok(Some(AutoDerefRef(AutoDerefRef { autoderefs: 0, autoref: Some(AutoBorrowObj(r_a, b_mutbl)) @@ -404,7 +404,7 @@ impl<'f> Coerce<'f> { sig: fn_ty_a.sig.clone(), .. *fn_ty_b }); - if_ok!(self.subtype(a_closure, b)); + try!(self.subtype(a_closure, b)); Ok(Some(adj)) }) } @@ -428,7 +428,7 @@ impl<'f> Coerce<'f> { // check that the types which they point at are compatible let a_unsafe = ty::mk_ptr(self.get_ref().infcx.tcx, mt_a); - if_ok!(self.subtype(a_unsafe, b)); + try!(self.subtype(a_unsafe, b)); // although references and unsafe ptrs have the same // representation, we still register an AutoDerefRef so that diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 1aae97d3d83e9..aaffa1cfce460 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -121,7 +121,7 @@ pub trait Combine { for &space in subst::ParamSpace::all().iter() { let a_tps = a_subst.types.get_slice(space); let b_tps = b_subst.types.get_slice(space); - let tps = if_ok!(self.tps(space, a_tps, b_tps)); + let tps = try!(self.tps(space, a_tps, b_tps)); let a_regions = a_subst.regions().get_slice(space); let b_regions = b_subst.regions().get_slice(space); @@ -137,11 +137,11 @@ pub trait Combine { } }; - let regions = if_ok!(relate_region_params(self, - item_def_id, - r_variances, - a_regions, - b_regions)); + let regions = try!(relate_region_params(self, + item_def_id, + r_variances, + a_regions, + b_regions)); substs.types.replace(space, tps); substs.mut_regions().replace(space, regions); @@ -185,7 +185,7 @@ pub trait Combine { ty::Contravariant => this.contraregions(a_r, b_r), ty::Bivariant => Ok(a_r), }; - rs.push(if_ok!(r)); + rs.push(try!(r)); } Ok(rs) } @@ -193,9 +193,9 @@ pub trait Combine { fn bare_fn_tys(&self, a: &ty::BareFnTy, b: &ty::BareFnTy) -> cres { - let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style)); - let abi = if_ok!(self.abi(a.abi, b.abi)); - let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig)); + let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style)); + let abi = try!(self.abi(a.abi, b.abi)); + let sig = try!(self.fn_sigs(&a.sig, &b.sig)); Ok(ty::BareFnTy {fn_style: fn_style, abi: abi, sig: sig}) @@ -207,7 +207,7 @@ pub trait Combine { let store = match (a.store, b.store) { (ty::RegionTraitStore(a_r, a_m), ty::RegionTraitStore(b_r, b_m)) if a_m == b_m => { - let r = if_ok!(self.contraregions(a_r, b_r)); + let r = try!(self.contraregions(a_r, b_r)); ty::RegionTraitStore(r, a_m) } @@ -219,11 +219,11 @@ pub trait Combine { return Err(ty::terr_sigil_mismatch(expected_found(self, a.store, b.store))) } }; - let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style)); - let onceness = if_ok!(self.oncenesses(a.onceness, b.onceness)); - let bounds = if_ok!(self.bounds(a.bounds, b.bounds)); - let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig)); - let abi = if_ok!(self.abi(a.abi, b.abi)); + let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style)); + let onceness = try!(self.oncenesses(a.onceness, b.onceness)); + let bounds = try!(self.bounds(a.bounds, b.bounds)); + let sig = try!(self.fn_sigs(&a.sig, &b.sig)); + let abi = try!(self.abi(a.abi, b.abi)); Ok(ty::ClosureTy { fn_style: fn_style, onceness: onceness, @@ -294,7 +294,7 @@ pub trait Combine { Err(ty::terr_traits( expected_found(self, a.def_id, b.def_id))) } else { - let substs = if_ok!(self.substs(a.def_id, &a.substs, &b.substs)); + let substs = try!(self.substs(a.def_id, &a.substs, &b.substs)); Ok(ty::TraitRef { def_id: a.def_id, substs: substs }) } @@ -360,10 +360,10 @@ pub fn super_fn_sigs(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres< return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic))); } - let inputs = if_ok!(argvecs(this, + let inputs = try!(argvecs(this, a.inputs.as_slice(), b.inputs.as_slice())); - let output = if_ok!(this.tys(a.output, b.output)); + let output = try!(this.tys(a.output, b.output)); Ok(FnSig {binder_id: a.binder_id, inputs: inputs, output: output, @@ -413,7 +413,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { // Relate integral variables to other types (&ty::ty_infer(IntVar(a_id)), &ty::ty_infer(IntVar(b_id))) => { - if_ok!(this.infcx().simple_vars(this.a_is_expected(), + try!(this.infcx().simple_vars(this.a_is_expected(), a_id, b_id)); Ok(a) } @@ -436,7 +436,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { // Relate floating-point variables to other types (&ty::ty_infer(FloatVar(a_id)), &ty::ty_infer(FloatVar(b_id))) => { - if_ok!(this.infcx().simple_vars(this.a_is_expected(), + try!(this.infcx().simple_vars(this.a_is_expected(), a_id, b_id)); Ok(a) } @@ -468,7 +468,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { (&ty::ty_enum(a_id, ref a_substs), &ty::ty_enum(b_id, ref b_substs)) if a_id == b_id => { - let substs = if_ok!(this.substs(a_id, + let substs = try!(this.substs(a_id, a_substs, b_substs)); Ok(ty::mk_enum(tcx, a_id, substs)) @@ -478,8 +478,8 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { &ty::ty_trait(ref b_)) if a_.def_id == b_.def_id => { debug!("Trying to match traits {:?} and {:?}", a, b); - let substs = if_ok!(this.substs(a_.def_id, &a_.substs, &b_.substs)); - let bounds = if_ok!(this.bounds(a_.bounds, b_.bounds)); + let substs = try!(this.substs(a_.def_id, &a_.substs, &b_.substs)); + let bounds = try!(this.bounds(a_.bounds, b_.bounds)); Ok(ty::mk_trait(tcx, a_.def_id, substs.clone(), @@ -488,7 +488,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { (&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs)) if a_id == b_id => { - let substs = if_ok!(this.substs(a_id, a_substs, b_substs)); + let substs = try!(this.substs(a_id, a_substs, b_substs)); Ok(ty::mk_struct(tcx, a_id, substs)) } @@ -502,27 +502,27 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { } (&ty::ty_uniq(a_inner), &ty::ty_uniq(b_inner)) => { - let typ = if_ok!(this.tys(a_inner, b_inner)); + let typ = try!(this.tys(a_inner, b_inner)); check_ptr_to_unsized(this, a, b, a_inner, b_inner, ty::mk_uniq(tcx, typ)) } (&ty::ty_ptr(ref a_mt), &ty::ty_ptr(ref b_mt)) => { - let mt = if_ok!(this.mts(a_mt, b_mt)); + let mt = try!(this.mts(a_mt, b_mt)); check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_ptr(tcx, mt)) } (&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => { - let r = if_ok!(this.contraregions(a_r, b_r)); + let r = try!(this.contraregions(a_r, b_r)); // FIXME(14985) If we have mutable references to trait objects, we // used to use covariant subtyping. I have preserved this behaviour, // even though it is probably incorrect. So don't go down the usual // path which would require invariance. let mt = match (&ty::get(a_mt.ty).sty, &ty::get(b_mt.ty).sty) { (&ty::ty_trait(..), &ty::ty_trait(..)) if a_mt.mutbl == b_mt.mutbl => { - let ty = if_ok!(this.tys(a_mt.ty, b_mt.ty)); + let ty = try!(this.tys(a_mt.ty, b_mt.ty)); ty::mt { ty: ty, mutbl: a_mt.mutbl } } - _ => if_ok!(this.mts(a_mt, b_mt)) + _ => try!(this.mts(a_mt, b_mt)) }; check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_rptr(tcx, r, mt)) } @@ -573,7 +573,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { vid: ty::IntVid, val: ty::IntVarValue) -> cres { - if_ok!(this.infcx().simple_var_t(vid_is_expected, vid, val)); + try!(this.infcx().simple_var_t(vid_is_expected, vid, val)); match val { IntType(v) => Ok(ty::mk_mach_int(v)), UintType(v) => Ok(ty::mk_mach_uint(v)) diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 8be1700b635b4..ff686d1ae0958 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -512,6 +512,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sub, ""); } + infer::ProcCapture(span, id) => { + self.tcx.sess.span_err( + span, + format!("captured variable `{}` must be 'static \ + to be captured in a proc", + ty::local_var_name_str(self.tcx, id).get()) + .as_slice()); + note_and_explain_region( + self.tcx, + "captured variable is only valid for ", + sup, + ""); + } infer::IndexSlice(span) => { self.tcx.sess.span_err(span, "index of slice outside its lifetime"); @@ -619,6 +632,23 @@ impl<'a> ErrorReporting for InferCtxt<'a> { sup, ""); } + infer::TypeParameterBound(ident, span) => { + self.tcx.sess.span_err( + span, + format!("type parameter `{}` instantiated \ + with a type whose lifetime is too short", + ident.user_string(self.tcx)).as_slice()); + note_and_explain_region( + self.tcx, + "the type parameter requires ", + sub, + "..."); + note_and_explain_region( + self.tcx, + "...but the type is only valid for ", + sup, + ""); + } } } @@ -1256,11 +1286,11 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { bound_region_to_string(self.tcx, "lifetime parameter ", true, br)) } infer::EarlyBoundRegion(_, name) => { - format!(" for lifetime parameter `{}", + format!(" for lifetime parameter `{}`", token::get_name(name).get()) } infer::BoundRegionInCoherence(name) => { - format!(" for lifetime parameter `{} in coherence check", + format!(" for lifetime parameter `{}` in coherence check", token::get_name(name).get()) } infer::UpvarRegion(ref upvar_id, _) => { @@ -1361,6 +1391,15 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { self.tcx, id).get().to_string()).as_slice()); } + infer::ProcCapture(span, id) => { + self.tcx.sess.span_note( + span, + format!("...so that captured variable `{}` \ + is 'static", + ty::local_var_name_str( + self.tcx, + id).get()).as_slice()); + } infer::IndexSlice(span) => { self.tcx.sess.span_note( span, @@ -1396,7 +1435,7 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { infer::AutoBorrow(span) => { self.tcx.sess.span_note( span, - "...so that automatically reference is valid \ + "...so that auto-reference is valid \ at the time of borrow"); } infer::BindingTypeIsNotValidAtDecl(span) => { @@ -1410,6 +1449,13 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { "...so that the pointer does not outlive the \ data it points at"); } + infer::TypeParameterBound(ident, span) => { + self.tcx.sess.span_note( + span, + format!("...so that the type parameter `{}` meets its \ + declared bounds", + ident.user_string(self.tcx)).as_slice()); + } } } } diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index b6628c22ae60a..47d8785d47ffe 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -29,24 +29,28 @@ use util::common::{indenter}; use util::ppaux::mt_to_string; use util::ppaux::Repr; -pub struct Glb<'f>(pub CombineFields<'f>); // "greatest lower bound" (common subtype) +/// "Greatest lower bound" (common subtype) +pub struct Glb<'f> { + pub fields: CombineFields<'f> +} -impl<'f> Glb<'f> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Glb(ref v) = *self; v } +#[allow(non_snake_case_functions)] +pub fn Glb<'f>(cf: CombineFields<'f>) -> Glb<'f> { + Glb { fields: cf } } impl<'f> Combine for Glb<'f> { - fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx } + fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx } fn tag(&self) -> String { "glb".to_string() } - fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) } + fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) } + fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) } + fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) } fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres { - let tcx = self.get_ref().infcx.tcx; + let tcx = self.fields.infcx.tcx; debug!("{}.mts({}, {})", self.tag(), @@ -105,10 +109,10 @@ impl<'f> Combine for Glb<'f> { fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({:?}, {:?})", self.tag(), - a.repr(self.get_ref().infcx.tcx), - b.repr(self.get_ref().infcx.tcx)); + a.repr(self.fields.infcx.tcx), + b.repr(self.fields.infcx.tcx)); - Ok(self.get_ref().infcx.region_vars.glb_regions(Subtype(self.trace()), a, b)) + Ok(self.fields.infcx.region_vars.glb_regions(Subtype(self.trace()), a, b)) } fn contraregions(&self, a: ty::Region, b: ty::Region) @@ -125,33 +129,33 @@ impl<'f> Combine for Glb<'f> { // please see the large comment in `region_inference.rs`. debug!("{}.fn_sigs({:?}, {:?})", - self.tag(), a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx)); + self.tag(), a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx)); let _indenter = indenter(); // Make a mark so we can examine "all bindings that were // created as part of this type comparison". - let mark = self.get_ref().infcx.region_vars.mark(); + let mark = self.fields.infcx.region_vars.mark(); // Instantiate each bound region with a fresh region variable. let (a_with_fresh, a_map) = - self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions( + self.fields.infcx.replace_late_bound_regions_with_fresh_regions( self.trace(), a); let a_vars = var_ids(self, &a_map); let (b_with_fresh, b_map) = - self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions( + self.fields.infcx.replace_late_bound_regions_with_fresh_regions( self.trace(), b); let b_vars = var_ids(self, &b_map); // Collect constraints. - let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); - debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx)); + let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); + debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx)); // Generalize the regions appearing in fn_ty0 if possible let new_vars = - self.get_ref().infcx.region_vars.vars_created_since_mark(mark); + self.fields.infcx.region_vars.vars_created_since_mark(mark); let sig1 = fold_regions_in_sig( - self.get_ref().infcx.tcx, + self.fields.infcx.tcx, &sig0, |r| { generalize_region(self, @@ -163,7 +167,7 @@ impl<'f> Combine for Glb<'f> { b_vars.as_slice(), r) }); - debug!("sig1 = {}", sig1.repr(self.get_ref().infcx.tcx)); + debug!("sig1 = {}", sig1.repr(self.fields.infcx.tcx)); return Ok(sig1); fn generalize_region(this: &Glb, @@ -179,7 +183,7 @@ impl<'f> Combine for Glb<'f> { return r0; } - let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0); + let tainted = this.fields.infcx.region_vars.tainted(mark, r0); let mut a_r = None; let mut b_r = None; @@ -246,14 +250,14 @@ impl<'f> Combine for Glb<'f> { return ty::ReLateBound(new_binder_id, *a_br); } } - this.get_ref().infcx.tcx.sess.span_bug( - this.get_ref().trace.origin.span(), + this.fields.infcx.tcx.sess.span_bug( + this.fields.trace.origin.span(), format!("could not find original bound region for {:?}", r).as_slice()) } fn fresh_bound_variable(this: &Glb, binder_id: NodeId) -> ty::Region { - this.get_ref().infcx.region_vars.new_bound(binder_id) + this.fields.infcx.region_vars.new_bound(binder_id) } } } diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 708eb498f8421..8a40021ea9676 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -9,7 +9,6 @@ // except according to those terms. /*! - * * # Lattice Variables * * This file contains generic code for operating on inference variables @@ -343,7 +342,7 @@ pub trait TyLatticeDir { } impl<'f> LatticeDir for Lub<'f> { - fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() } + fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.fields.clone() } fn bnd(&self, b: &Bounds) -> Option { b.ub.clone() } fn with_bnd(&self, b: &Bounds, t: T) -> Bounds { Bounds { ub: Some(t), ..(*b).clone() } @@ -357,7 +356,7 @@ impl<'f> TyLatticeDir for Lub<'f> { } impl<'f> LatticeDir for Glb<'f> { - fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() } + fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.fields.clone() } fn bnd(&self, b: &Bounds) -> Option { b.lb.clone() } fn with_bnd(&self, b: &Bounds, t: T) -> Bounds { Bounds { lb: Some(t), ..(*b).clone() } diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 6a50038afe77f..8c5a2bdcbb2f9 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -28,24 +28,28 @@ use syntax::ast::{MutMutable, MutImmutable}; use util::ppaux::mt_to_string; use util::ppaux::Repr; -pub struct Lub<'f>(pub CombineFields<'f>); // least-upper-bound: common supertype +/// "Least upper bound" (common supertype) +pub struct Lub<'f> { + pub fields: CombineFields<'f> +} -impl<'f> Lub<'f> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Lub(ref v) = *self; v } +#[allow(non_snake_case_functions)] +pub fn Lub<'f>(cf: CombineFields<'f>) -> Lub<'f> { + Lub { fields: cf } } impl<'f> Combine for Lub<'f> { - fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx } + fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx } fn tag(&self) -> String { "lub".to_string() } - fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) } + fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) } + fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) } + fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) } fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres { - let tcx = self.get_ref().infcx.tcx; + let tcx = self.fields.infcx.tcx; debug!("{}.mts({}, {})", self.tag(), @@ -63,7 +67,7 @@ impl<'f> Combine for Lub<'f> { } MutMutable => { - self.get_ref().infcx.try(|| { + self.fields.infcx.try(|| { eq_tys(self, a.ty, b.ty).then(|| { Ok(ty::mt {ty: a.ty, mutbl: m}) }) @@ -104,10 +108,10 @@ impl<'f> Combine for Lub<'f> { fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({}, {})", self.tag(), - a.repr(self.get_ref().infcx.tcx), - b.repr(self.get_ref().infcx.tcx)); + a.repr(self.fields.infcx.tcx), + b.repr(self.fields.infcx.tcx)); - Ok(self.get_ref().infcx.region_vars.lub_regions(Subtype(self.trace()), a, b)) + Ok(self.fields.infcx.region_vars.lub_regions(Subtype(self.trace()), a, b)) } fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres { @@ -116,26 +120,26 @@ impl<'f> Combine for Lub<'f> { // Make a mark so we can examine "all bindings that were // created as part of this type comparison". - let mark = self.get_ref().infcx.region_vars.mark(); + let mark = self.fields.infcx.region_vars.mark(); // Instantiate each bound region with a fresh region variable. let (a_with_fresh, a_map) = - self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions( + self.fields.infcx.replace_late_bound_regions_with_fresh_regions( self.trace(), a); let (b_with_fresh, _) = - self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions( + self.fields.infcx.replace_late_bound_regions_with_fresh_regions( self.trace(), b); // Collect constraints. - let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); - debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx)); + let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); + debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx)); // Generalize the regions appearing in sig0 if possible let new_vars = - self.get_ref().infcx.region_vars.vars_created_since_mark(mark); + self.fields.infcx.region_vars.vars_created_since_mark(mark); let sig1 = fold_regions_in_sig( - self.get_ref().infcx.tcx, + self.fields.infcx.tcx, &sig0, |r| generalize_region(self, mark, new_vars.as_slice(), sig0.binder_id, &a_map, r)); @@ -155,7 +159,7 @@ impl<'f> Combine for Lub<'f> { return r0; } - let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0); + let tainted = this.fields.infcx.region_vars.tainted(mark, r0); // Variables created during LUB computation which are // *related* to regions that pre-date the LUB computation @@ -182,8 +186,8 @@ impl<'f> Combine for Lub<'f> { } } - this.get_ref().infcx.tcx.sess.span_bug( - this.get_ref().trace.origin.span(), + this.fields.infcx.tcx.sess.span_bug( + this.fields.trace.origin.span(), format!("region {:?} is not associated with \ any bound region from A!", r0).as_slice()) diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 30fffc42a3f97..e4b5591d5f78e 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -161,6 +161,9 @@ pub enum SubregionOrigin { // Closure bound must not outlive captured free variables FreeVariable(Span, ast::NodeId), + // Proc upvars must be 'static + ProcCapture(Span, ast::NodeId), + // Index into slice must be within its lifetime IndexSlice(Span), @@ -194,6 +197,10 @@ pub enum SubregionOrigin { // An auto-borrow that does not enclose the expr where it occurs AutoBorrow(Span), + + // Type parameter requires type to have certain lifetime + // (typically 'static). + TypeParameterBound(ast::Ident, Span), } /// Reasons to create a region inference variable @@ -519,13 +526,13 @@ impl<'a> InferCtxt<'a> { self.type_unification_table .borrow_mut() - .rollback_to(self.tcx, type_snapshot); + .rollback_to(type_snapshot); self.int_unification_table .borrow_mut() - .rollback_to(self.tcx, int_snapshot); + .rollback_to(int_snapshot); self.float_unification_table .borrow_mut() - .rollback_to(self.tcx, float_snapshot); + .rollback_to(float_snapshot); self.region_vars .rollback_to(region_vars_snapshot); } @@ -894,6 +901,7 @@ impl SubregionOrigin { InvokeClosure(a) => a, DerefPointer(a) => a, FreeVariable(a, _) => a, + ProcCapture(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, Reborrow(a) => a, @@ -905,6 +913,7 @@ impl SubregionOrigin { CallReturn(a) => a, AddrOf(a) => a, AutoBorrow(a) => a, + TypeParameterBound(_, a) => a, } } } @@ -927,6 +936,9 @@ impl Repr for SubregionOrigin { FreeVariable(a, b) => { format!("FreeVariable({}, {})", a.repr(tcx), b) } + ProcCapture(a, b) => { + format!("ProcCapture({}, {})", a.repr(tcx), b) + } IndexSlice(a) => { format!("IndexSlice({})", a.repr(tcx)) } @@ -948,6 +960,8 @@ impl Repr for SubregionOrigin { CallReturn(a) => format!("CallReturn({})", a.repr(tcx)), AddrOf(a) => format!("AddrOf({})", a.repr(tcx)), AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)), + TypeParameterBound(a,b) => format!("TypeParameterBound({},{})", + a.repr(tcx), b.repr(tcx)), } } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index dc674da38af5b..05a222bb3be72 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -191,7 +191,7 @@ impl<'a> RegionVarBindings<'a> { } pub fn commit(&self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: commit()"); + debug!("RegionVarBindings: commit({})", snapshot.length); assert!(self.undo_log.borrow().len() > snapshot.length); assert!(*self.undo_log.borrow().get(snapshot.length) == OpenSnapshot); diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 44c147bfe7f62..410035dea2cf8 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -26,35 +26,41 @@ use util::ppaux::{bound_region_to_string, Repr}; use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable}; -pub struct Sub<'f>(pub CombineFields<'f>); // "subtype", "subregion" etc -impl<'f> Sub<'f> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Sub(ref v) = *self; v } +/// "Greatest lower bound" (common subtype) +pub struct Sub<'f> { + fields: CombineFields<'f> +} + +#[allow(non_snake_case_functions)] +pub fn Sub<'f>(cf: CombineFields<'f>) -> Sub<'f> { + Sub { fields: cf } } impl<'f> Combine for Sub<'f> { - fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx } + fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx } fn tag(&self) -> String { "sub".to_string() } - fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) } + fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) } + fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) } + fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) } fn contratys(&self, a: ty::t, b: ty::t) -> cres { let opp = CombineFields { - a_is_expected: !self.get_ref().a_is_expected, - ..self.get_ref().clone() + a_is_expected: !self.fields.a_is_expected, + ..self.fields.clone() }; Sub(opp).tys(b, a) } fn contraregions(&self, a: ty::Region, b: ty::Region) - -> cres { + -> cres + { let opp = CombineFields { - a_is_expected: !self.get_ref().a_is_expected, - ..self.get_ref().clone() + a_is_expected: !self.fields.a_is_expected, + ..self.fields.clone() }; Sub(opp).regions(b, a) } @@ -62,16 +68,16 @@ impl<'f> Combine for Sub<'f> { fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({}, {})", self.tag(), - a.repr(self.get_ref().infcx.tcx), - b.repr(self.get_ref().infcx.tcx)); - self.get_ref().infcx.region_vars.make_subregion(Subtype(self.trace()), a, b); + a.repr(self.fields.infcx.tcx), + b.repr(self.fields.infcx.tcx)); + self.fields.infcx.region_vars.make_subregion(Subtype(self.trace()), a, b); Ok(a) } fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres { debug!("mts({} <: {})", - a.repr(self.get_ref().infcx.tcx), - b.repr(self.get_ref().infcx.tcx)); + a.repr(self.fields.infcx.tcx), + b.repr(self.fields.infcx.tcx)); if a.mutbl != b.mutbl { return Err(ty::terr_mutability); @@ -117,7 +123,7 @@ impl<'f> Combine for Sub<'f> { fn tys(&self, a: ty::t, b: ty::t) -> cres { debug!("{}.tys({}, {})", self.tag(), - a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx)); + a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx)); if a == b { return Ok(a); } let _indenter = indenter(); match (&ty::get(a).sty, &ty::get(b).sty) { @@ -126,15 +132,15 @@ impl<'f> Combine for Sub<'f> { } (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { - if_ok!(self.get_ref().var_sub_var(a_id, b_id)); + if_ok!(self.fields.var_sub_var(a_id, b_id)); Ok(a) } (&ty::ty_infer(TyVar(a_id)), _) => { - if_ok!(self.get_ref().var_sub_t(a_id, b)); + if_ok!(self.fields.var_sub_t(a_id, b)); Ok(a) } (_, &ty::ty_infer(TyVar(b_id))) => { - if_ok!(self.get_ref().t_sub_var(a, b_id)); + if_ok!(self.fields.t_sub_var(a, b_id)); Ok(a) } @@ -150,7 +156,7 @@ impl<'f> Combine for Sub<'f> { fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres { debug!("fn_sigs(a={}, b={})", - a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx)); + a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx)); let _indenter = indenter(); // Rather than checking the subtype relationship between `a` and `b` @@ -162,38 +168,38 @@ impl<'f> Combine for Sub<'f> { // Make a mark so we can examine "all bindings that were // created as part of this type comparison". - let mark = self.get_ref().infcx.region_vars.mark(); + let mark = self.fields.infcx.region_vars.mark(); // First, we instantiate each bound region in the subtype with a fresh // region variable. let (a_sig, _) = - self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions( + self.fields.infcx.replace_late_bound_regions_with_fresh_regions( self.trace(), a); // Second, we instantiate each bound region in the supertype with a // fresh concrete region. let (skol_map, b_sig) = { - replace_late_bound_regions_in_fn_sig(self.get_ref().infcx.tcx, b, |br| { - let skol = self.get_ref().infcx.region_vars.new_skolemized(br); + replace_late_bound_regions_in_fn_sig(self.fields.infcx.tcx, b, |br| { + let skol = self.fields.infcx.region_vars.new_skolemized(br); debug!("Bound region {} skolemized to {:?}", - bound_region_to_string(self.get_ref().infcx.tcx, "", false, br), + bound_region_to_string(self.fields.infcx.tcx, "", false, br), skol); skol }) }; - debug!("a_sig={}", a_sig.repr(self.get_ref().infcx.tcx)); - debug!("b_sig={}", b_sig.repr(self.get_ref().infcx.tcx)); + debug!("a_sig={}", a_sig.repr(self.fields.infcx.tcx)); + debug!("b_sig={}", b_sig.repr(self.fields.infcx.tcx)); // Compare types now that bound regions have been replaced. - let sig = if_ok!(super_fn_sigs(self, &a_sig, &b_sig)); + let sig = try!(super_fn_sigs(self, &a_sig, &b_sig)); // Presuming type comparison succeeds, we need to check // that the skolemized regions do not "leak". let new_vars = - self.get_ref().infcx.region_vars.vars_created_since_mark(mark); + self.fields.infcx.region_vars.vars_created_since_mark(mark); for (&skol_br, &skol) in skol_map.iter() { - let tainted = self.get_ref().infcx.region_vars.tainted(mark, skol); + let tainted = self.fields.infcx.region_vars.tainted(mark, skol); for tainted_region in tainted.iter() { // Each skolemized should only be relatable to itself // or new variables: @@ -210,16 +216,16 @@ impl<'f> Combine for Sub<'f> { if self.a_is_expected() { debug!("Not as polymorphic!"); return Err(ty::terr_regions_insufficiently_polymorphic( - skol_br, *tainted_region)); + skol_br, *tainted_region)); } else { debug!("Overly polymorphic!"); return Err(ty::terr_regions_overly_polymorphic( - skol_br, *tainted_region)); + skol_br, *tainted_region)); } } } return Ok(sig); } - } + diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index 44afc04d3f0ef..6f884091cb219 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -16,9 +16,9 @@ use middle::typeck::infer::{Bounds, uok, ures}; use middle::typeck::infer::InferCtxt; use std::cell::RefCell; use std::fmt::Show; -use std::mem; use syntax::ast; use util::ppaux::Repr; +use sv = util::snapshot_vec; /** * This trait is implemented by any type that can serve as a type @@ -82,13 +82,8 @@ pub struct UnificationTable { /** * Indicates the current value of each key. */ - values: Vec>, - /** - * When a snapshot is active, logs each change made to the table - * so that they can be unrolled. - */ - undo_log: Vec>, + values: sv::SnapshotVec,(),Delegate>, } /** @@ -96,29 +91,9 @@ pub struct UnificationTable { * made during the snapshot may either be *committed* or *rolled back*. */ pub struct Snapshot { - // Ensure that this snapshot is keyed to the table type. - marker1: marker::CovariantType, - - // Snapshots are tokens that should be created/consumed linearly. - marker2: marker::NoCopy, - - // Length of the undo log at the time the snapshot was taken. - length: uint, -} - -#[deriving(PartialEq)] -enum UndoLog { - /// Indicates where a snapshot started. - OpenSnapshot, - - /// Indicates a snapshot that has been committed. - CommittedSnapshot, - - /// New variable with given index was created. - NewVar(uint), - - /// Variable with given index was changed *from* the given value. - SetVar(uint, VarValue), + // Link snapshot to the key type `K` of the table. + marker: marker::CovariantType, + snapshot: sv::Snapshot, } /** @@ -131,6 +106,8 @@ pub struct Node { pub rank: uint, } +pub struct Delegate; + // We can't use V:LatticeValue, much as I would like to, // because frequently the pattern is that V=Bounds for some // other type parameter U, and we have no way to say @@ -139,77 +116,26 @@ pub struct Node { impl> UnificationTable { pub fn new() -> UnificationTable { UnificationTable { - values: Vec::new(), - undo_log: Vec::new() + values: sv::SnapshotVec::new(Delegate), } } - pub fn in_snapshot(&self) -> bool { - /*! True if a snapshot has been started. */ - - self.undo_log.len() > 0 - } - /** * Starts a new snapshot. Each snapshot must be either * rolled back or committed in a "LIFO" (stack) order. */ pub fn snapshot(&mut self) -> Snapshot { - let length = self.undo_log.len(); - debug!("{}: snapshot at length {}", - UnifyKey::tag(None::), - length); - self.undo_log.push(OpenSnapshot); - Snapshot { length: length, - marker1: marker::CovariantType, - marker2: marker::NoCopy } - } - - fn assert_open_snapshot(&self, snapshot: &Snapshot) { - // Or else there was a failure to follow a stack discipline: - assert!(self.undo_log.len() > snapshot.length); - - // Invariant established by start_snapshot(): - assert!(*self.undo_log.get(snapshot.length) == OpenSnapshot); + Snapshot { marker: marker::CovariantType::, + snapshot: self.values.start_snapshot() } } /** * Reverses all changes since the last snapshot. Also * removes any keys that have been created since then. */ - pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot) { - debug!("{}: rollback_to({})", - UnifyKey::tag(None::), - snapshot.length); - - self.assert_open_snapshot(&snapshot); - - while self.undo_log.len() > snapshot.length + 1 { - match self.undo_log.pop().unwrap() { - OpenSnapshot => { - // This indicates a failure to obey the stack discipline. - tcx.sess.bug("Cannot rollback an uncommitted snapshot"); - } - - CommittedSnapshot => { - // This occurs when there are nested snapshots and - // the inner is committed but outer is rolled back. - } - - NewVar(i) => { - assert!(self.values.len() == i); - self.values.pop(); - } - - SetVar(i, v) => { - *self.values.get_mut(i) = v; - } - } - } - - let v = self.undo_log.pop().unwrap(); - assert!(v == OpenSnapshot); - assert!(self.undo_log.len() == snapshot.length); + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("{}: rollback_to()", UnifyKey::tag(None::)); + self.values.rollback_to(snapshot.snapshot); } /** @@ -217,28 +143,12 @@ impl> UnificationTable { * can still be undone if there is a snapshot further out. */ pub fn commit(&mut self, snapshot: Snapshot) { - debug!("{}: commit({})", - UnifyKey::tag(None::), - snapshot.length); - - self.assert_open_snapshot(&snapshot); - - if snapshot.length == 0 { - // The root snapshot. - self.undo_log.truncate(0); - } else { - *self.undo_log.get_mut(snapshot.length) = CommittedSnapshot; - } + debug!("{}: commit()", UnifyKey::tag(None::)); + self.values.commit(snapshot.snapshot); } pub fn new_key(&mut self, value: V) -> K { - let index = self.values.len(); - - if self.in_snapshot() { - self.undo_log.push(NewVar(index)); - } - - self.values.push(Root(value, 0)); + let index = self.values.push(Root(value, 0)); let k = UnifyKey::from_index(index); debug!("{}: created new key: {}", UnifyKey::tag(None::), @@ -246,20 +156,6 @@ impl> UnificationTable { k } - fn swap_value(&mut self, - index: uint, - new_value: VarValue) - -> VarValue - { - /*! - * Primitive operation to swap a value in the var array. - * Caller should update the undo log if we are in a snapshot. - */ - - let loc = self.values.get_mut(index); - mem::replace(loc, new_value) - } - pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node { /*! * Find the root node for `vid`. This uses the standard @@ -274,15 +170,7 @@ impl> UnificationTable { let node: Node = self.get(tcx, redirect.clone()); if node.key != redirect { // Path compression - let old_value = - self.swap_value(index, Redirect(node.key.clone())); - - // If we are in a snapshot, record this compression, - // because it's possible that the unification which - // caused it will be rolled back later. - if self.in_snapshot() { - self.undo_log.push(SetVar(index, old_value)); - } + self.values.set(index, Redirect(node.key.clone())); } node } @@ -310,15 +198,12 @@ impl> UnificationTable { */ assert!(self.is_root(&key)); - assert!(self.in_snapshot()); debug!("Updating variable {} to {}", key.repr(tcx), new_value.repr(tcx)); - let index = key.index(); - let old_value = self.swap_value(index, new_value); - self.undo_log.push(SetVar(index, old_value)); + self.values.set(key.index(), new_value); } pub fn unify(&mut self, @@ -359,6 +244,12 @@ impl> UnificationTable { } } +impl sv::SnapshotVecDelegate,()> for Delegate { + fn reverse(&mut self, _: &mut Vec>, _: ()) { + fail!("Nothing to reverse"); + } +} + /////////////////////////////////////////////////////////////////////////// // Code to handle simple keys like ints, floats---anything that // doesn't have a subtyping relationship we need to worry about. @@ -373,7 +264,8 @@ pub trait SimplyUnifiable : Clone + PartialEq + Repr { pub fn err(a_is_expected: bool, a_t: V, - b_t: V) -> ures { + b_t: V) + -> ures { if a_is_expected { Err(SimplyUnifiable::to_type_err( ty::expected_found {expected: a_t, found: b_t})) diff --git a/src/librustc/util/snapshot_vec.rs b/src/librustc/util/snapshot_vec.rs new file mode 100644 index 0000000000000..94de07f6529d4 --- /dev/null +++ b/src/librustc/util/snapshot_vec.rs @@ -0,0 +1,195 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + * A utility class for implementing "snapshottable" things; a + * snapshottable data structure permits you to take a snapshot (via + * `start_snapshot`) and then, after making some changes, elect either + * to rollback to the start of the snapshot or commit those changes. + * + * This vector is intended to be used as part of an abstraction, not + * serve as a complete abstraction on its own. As such, while it will + * roll back most changes on its own, it also supports a `get_mut` + * operation that gives you an abitrary mutable pointer into the + * vector. To ensure that any changes you make this with this pointer + * are rolled back, you must invoke `record` to record any changes you + * make and also supplying a delegate capable of reversing those + * changes. + */ + +use std::kinds::marker; +use std::mem; + +#[deriving(PartialEq)] +enum UndoLog { + /// Indicates where a snapshot started. + OpenSnapshot, + + /// Indicates a snapshot that has been committed. + CommittedSnapshot, + + /// New variable with given index was created. + NewElem(uint), + + /// Variable with given index was changed *from* the given value. + SetElem(uint, T), + + /// Extensible set of actions + Other(U) +} + +pub struct SnapshotVec { + values: Vec, + undo_log: Vec>, + delegate: D +} + +pub struct Snapshot { + // Snapshots are tokens that should be created/consumed linearly. + marker: marker::NoCopy, + + // Length of the undo log at the time the snapshot was taken. + length: uint, +} + +pub trait SnapshotVecDelegate { + fn reverse(&mut self, values: &mut Vec, action: U); +} + +impl> SnapshotVec { + pub fn new(delegate: D) -> SnapshotVec { + SnapshotVec { + values: Vec::new(), + undo_log: Vec::new(), + delegate: delegate + } + } + + fn in_snapshot(&self) -> bool { + !self.undo_log.is_empty() + } + + pub fn record(&mut self, action: U) { + if self.in_snapshot() { + self.undo_log.push(Other(action)); + } + } + + pub fn push(&mut self, elem: T) -> uint { + let len = self.values.len(); + self.values.push(elem); + + if self.in_snapshot() { + self.undo_log.push(NewElem(len)); + } + + len + } + + pub fn get<'a>(&'a self, index: uint) -> &'a T { + self.values.get(index) + } + + pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T { + /*! + * Returns a mutable pointer into the vec; whatever changes + * you make here cannot be undone automatically, so you should + * be sure call `record()` with some sort of suitable undo + * action. + */ + + self.values.get_mut(index) + } + + pub fn set(&mut self, index: uint, new_elem: T) { + /*! + * Updates the element at the given index. The old value will + * saved (and perhaps restored) if a snapshot is active. + */ + + let old_elem = mem::replace(self.values.get_mut(index), new_elem); + if self.in_snapshot() { + self.undo_log.push(SetElem(index, old_elem)); + } + } + + pub fn start_snapshot(&mut self) -> Snapshot { + let length = self.undo_log.len(); + self.undo_log.push(OpenSnapshot); + Snapshot { length: length, + marker: marker::NoCopy } + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot) { + // Or else there was a failure to follow a stack discipline: + assert!(self.undo_log.len() > snapshot.length); + + // Invariant established by start_snapshot(): + assert!( + match *self.undo_log.get(snapshot.length) { + OpenSnapshot => true, + _ => false + }); + } + + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("rollback_to({})", snapshot.length); + + self.assert_open_snapshot(&snapshot); + + while self.undo_log.len() > snapshot.length + 1 { + match self.undo_log.pop().unwrap() { + OpenSnapshot => { + // This indicates a failure to obey the stack discipline. + fail!("Cannot rollback an uncommited snapshot"); + } + + CommittedSnapshot => { + // This occurs when there are nested snapshots and + // the inner is commited but outer is rolled back. + } + + NewElem(i) => { + assert!(self.values.len() == i); + self.values.pop(); + } + + SetElem(i, v) => { + *self.values.get_mut(i) = v; + } + + Other(u) => { + self.delegate.reverse(&mut self.values, u); + } + } + } + + let v = self.undo_log.pop().unwrap(); + assert!(match v { OpenSnapshot => true, _ => false }); + assert!(self.undo_log.len() == snapshot.length); + } + + /** + * Commits all changes since the last snapshot. Of course, they + * can still be undone if there is a snapshot further out. + */ + pub fn commit(&mut self, snapshot: Snapshot) { + debug!("commit({})", snapshot.length); + + self.assert_open_snapshot(&snapshot); + + if snapshot.length == 0 { + // The root snapshot. + self.undo_log.truncate(0); + } else { + *self.undo_log.get_mut(snapshot.length) = CommittedSnapshot; + } + } +} diff --git a/src/test/compile-fail/kindck-inherited-copy-bound.rs b/src/test/compile-fail/kindck-inherited-copy-bound.rs new file mode 100644 index 0000000000000..2520ed215d55e --- /dev/null +++ b/src/test/compile-fail/kindck-inherited-copy-bound.rs @@ -0,0 +1,30 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that Copy bounds inherited by trait are checked. + +use std::any::Any; +use std::any::AnyRefExt; + +trait Foo : Copy { +} + +impl Foo for T { +} + +fn take_param(foo: &T) { } + +fn main() { + let x = box 3i; + take_param(&x); //~ ERROR does not fulfill `Copy` + + let y = &x; + let z = &x as &Foo; //~ ERROR does not fulfill `Copy` +} diff --git a/src/test/compile-fail/proc-static-bound.rs b/src/test/compile-fail/proc-static-bound.rs index f11ddc0151f96..260cd8c627c31 100644 --- a/src/test/compile-fail/proc-static-bound.rs +++ b/src/test/compile-fail/proc-static-bound.rs @@ -12,11 +12,10 @@ fn main() { let mut x = Some(1); let mut p: proc(&mut Option) = proc(_) {}; match x { - Some(ref y) => { + Some(ref y) => { //~ ERROR does not live long enough p = proc(z: &mut Option) { *z = None; let _ = y; - //~^ ERROR cannot capture variable of type `&int`, which does not fulfill `'static` }; } None => {} diff --git a/src/test/compile-fail/regions-infer-proc-static-upvar.rs b/src/test/compile-fail/regions-infer-proc-static-upvar.rs new file mode 100644 index 0000000000000..7fe85290da0ec --- /dev/null +++ b/src/test/compile-fail/regions-infer-proc-static-upvar.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that, when a variable of type `&T` is captured inside a proc, +// we correctly infer/require that its lifetime is 'static. + +fn foo(_p: proc():'static) { } + +static i: int = 3; + +fn capture_local() { + let x = 3i; + let y = &x; //~ ERROR `x` does not live long enough + foo(proc() { + let _a = *y; + }); +} + +fn capture_static() { + // Legal because &i can have static lifetime: + let y = &i; + foo(proc() { + let _a = *y; + }); +} + +fn main() { } diff --git a/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs b/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs new file mode 100644 index 0000000000000..efe3994dbb7b9 --- /dev/null +++ b/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test an edge case in region inference: the lifetime of the borrow +// of `*x` must be extended to at least 'a. + +fn foo<'a,'b>(x: &'a &'b mut int) -> &'a int { + let y = &*x; // should be inferred to have type &'a &'b mut int... + + // ...because if we inferred, say, &'x &'b mut int where 'x <= 'a, + // this reborrow would be illegal: + &**y +} + +pub fn main() { + /* Just want to know that it compiles. */ +} diff --git a/src/test/run-pass/regions-infer-static-from-bound.rs b/src/test/run-pass/regions-infer-static-from-bound.rs new file mode 100644 index 0000000000000..52e57e14d3a72 --- /dev/null +++ b/src/test/run-pass/regions-infer-static-from-bound.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that a 'static bound influences the lifetime we infer for a borrow. +// This test should compile. + +static i: uint = 3; +fn foo1(t: T) { } +fn foo2(t: T) { } +pub fn main() { + foo1(&i); + foo2(&i); +} diff --git a/src/test/run-pass/regions-infer-static-from-proc.rs b/src/test/run-pass/regions-infer-static-from-proc.rs new file mode 100644 index 0000000000000..823644ddfb519 --- /dev/null +++ b/src/test/run-pass/regions-infer-static-from-proc.rs @@ -0,0 +1,23 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that the 'static bound on a proc influences lifetimes of +// region variables contained within (otherwise, region inference will +// give `x` a very short lifetime). + +static i: uint = 3; +fn foo(_: proc():'static) {} +fn read(_: uint) { } +pub fn main() { + let x = &i; + foo(proc() { + read(*x); + }); +}