From 53c1956cfbbe8a5957bfa62f57bfb0e4b999a7b0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 24 Jan 2015 17:32:10 -0500 Subject: [PATCH 01/10] Introduce the notion of deferred resolutions and use it to hold off on doing the final checking for closure calls until after trait inference is performed. This isn't important now, but it's essential if we are to delay inferring the closure kind. --- src/librustc_typeck/check/_match.rs | 31 +-- src/librustc_typeck/check/callee.rs | 188 +++++++++++++++--- src/librustc_typeck/check/closure.rs | 8 +- src/librustc_typeck/check/method/confirm.rs | 12 +- src/librustc_typeck/check/method/mod.rs | 4 +- src/librustc_typeck/check/mod.rs | 206 +++++++++++--------- src/librustc_typeck/check/vtable.rs | 3 + 7 files changed, 308 insertions(+), 144 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index cb4c880717bc8..c4b7ffb87296e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -30,7 +30,7 @@ use syntax::print::pprust; use syntax::ptr::P; pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, - pat: &ast::Pat, + pat: &'tcx ast::Pat, expected: Ty<'tcx>) { let fcx = pcx.fcx; @@ -157,9 +157,10 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, } ast::PatIdent(_, ref path, _) => { let path = ast_util::ident_to_path(path.span, path.node); - check_pat_enum(pcx, pat, &path, &Some(vec![]), expected); + check_pat_enum(pcx, pat, &path, Some(&[]), expected); } ast::PatEnum(ref path, ref subpats) => { + let subpats = subpats.as_ref().map(|v| &v[]); check_pat_enum(pcx, pat, path, subpats, expected); } ast::PatStruct(ref path, ref fields, etc) => { @@ -335,9 +336,9 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, } pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, - discrim: &ast::Expr, - arms: &[ast::Arm], + expr: &'tcx ast::Expr, + discrim: &'tcx ast::Expr, + arms: &'tcx [ast::Arm], expected: Expectation<'tcx>, match_src: ast::MatchSource) { let tcx = fcx.ccx.tcx; @@ -424,8 +425,8 @@ pub struct pat_ctxt<'a, 'tcx: 'a> { pub map: PatIdMap, } -pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, - path: &ast::Path, fields: &[Spanned], +pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat, + path: &ast::Path, fields: &'tcx [Spanned], etc: bool, expected: Ty<'tcx>) { let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; @@ -483,10 +484,12 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, variant_def_id, etc); } -pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, - path: &ast::Path, subpats: &Option>>, - expected: Ty<'tcx>) { - +pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, + pat: &ast::Pat, + path: &ast::Path, + subpats: Option<&'tcx [P]>, + expected: Ty<'tcx>) +{ // Typecheck the path. let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; @@ -536,7 +539,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, "`{}` does not name a non-struct variant or a tuple struct", name); fcx.write_error(pat.id); - if let Some(ref subpats) = *subpats { + if let Some(subpats) = subpats { for pat in subpats.iter() { check_pat(pcx, &**pat, tcx.types.err); } @@ -545,7 +548,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, } }; - if let Some(ref subpats) = *subpats { + if let Some(subpats) = subpats { if subpats.len() == arg_tys.len() { for (subpat, arg_ty) in subpats.iter().zip(arg_tys.iter()) { check_pat(pcx, &**subpat, *arg_ty); @@ -579,7 +582,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat, /// `etc` is true if the pattern said '...' and false otherwise. pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, span: Span, - fields: &[Spanned], + fields: &'tcx [Spanned], struct_fields: &[ty::field<'tcx>], struct_id: ast::DefId, etc: bool) { diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index e37c1ab7f0cb7..dc8bde9f84d6d 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -13,6 +13,8 @@ use super::AutorefArgs; use super::check_argument_types; use super::check_expr; use super::check_method_argument_types; +use super::demand; +use super::DeferredResolution; use super::err_args; use super::Expectation; use super::expected_types_for_fn_args; @@ -24,13 +26,14 @@ use super::TupleArgumentsFlag; use super::UnresolvedTypeAction; use super::write_call; +use CrateCtxt; use middle::infer; -use middle::ty::{self, Ty}; +use middle::ty::{self, Ty, ClosureTyper}; use syntax::ast; use syntax::codemap::Span; use syntax::parse::token; use syntax::ptr::P; -use CrateCtxt; +use util::ppaux::Repr; /// Check that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -66,9 +69,9 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id: } pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - call_expr: &ast::Expr, - callee_expr: &ast::Expr, - arg_exprs: &[P], + call_expr: &'tcx ast::Expr, + callee_expr: &'tcx ast::Expr, + arg_exprs: &'tcx [P], expected: Expectation<'tcx>) { check_expr(fcx, callee_expr); @@ -96,24 +99,35 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected); } + Some(CallStep::Closure(fn_sig)) => { + confirm_closure_call(fcx, call_expr, arg_exprs, expected, fn_sig); + } + Some(CallStep::Overloaded(method_callee)) => { - confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee, expected); + confirm_overloaded_call(fcx, call_expr, callee_expr, + arg_exprs, expected, method_callee); } } } enum CallStep<'tcx> { Builtin, + Closure(ty::FnSig<'tcx>), Overloaded(ty::MethodCallee<'tcx>) } fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - call_expr: &ast::Expr, - callee_expr: &ast::Expr, + call_expr: &'tcx ast::Expr, + callee_expr: &'tcx ast::Expr, adjusted_ty: Ty<'tcx>, autoderefref: ty::AutoDerefRef<'tcx>) -> Option> { + debug!("try_overloaded_call_step(call_expr={}, adjusted_ty={}, autoderefref={})", + call_expr.repr(fcx.tcx()), + adjusted_ty.repr(fcx.tcx()), + autoderefref.repr(fcx.tcx())); + // If the callee is a bare function or a closure, then we're all set. match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty { ty::ty_bare_fn(..) => { @@ -123,9 +137,37 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, return Some(CallStep::Builtin); } + ty::ty_closure(def_id, _, substs) => { + let closure_ty = + fcx.closure_type(def_id, substs); + let fn_sig = + fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span, + infer::FnCall, + &closure_ty.sig).0; + fcx.record_deferred_resolution(box CallResolution { + call_expr: call_expr, + callee_expr: callee_expr, + adjusted_ty: adjusted_ty, + autoderefref: autoderefref, + fn_sig: fn_sig.clone(), + }); + return Some(CallStep::Closure(fn_sig)); + } + _ => {} } + try_overloaded_call_traits(fcx, call_expr, callee_expr, adjusted_ty, autoderefref) + .map(|method_callee| CallStep::Overloaded(method_callee)) +} + +fn try_overloaded_call_traits<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, + call_expr: &ast::Expr, + callee_expr: &ast::Expr, + adjusted_ty: Ty<'tcx>, + autoderefref: ty::AutoDerefRef<'tcx>) + -> Option> +{ // Try the options that are least restrictive on the caller first. for &(opt_trait_def_id, method_name) in [ (fcx.tcx().lang_items.fn_trait(), token::intern("call")), @@ -147,7 +189,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, None) { None => continue, Some(method_callee) => { - return Some(CallStep::Overloaded(method_callee)); + return Some(method_callee); } } } @@ -158,7 +200,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, call_expr: &ast::Expr, callee_ty: Ty<'tcx>, - arg_exprs: &[P], + arg_exprs: &'tcx [P], expected: Expectation<'tcx>) { let error_fn_sig; @@ -215,22 +257,124 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, write_call(fcx, call_expr, fn_sig.output); } +fn confirm_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + call_expr: &ast::Expr, + arg_exprs: &'tcx [P], + expected: Expectation<'tcx>, + fn_sig: ty::FnSig<'tcx>) +{ + // `fn_sig` is the *signature* of the cosure being called. We + // don't know the full details yet (`Fn` vs `FnMut` etc), but we + // do know the types expected for each argument and the return + // type. + + let expected_arg_tys = + expected_types_for_fn_args(fcx, + call_expr.span, + expected, + fn_sig.output.clone(), + &*fn_sig.inputs); + + check_argument_types(fcx, + call_expr.span, + &*fn_sig.inputs, + &*expected_arg_tys, + arg_exprs, + AutorefArgs::No, + fn_sig.variadic, + TupleArgumentsFlag::TupleArguments); + + write_call(fcx, call_expr, fn_sig.output); +} + fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, call_expr: &ast::Expr, - arg_exprs: &[P], - method_callee: ty::MethodCallee<'tcx>, - expected: Expectation<'tcx>) + callee_expr: &'tcx ast::Expr, + arg_exprs: &'tcx [P], + expected: Expectation<'tcx>, + method_callee: ty::MethodCallee<'tcx>) { - let output_type = check_method_argument_types(fcx, - call_expr.span, - method_callee.ty, - call_expr, - arg_exprs, - AutorefArgs::No, - TupleArgumentsFlag::TupleArguments, - expected); + let output_type = + check_method_argument_types(fcx, + call_expr.span, + method_callee.ty, + callee_expr, + arg_exprs, + AutorefArgs::No, + TupleArgumentsFlag::TupleArguments, + expected); + write_call(fcx, call_expr, output_type); + + write_overloaded_call_method_map(fcx, call_expr, method_callee); +} + +fn write_overloaded_call_method_map<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, + call_expr: &ast::Expr, + method_callee: ty::MethodCallee<'tcx>) { let method_call = ty::MethodCall::expr(call_expr.id); fcx.inh.method_map.borrow_mut().insert(method_call, method_callee); - write_call(fcx, call_expr, output_type); } +struct CallResolution<'tcx> { + call_expr: &'tcx ast::Expr, + callee_expr: &'tcx ast::Expr, + adjusted_ty: Ty<'tcx>, + autoderefref: ty::AutoDerefRef<'tcx>, + fn_sig: ty::FnSig<'tcx>, +} + +impl<'tcx> Repr<'tcx> for CallResolution<'tcx> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \ + autoderefref={}, fn_sig={})", + self.call_expr.repr(tcx), + self.callee_expr.repr(tcx), + self.adjusted_ty.repr(tcx), + self.autoderefref.repr(tcx), + self.fn_sig.repr(tcx)) + } +} + +impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> { + fn attempt_resolution<'a>(&self, fcx: &FnCtxt<'a,'tcx>) -> bool { + debug!("attempt_resolution() {}", + self.repr(fcx.tcx())); + + // We may now know enough to figure out fn vs fnmut etc. + match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr, + self.adjusted_ty, self.autoderefref.clone()) { + None => false, + Some(method_callee) => { + // One problem is that when we get here, we are going + // to have a newly instantiated function signature + // from the call trait. This has to be reconciled with + // the older function signature we had before. In + // principle we *should* be able to fn_sigs(), but we + // can't because of the annoying need for a TypeTrace. + // (This always bites me, should find a way to + // refactor it.) + let method_sig = + ty::assert_no_late_bound_regions(fcx.tcx(), + ty::ty_fn_sig(method_callee.ty)); + + debug!("attempt_resolution: method_callee={}", + method_callee.repr(fcx.tcx())); + + for (&method_arg_ty, &self_arg_ty) in + method_sig.inputs[1..].iter().zip(self.fn_sig.inputs.iter()) + { + demand::eqtype(fcx, self.call_expr.span, self_arg_ty, method_arg_ty); + } + + demand::eqtype(fcx, + self.call_expr.span, + method_sig.output.unwrap(), + self.fn_sig.output.unwrap()); + + write_overloaded_call_method_map(fcx, self.call_expr, method_callee); + + true + } + } + } +} diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index bdae34e787850..906a8a33314cc 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -26,8 +26,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, _capture: ast::CaptureClause, opt_kind: Option, - decl: &ast::FnDecl, - body: &ast::Block, + decl: &'tcx ast::FnDecl, + body: &'tcx ast::Block, expected: Expectation<'tcx>) { debug!("check_expr_closure(expr={},expected={})", expr.repr(fcx.tcx()), @@ -76,8 +76,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, kind: ty::ClosureKind, - decl: &ast::FnDecl, - body: &ast::Block, + decl: &'tcx ast::FnDecl, + body: &'tcx ast::Block, expected_sig: Option>) { let expr_def_id = ast_util::local_def(expr.id); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 56a32186c9eac..c326116cbd544 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -31,8 +31,8 @@ use util::ppaux::Repr; struct ConfirmContext<'a, 'tcx:'a> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, - self_expr: &'a ast::Expr, - call_expr: &'a ast::Expr, + self_expr: &'tcx ast::Expr, + call_expr: &'tcx ast::Expr, } struct InstantiatedMethodSig<'tcx> { @@ -51,8 +51,8 @@ struct InstantiatedMethodSig<'tcx> { pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, - self_expr: &ast::Expr, - call_expr: &ast::Expr, + self_expr: &'tcx ast::Expr, + call_expr: &'tcx ast::Expr, unadjusted_self_ty: Ty<'tcx>, pick: probe::Pick<'tcx>, supplied_method_types: Vec>) @@ -70,8 +70,8 @@ pub fn confirm<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, impl<'a,'tcx> ConfirmContext<'a,'tcx> { fn new(fcx: &'a FnCtxt<'a, 'tcx>, span: Span, - self_expr: &'a ast::Expr, - call_expr: &'a ast::Expr) + self_expr: &'tcx ast::Expr, + call_expr: &'tcx ast::Expr) -> ConfirmContext<'a, 'tcx> { ConfirmContext { fcx: fcx, span: span, self_expr: self_expr, call_expr: call_expr } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index d92cc1dfc1e95..24e9f1c8720ee 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -90,8 +90,8 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, method_name: ast::Name, self_ty: Ty<'tcx>, supplied_method_types: Vec>, - call_expr: &ast::Expr, - self_expr: &ast::Expr) + call_expr: &'tcx ast::Expr, + self_expr: &'tcx ast::Expr) -> Result, MethodError> { debug!("lookup(method_name={}, self_ty={}, call_expr={}, self_expr={})", diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9dcde1c2a0a50..565e96483b9a2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -170,8 +170,17 @@ pub struct Inherited<'a, 'tcx: 'a> { // Tracks trait obligations incurred during this function body. fulfillment_cx: RefCell>, + + // + deferred_resolutions: RefCell>>, +} + +trait DeferredResolution<'tcx> { + fn attempt_resolution<'a>(&self, fcx: &FnCtxt<'a,'tcx>) -> bool; } +type DeferredResolutionHandler<'tcx> = Box+'tcx>; + /// When type-checking an expression, we propagate downward /// whatever type hint we are able in the form of an `Expectation`. #[derive(Copy)] @@ -377,6 +386,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { closures: RefCell::new(DefIdMap()), fn_sig_map: RefCell::new(NodeMap()), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), + deferred_resolutions: RefCell::new(Vec::new()), } } @@ -425,13 +435,13 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>) struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } -impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &ast::Item) { +impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> { + fn visit_item(&mut self, i: &'tcx ast::Item) { check_item(self.ccx, i); visit::walk_item(self, i); } - fn visit_ty(&mut self, t: &ast::Ty) { + fn visit_ty(&mut self, t: &'tcx ast::Ty) { match t.node { ast::TyFixedLengthVec(_, ref expr) => { check_const_in_type(self.ccx, &**expr, self.ccx.tcx.types.uint); @@ -459,8 +469,8 @@ pub fn check_item_types(ccx: &CrateCtxt) { } fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, - decl: &ast::FnDecl, - body: &ast::Block, + decl: &'tcx ast::FnDecl, + body: &'tcx ast::Block, id: ast::NodeId, raw_fty: Ty<'tcx>, param_env: ty::ParameterEnvironment<'a, 'tcx>) @@ -512,9 +522,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { } } -impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. - fn visit_local(&mut self, local: &ast::Local) { + fn visit_local(&mut self, local: &'tcx ast::Local) { let o_ty = match local.ty { Some(ref ty) => Some(self.fcx.to_ty(&**ty)), None => None @@ -528,7 +538,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { } // Add pattern bindings. - fn visit_pat(&mut self, p: &ast::Pat) { + fn visit_pat(&mut self, p: &'tcx ast::Pat) { if let ast::PatIdent(_, ref path1, _) = p.node { if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) { let var_ty = self.assign(p.span, p.id, None); @@ -546,7 +556,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { visit::walk_pat(self, p); } - fn visit_block(&mut self, b: &ast::Block) { + fn visit_block(&mut self, b: &'tcx ast::Block) { // non-obvious: the `blk` variable maps to region lb, so // we have to keep this up-to-date. This // is... unfortunate. It'd be nice to not need this. @@ -555,7 +565,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { // Since an expr occurs as part of the type fixed size arrays we // need to record the type for that node - fn visit_ty(&mut self, t: &ast::Ty) { + fn visit_ty(&mut self, t: &'tcx ast::Ty) { match t.node { ast::TyFixedLengthVec(ref ty, ref count_expr) => { self.visit_ty(&**ty); @@ -566,8 +576,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { } // Don't descend into fns and items - fn visit_fn(&mut self, _: visit::FnKind<'v>, _: &'v ast::FnDecl, - _: &'v ast::Block, _: Span, _: ast::NodeId) { } + fn visit_fn(&mut self, _: visit::FnKind<'tcx>, _: &'tcx ast::FnDecl, + _: &'tcx ast::Block, _: Span, _: ast::NodeId) { } fn visit_item(&mut self, _: &ast::Item) { } } @@ -582,9 +592,9 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, unsafety: ast::Unsafety, unsafety_id: ast::NodeId, fn_sig: &ty::FnSig<'tcx>, - decl: &ast::FnDecl, + decl: &'tcx ast::FnDecl, fn_id: ast::NodeId, - body: &ast::Block, + body: &'tcx ast::Block, inherited: &'a Inherited<'a, 'tcx>) -> FnCtxt<'a, 'tcx> { @@ -677,7 +687,7 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { } } -pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) { +pub fn check_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { debug!("check_item(it.id={}, it.ident={})", it.id, ty::item_path_str(ccx.tcx, local_def(it.id))); @@ -829,7 +839,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, /// * `method`: the method definition fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, item_generics: &ty::Generics<'tcx>, - method: &ast::Method) { + method: &'tcx ast::Method) { debug!("check_method_body(item_generics={}, method.id={})", item_generics.repr(ccx.tcx), method.id); @@ -1133,10 +1143,10 @@ fn check_cast_inner<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } } -fn check_cast(fcx: &FnCtxt, - cast_expr: &ast::Expr, - e: &ast::Expr, - t: &ast::Ty) { +fn check_cast<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + cast_expr: &ast::Expr, + e: &'tcx ast::Expr, + t: &ast::Ty) { let id = cast_expr.id; let span = cast_expr.span; @@ -1279,6 +1289,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) } } + fn record_deferred_resolution(&self, r: DeferredResolutionHandler<'tcx>) { + self.inh.deferred_resolutions.borrow_mut().push(r); + } + pub fn tag(&self) -> String { format!("{:?}", self as *const FnCtxt) } @@ -2068,7 +2082,7 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, method_call: MethodCall, expr: &ast::Expr, - base_expr: &ast::Expr, + base_expr: &'tcx ast::Expr, adjusted_ty: Ty<'tcx>, adjustment: ty::AutoDerefRef<'tcx>, lvalue_pref: LvaluePreference, @@ -2138,8 +2152,8 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, method_fn_ty: Ty<'tcx>, - callee_expr: &ast::Expr, - args_no_rcvr: &[P], + callee_expr: &'tcx ast::Expr, + args_no_rcvr: &'tcx [P], autoref_args: AutorefArgs, tuple_arguments: TupleArgumentsFlag, expected: Expectation<'tcx>) @@ -2194,7 +2208,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, fn_inputs: &[Ty<'tcx>], expected_arg_tys: &[Ty<'tcx>], - args: &[P], + args: &'tcx [P], autoref_args: AutorefArgs, variadic: bool, tuple_arguments: TupleArgumentsFlag) { @@ -2462,7 +2476,7 @@ pub fn valid_range_bounds(ccx: &CrateCtxt, } pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected: Ty<'tcx>) { check_expr_with_unifier( fcx, expr, ExpectHasType(expected), NoPreference, @@ -2470,14 +2484,14 @@ pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } fn check_expr_coercable_to_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected: Ty<'tcx>) { check_expr_with_unifier( fcx, expr, ExpectHasType(expected), NoPreference, || demand::coerce(fcx, expr.span, expected, expr)); } -fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr, +fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx ast::Expr, expected: Ty<'tcx>) { check_expr_with_unifier( fcx, expr, ExpectHasType(expected), NoPreference, @@ -2485,7 +2499,7 @@ fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr, } fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected: Expectation<'tcx>) { check_expr_with_unifier( fcx, expr, expected, NoPreference, @@ -2493,19 +2507,19 @@ fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected: Expectation<'tcx>, lvalue_pref: LvaluePreference) { check_expr_with_unifier(fcx, expr, expected, lvalue_pref, || ()) } -fn check_expr(fcx: &FnCtxt, expr: &ast::Expr) { +fn check_expr<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr) { check_expr_with_unifier(fcx, expr, NoExpectation, NoPreference, || ()) } -fn check_expr_with_lvalue_pref(fcx: &FnCtxt, expr: &ast::Expr, - lvalue_pref: LvaluePreference) { +fn check_expr_with_lvalue_pref<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &'tcx ast::Expr, + lvalue_pref: LvaluePreference) { check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ()) } @@ -2613,7 +2627,7 @@ fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, /// that there are actually multiple representations for `ty_err`, so avoid /// that when err needs to be handled differently. fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected: Expectation<'tcx>, lvalue_pref: LvaluePreference, unifier: F) where @@ -2624,9 +2638,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // Checks a method call. fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, method_name: ast::SpannedIdent, - args: &[P], + args: &'tcx [P], tps: &[P], expected: Expectation<'tcx>, lvalue_pref: LvaluePreference) { @@ -2675,9 +2689,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // A generic function for checking the then and else in an if // or if-else. fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - cond_expr: &ast::Expr, - then_blk: &ast::Block, - opt_else_expr: Option<&ast::Expr>, + cond_expr: &'tcx ast::Expr, + then_blk: &'tcx ast::Block, + opt_else_expr: Option<&'tcx ast::Expr>, id: ast::NodeId, sp: Span, expected: Expectation<'tcx>) { @@ -2717,12 +2731,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>, - op_ex: &ast::Expr, + op_ex: &'tcx ast::Expr, lhs_ty: Ty<'tcx>, opname: ast::Name, trait_did: Option, lhs: &'a ast::Expr, - rhs: Option<&P>, + rhs: Option<&'tcx P>, unbound_method: F, autoref_args: AutorefArgs) -> Ty<'tcx> where F: FnOnce(), @@ -2803,12 +2817,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } // could be either an expr_binop or an expr_assign_binop - fn check_binop(fcx: &FnCtxt, - expr: &ast::Expr, - op: ast::BinOp, - lhs: &ast::Expr, - rhs: &P, - is_binop_assignment: IsBinopAssignment) { + fn check_binop<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &'tcx ast::Expr, + op: ast::BinOp, + lhs: &'tcx ast::Expr, + rhs: &'tcx P, + is_binop_assignment: IsBinopAssignment) { let tcx = fcx.ccx.tcx; let lvalue_pref = match is_binop_assignment { @@ -2923,11 +2937,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - ex: &ast::Expr, - lhs_expr: &ast::Expr, + ex: &'tcx ast::Expr, + lhs_expr: &'tcx ast::Expr, lhs_resolved_t: Ty<'tcx>, op: ast::BinOp, - rhs: &P) -> Ty<'tcx> { + rhs: &'tcx P) -> Ty<'tcx> { let tcx = fcx.ccx.tcx; let lang = &tcx.lang_items; let (name, trait_did) = match op.node { @@ -2966,8 +2980,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, op_str: &str, mname: &str, trait_did: Option, - ex: &ast::Expr, - rhs_expr: &ast::Expr, + ex: &'tcx ast::Expr, + rhs_expr: &'tcx ast::Expr, rhs_t: Ty<'tcx>, op: ast::UnOp) -> Ty<'tcx> { lookup_op_method(fcx, ex, rhs_t, token::intern(mname), @@ -2980,11 +2994,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } // Check field access expressions - fn check_field(fcx: &FnCtxt, - expr: &ast::Expr, - lvalue_pref: LvaluePreference, - base: &ast::Expr, - field: &ast::SpannedIdent) { + fn check_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &'tcx ast::Expr, + lvalue_pref: LvaluePreference, + base: &'tcx ast::Expr, + field: &ast::SpannedIdent) { let tcx = fcx.ccx.tcx; check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, @@ -3077,11 +3091,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } // Check tuple index expressions - fn check_tup_field(fcx: &FnCtxt, - expr: &ast::Expr, - lvalue_pref: LvaluePreference, - base: &ast::Expr, - idx: codemap::Spanned) { + fn check_tup_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &'tcx ast::Expr, + lvalue_pref: LvaluePreference, + base: &'tcx ast::Expr, + idx: codemap::Spanned) { let tcx = fcx.ccx.tcx; check_expr_with_lvalue_pref(fcx, base, lvalue_pref); let expr_t = structurally_resolved_type(fcx, expr.span, @@ -3149,7 +3163,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, node_id: ast::NodeId, substitutions: &'tcx subst::Substs<'tcx>, field_types: &[ty::field_ty], - ast_fields: &[ast::Field], + ast_fields: &'tcx [ast::Field], check_completeness: bool, enum_id_opt: Option) { let tcx = fcx.ccx.tcx; @@ -3252,12 +3266,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } - fn check_struct_constructor(fcx: &FnCtxt, - id: ast::NodeId, - span: codemap::Span, - class_id: ast::DefId, - fields: &[ast::Field], - base_expr: Option<&ast::Expr>) { + fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + id: ast::NodeId, + span: codemap::Span, + class_id: ast::DefId, + fields: &'tcx [ast::Field], + base_expr: Option<&'tcx ast::Expr>) { let tcx = fcx.ccx.tcx; // Generate the struct type. @@ -3294,12 +3308,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, struct_type); } - fn check_struct_enum_variant(fcx: &FnCtxt, - id: ast::NodeId, - span: codemap::Span, - enum_id: ast::DefId, - variant_id: ast::DefId, - fields: &[ast::Field]) { + fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + id: ast::NodeId, + span: codemap::Span, + enum_id: ast::DefId, + variant_id: ast::DefId, + fields: &'tcx [ast::Field]) { let tcx = fcx.ccx.tcx; // Look up the number of type parameters and the raw type, and @@ -3324,10 +3338,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, enum_type); } - fn check_struct_fields_on_error(fcx: &FnCtxt, - id: ast::NodeId, - fields: &[ast::Field], - base_expr: &Option>) { + fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + id: ast::NodeId, + fields: &'tcx [ast::Field], + base_expr: &'tcx Option>) { // Make sure to still write the types // otherwise we might ICE fcx.write_error(id); @@ -4126,15 +4140,15 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> { } } -pub fn check_decl_initializer(fcx: &FnCtxt, - nid: ast::NodeId, - init: &ast::Expr) +pub fn check_decl_initializer<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + nid: ast::NodeId, + init: &'tcx ast::Expr) { let local_ty = fcx.local_ty(init.span, nid); check_expr_coercable_to_type(fcx, init, local_ty) } -pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) { +pub fn check_decl_local<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, local: &'tcx ast::Local) { let tcx = fcx.ccx.tcx; let t = fcx.local_ty(local.span, local.id); @@ -4159,7 +4173,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) { } } -pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) { +pub fn check_stmt<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, stmt: &'tcx ast::Stmt) { let node_id; let mut saw_bot = false; let mut saw_err = false; @@ -4204,7 +4218,7 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) { } } -pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block) { +pub fn check_block_no_value<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, blk: &'tcx ast::Block) { check_block_with_expected(fcx, blk, ExpectHasType(ty::mk_nil(fcx.tcx()))); let blkty = fcx.node_ty(blk.id); if ty::type_is_error(blkty) { @@ -4216,7 +4230,7 @@ pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block) { } fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - blk: &ast::Block, + blk: &'tcx ast::Block, expected: Expectation<'tcx>) { let prev = { let mut fcx_ps = fcx.ps.borrow_mut(); @@ -4299,17 +4313,17 @@ fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, /// length expression in a fixed-length vector, but someday it might be /// extended to type-level numeric literals. fn check_const_in_type<'a,'tcx>(ccx: &'a CrateCtxt<'a,'tcx>, - expr: &ast::Expr, + expr: &'tcx ast::Expr, expected_type: Ty<'tcx>) { let inh = static_inherited_fields(ccx); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(expected_type), expr.id); check_const_with_ty(&fcx, expr.span, expr, expected_type); } -fn check_const(ccx: &CrateCtxt, - sp: Span, - e: &ast::Expr, - id: ast::NodeId) { +fn check_const<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, + sp: Span, + e: &'tcx ast::Expr, + id: ast::NodeId) { let inh = static_inherited_fields(ccx); let rty = ty::node_id_to_type(ccx.tcx, id); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id); @@ -4319,7 +4333,7 @@ fn check_const(ccx: &CrateCtxt, fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, _: Span, - e: &ast::Expr, + e: &'tcx ast::Expr, declty: Ty<'tcx>) { // Gather locals in statics (because of block expressions). // This is technically unnecessary because locals in static items are forbidden, @@ -4420,10 +4434,10 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) { } } -pub fn check_enum_variants(ccx: &CrateCtxt, - sp: Span, - vs: &[P], - id: ast::NodeId) { +pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, + sp: Span, + vs: &'tcx [P], + id: ast::NodeId) { fn disr_in_range(ccx: &CrateCtxt, ty: attr::IntType, @@ -4453,7 +4467,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt, } fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, - vs: &[P], + vs: &'tcx [P], id: ast::NodeId, hint: attr::ReprAttr) -> Vec>> { diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 41b63830279a8..9d8eaf98569fa 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -280,6 +280,9 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>, pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { debug!("select_all_fcx_obligations_or_error"); + fcx.inh.deferred_resolutions.borrow_mut() + .retain(|r| !r.attempt_resolution(fcx)); + select_fcx_obligations_where_possible(fcx); fcx.default_type_parameters(); From bc41cc0ec9fbe395598fde5ef22b3e95582a5236 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 26 Jan 2015 06:22:06 -0500 Subject: [PATCH 02/10] Make `unboxed_closure_kind` return `Option` to allow for the possibility that its value is not yet known. --- src/librustc/middle/mem_categorization.rs | 12 ++++++++++-- src/librustc/middle/traits/select.rs | 17 +++++++++++------ src/librustc/middle/ty.rs | 21 ++++++++++++++++----- src/librustc_trans/trans/common.rs | 12 +++++++++--- src/librustc_typeck/check/mod.rs | 7 +++++-- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 0d86dbeadf4a7..1330ad86bfc92 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -594,8 +594,16 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { let ty = try!(self.node_ty(fn_node_id)); match ty.sty { ty::ty_closure(closure_id, _, _) => { - let kind = self.typer.closure_kind(closure_id); - self.cat_upvar(id, span, var_id, fn_node_id, kind) + match self.typer.closure_kind(closure_id) { + Some(kind) => { + self.cat_upvar(id, span, var_id, fn_node_id, kind) + } + None => { + self.tcx().sess.span_bug( + span, + &*format!("No closure kind for {:?}", closure_id)); + } + } } _ => { self.tcx().sess.span_bug( diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 5780b5b70f4a2..106c07baaa7a1 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1024,12 +1024,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { kind, obligation.repr(self.tcx())); - let closure_kind = self.closure_typer.closure_kind(closure_def_id); - - debug!("closure_kind = {:?}", closure_kind); - - if closure_kind == kind { - candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone())); + match self.closure_typer.closure_kind(closure_def_id) { + Some(closure_kind) => { + debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); + if closure_kind == kind { + candidates.vec.push(ClosureCandidate(closure_def_id, substs.clone())); + } + } + None => { + debug!("assemble_unboxed_candidates: closure_kind not yet known"); + candidates.ambiguous = true; + } } Ok(()) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 8e88e4338cd85..cd9684b840b77 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2288,14 +2288,22 @@ impl ClosureKind { pub trait ClosureTyper<'tcx> { fn param_env<'a>(&'a self) -> &'a ty::ParameterEnvironment<'a, 'tcx>; - fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind; - + /// Is this a `Fn`, `FnMut` or `FnOnce` closure? During typeck, + /// returns `None` if the kind of this closure has not yet been + /// inferred. + fn closure_kind(&self, + def_id: ast::DefId) + -> Option; + + /// Returns the argument/return types of this closure. fn closure_type(&self, def_id: ast::DefId, substs: &subst::Substs<'tcx>) -> ty::ClosureTy<'tcx>; - // Returns `None` if the upvar types cannot yet be definitively determined. + /// Returns the set of all upvars and their transformed + /// types. During typeck, maybe return `None` if the upvar types + /// have not yet been inferred. fn closure_upvars(&self, def_id: ast::DefId, substs: &Substs<'tcx>) @@ -6473,8 +6481,11 @@ impl<'a,'tcx> ClosureTyper<'tcx> for ty::ParameterEnvironment<'a,'tcx> { self } - fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind { - self.tcx.closure_kind(def_id) + fn closure_kind(&self, + def_id: ast::DefId) + -> Option + { + Some(self.tcx.closure_kind(def_id)) } fn closure_type(&self, diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index a0dbc9c40a68d..5782b3987cb48 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -693,7 +693,10 @@ impl<'blk, 'tcx> ty::ClosureTyper<'tcx> for BlockS<'blk, 'tcx> { &self.fcx.param_env } - fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind { + fn closure_kind(&self, + def_id: ast::DefId) + -> Option + { let typer = NormalizingClosureTyper::new(self.tcx()); typer.closure_kind(def_id) } @@ -1065,8 +1068,11 @@ impl<'a,'tcx> ty::ClosureTyper<'tcx> for NormalizingClosureTyper<'a,'tcx> { &self.param_env } - fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind { - self.param_env.tcx.closure_kind(def_id) + fn closure_kind(&self, + def_id: ast::DefId) + -> Option + { + self.param_env.closure_kind(def_id) } fn closure_type(&self, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 565e96483b9a2..285b4fdf2e874 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -348,8 +348,11 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> { &self.inh.param_env } - fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind { - self.inh.closures.borrow()[def_id].kind + fn closure_kind(&self, + def_id: ast::DefId) + -> Option + { + Some(self.inh.closures.borrow()[def_id].kind) } fn closure_type(&self, From f5281e2bb56fa9ef4debf04e6141d2ad3650ca61 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 27 Jan 2015 13:45:42 -0500 Subject: [PATCH 03/10] Do not make fake types for upvars if we haven't yet inferred whether they are borrowed or by value. --- src/librustc/middle/ty.rs | 42 +++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index cd9684b840b77..f1e73ddc5a1e4 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5643,32 +5643,26 @@ pub fn closure_upvars<'tcx>(typer: &mc::Typer<'tcx>, closure_expr_id: closure_id.node }; - let captured_freevar_ty = match typer.upvar_capture(upvar_id) { - Some(UpvarCapture::ByValue) => { - freevar_ty - } - - Some(UpvarCapture::ByRef(borrow)) => { - mk_rptr(tcx, - tcx.mk_region(borrow.region), - ty::mt { - ty: freevar_ty, - mutbl: borrow.kind.to_mutbl_lossy(), - }) - } + typer.upvar_capture(upvar_id).map(|capture| { + let freevar_ref_ty = match capture { + UpvarCapture::ByValue => { + freevar_ty + } + UpvarCapture::ByRef(borrow) => { + mk_rptr(tcx, + tcx.mk_region(borrow.region), + ty::mt { + ty: freevar_ty, + mutbl: borrow.kind.to_mutbl_lossy(), + }) + } + }; - None => { - // FIXME(#16640) we should really return None here; - // but that requires better inference integration, - // for now gin up something. - freevar_ty + ClosureUpvar { + def: freevar.def, + span: freevar.span, + ty: freevar_ref_ty, } - }; - - Some(ClosureUpvar { - def: freevar.def, - span: freevar.span, - ty: captured_freevar_ty, }) }) .collect() From 92f94765ec2202ccda84a7da97c4d415ffb9b070 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 31 Jan 2015 06:33:41 -0500 Subject: [PATCH 04/10] Adjust the handling of trait obligations and defaults to account for upvar inference. Upvar inference can cause some obligations to be deferred, notably things like `F : Sized` where `F` is a closure type, or `F : FnMut`. Adjust the ordering therefore so that we process all traits and apply fallback, do upvar inference, and only then start reporting errors for outstanding obligations. --- src/librustc_typeck/check/mod.rs | 3 ++- src/librustc_typeck/check/vtable.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 285b4fdf2e874..c58377fc87bd7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -493,8 +493,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig, decl, id, body, &inh); - vtable::select_all_fcx_obligations_or_error(&fcx); + vtable::select_all_fcx_obligations_and_apply_defaults(&fcx); upvar::closure_analyze_fn(&fcx, id, decl, body); + vtable::select_all_fcx_obligations_or_error(&fcx); regionck::regionck_fn(&fcx, id, decl, body); writeback::resolve_type_vars_in_fn(&fcx, decl, body); } diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 9d8eaf98569fa..9921a331fa40d 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -277,15 +277,20 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>, } } -pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { +pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) { debug!("select_all_fcx_obligations_or_error"); fcx.inh.deferred_resolutions.borrow_mut() .retain(|r| !r.attempt_resolution(fcx)); - select_fcx_obligations_where_possible(fcx); fcx.default_type_parameters(); + select_fcx_obligations_where_possible(fcx); +} + +pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { + debug!("select_all_fcx_obligations_or_error"); + select_all_fcx_obligations_and_apply_defaults(fcx); let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut(); let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx); match r { From 2f465869fd23d1bea7a99ca2e602c94e13d0dffb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 30 Jan 2015 11:37:23 -0500 Subject: [PATCH 05/10] Separate out the unboxed closure table into two tables, so that we can generate the closure type and closure kind separately. --- src/librustc/metadata/common.rs | 14 ++-- src/librustc/metadata/encoder.rs | 37 --------- src/librustc/middle/astencode.rs | 97 +++++++++-------------- src/librustc/middle/expr_use_visitor.rs | 6 +- src/librustc/middle/ty.rs | 24 +++--- src/librustc/util/ppaux.rs | 6 +- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/closure.rs | 2 +- src/librustc_typeck/check/closure.rs | 8 +- src/librustc_typeck/check/method/probe.rs | 8 +- src/librustc_typeck/check/mod.rs | 10 ++- src/librustc_typeck/check/writeback.rs | 15 ++-- 12 files changed, 81 insertions(+), 148 deletions(-) diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 242ab630a20ef..bf2f5bd22c463 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -139,10 +139,11 @@ pub enum astencode_tag { // Reserves 0x40 -- 0x5f tag_table_adjustments = 0x51, tag_table_moves_map = 0x52, tag_table_capture_map = 0x53, - tag_table_closures = 0x54, - tag_table_upvar_capture_map = 0x55, - tag_table_capture_modes = 0x56, - tag_table_object_cast_map = 0x57, + tag_table_closure_tys = 0x54, + tag_table_closure_kinds = 0x55, + tag_table_upvar_capture_map = 0x56, + tag_table_capture_modes = 0x57, + tag_table_object_cast_map = 0x58, } static first_astencode_tag: uint = tag_ast as uint; @@ -225,10 +226,7 @@ pub struct LinkMeta { pub crate_hash: Svh, } -pub const tag_closures: uint = 0x95; -pub const tag_closure: uint = 0x96; -pub const tag_closure_type: uint = 0x97; -pub const tag_closure_kind: uint = 0x98; +// GAP 0x94...0x98 pub const tag_struct_fields: uint = 0x99; pub const tag_struct_field: uint = 0x9a; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6767f77de84bb..e09d29b98b09a 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -618,17 +618,6 @@ fn encode_visibility(rbml_w: &mut Encoder, visibility: ast::Visibility) { rbml_w.end_tag(); } -fn encode_closure_kind(rbml_w: &mut Encoder, kind: ty::ClosureKind) { - rbml_w.start_tag(tag_closure_kind); - let ch = match kind { - ty::FnClosureKind => 'f', - ty::FnMutClosureKind => 'm', - ty::FnOnceClosureKind => 'o', - }; - rbml_w.wr_str(&ch.to_string()[]); - rbml_w.end_tag(); -} - fn encode_explicit_self(rbml_w: &mut Encoder, explicit_self: &ty::ExplicitSelfCategory) { rbml_w.start_tag(tag_item_trait_method_explicit_self); @@ -1843,24 +1832,6 @@ fn encode_macro_defs(rbml_w: &mut Encoder, rbml_w.end_tag(); } -fn encode_closures<'a>(ecx: &'a EncodeContext, rbml_w: &'a mut Encoder) { - rbml_w.start_tag(tag_closures); - for (closure_id, closure) in ecx.tcx.closures.borrow().iter() { - if closure_id.krate != ast::LOCAL_CRATE { - continue - } - - rbml_w.start_tag(tag_closure); - encode_def_id(rbml_w, *closure_id); - rbml_w.start_tag(tag_closure_type); - write_closure_type(ecx, rbml_w, &closure.closure_type); - rbml_w.end_tag(); - encode_closure_kind(rbml_w, closure.kind); - rbml_w.end_tag(); - } - rbml_w.end_tag(); -} - fn encode_struct_field_attrs(rbml_w: &mut Encoder, krate: &ast::Crate) { struct StructFieldVisitor<'a, 'b:'a> { rbml_w: &'a mut Encoder<'b>, @@ -2069,7 +2040,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, native_lib_bytes: u64, plugin_registrar_fn_bytes: u64, macro_defs_bytes: u64, - closure_bytes: u64, impl_bytes: u64, misc_bytes: u64, item_bytes: u64, @@ -2084,7 +2054,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, native_lib_bytes: 0, plugin_registrar_fn_bytes: 0, macro_defs_bytes: 0, - closure_bytes: 0, impl_bytes: 0, misc_bytes: 0, item_bytes: 0, @@ -2154,11 +2123,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, encode_macro_defs(&mut rbml_w, krate); stats.macro_defs_bytes = rbml_w.writer.tell().unwrap() - i; - // Encode the types of all closures in this crate. - i = rbml_w.writer.tell().unwrap(); - encode_closures(&ecx, &mut rbml_w); - stats.closure_bytes = rbml_w.writer.tell().unwrap() - i; - // Encode the def IDs of impls, for coherence checking. i = rbml_w.writer.tell().unwrap(); encode_impls(&ecx, krate, &mut rbml_w); @@ -2199,7 +2163,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, println!(" native bytes: {}", stats.native_lib_bytes); println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes); println!(" macro def bytes: {}", stats.macro_defs_bytes); - println!(" closure bytes: {}", stats.closure_bytes); println!(" impl bytes: {}", stats.impl_bytes); println!(" misc bytes: {}", stats.misc_bytes); println!(" item bytes: {}", stats.item_bytes); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 902c029fef4bf..3764324734132 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -647,30 +647,7 @@ impl<'tcx> tr for MethodOrigin<'tcx> { } pub fn encode_closure_kind(ebml_w: &mut Encoder, kind: ty::ClosureKind) { - use serialize::Encoder; - - ebml_w.emit_enum("ClosureKind", |ebml_w| { - match kind { - ty::FnClosureKind => { - ebml_w.emit_enum_variant("FnClosureKind", 0, 3, |_| { - Ok(()) - }) - } - ty::FnMutClosureKind => { - ebml_w.emit_enum_variant("FnMutClosureKind", 1, 3, |_| { - Ok(()) - }) - } - ty::FnOnceClosureKind => { - ebml_w.emit_enum_variant("FnOnceClosureKind", - 2, - 3, - |_| { - Ok(()) - }) - } - } - }).unwrap() + kind.encode(ebml_w).unwrap(); } pub trait vtable_decoder_helpers<'tcx> { @@ -1310,12 +1287,20 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, }) } - for closure in tcx.closures.borrow().get(&ast_util::local_def(id)).iter() { - rbml_w.tag(c::tag_table_closures, |rbml_w| { + for &closure_type in tcx.closure_tys.borrow().get(&ast_util::local_def(id)).iter() { + rbml_w.tag(c::tag_table_closure_tys, |rbml_w| { + rbml_w.id(id); + rbml_w.tag(c::tag_table_val, |rbml_w| { + rbml_w.emit_closure_type(ecx, closure_type); + }) + }) + } + + for &&closure_kind in tcx.closure_kinds.borrow().get(&ast_util::local_def(id)).iter() { + rbml_w.tag(c::tag_table_closure_kinds, |rbml_w| { rbml_w.id(id); rbml_w.tag(c::tag_table_val, |rbml_w| { - rbml_w.emit_closure_type(ecx, &closure.closure_type); - encode_closure_kind(rbml_w, closure.kind) + encode_closure_kind(rbml_w, closure_kind) }) }) } @@ -1354,8 +1339,10 @@ trait rbml_decoder_decoder_helpers<'tcx> { -> subst::Substs<'tcx>; fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> ty::AutoAdjustment<'tcx>; - fn read_closure<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) - -> ty::Closure<'tcx>; + fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) + -> ty::ClosureKind; + fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) + -> ty::ClosureTy<'tcx>; fn read_auto_deref_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> ty::AutoDerefRef<'tcx>; fn read_autoref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) @@ -1782,35 +1769,23 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { }).unwrap() } - fn read_closure<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) - -> ty::Closure<'tcx> { - let closure_type = self.read_opaque(|this, doc| { + fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>) + -> ty::ClosureKind + { + Decodable::decode(self).ok().unwrap() + } + + fn read_closure_ty<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) + -> ty::ClosureTy<'tcx> + { + self.read_opaque(|this, doc| { Ok(tydecode::parse_ty_closure_data( doc.data, dcx.cdata.cnum, doc.start, dcx.tcx, |s, a| this.convert_def_id(dcx, s, a))) - }).unwrap(); - let variants = &[ - "FnClosureKind", - "FnMutClosureKind", - "FnOnceClosureKind" - ]; - let kind = self.read_enum("ClosureKind", |this| { - this.read_enum_variant(variants, |_, i| { - Ok(match i { - 0 => ty::FnClosureKind, - 1 => ty::FnMutClosureKind, - 2 => ty::FnOnceClosureKind, - _ => panic!("bad enum variant for ty::ClosureKind"), - }) - }) - }).unwrap(); - ty::Closure { - closure_type: closure_type, - kind: kind, - } + }).unwrap() } /// Converts a def-id that appears in a type. The correct @@ -1937,11 +1912,17 @@ fn decode_side_tables(dcx: &DecodeContext, let adj: ty::AutoAdjustment = val_dsr.read_auto_adjustment(dcx); dcx.tcx.adjustments.borrow_mut().insert(id, adj); } - c::tag_table_closures => { - let closure = - val_dsr.read_closure(dcx); - dcx.tcx.closures.borrow_mut().insert(ast_util::local_def(id), - closure); + c::tag_table_closure_tys => { + let closure_ty = + val_dsr.read_closure_ty(dcx); + dcx.tcx.closure_tys.borrow_mut().insert(ast_util::local_def(id), + closure_ty); + } + c::tag_table_closure_kinds => { + let closure_kind = + val_dsr.read_closure_kind(dcx); + dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id), + closure_kind); } _ => { dcx.tcx.sess.bug( diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 188a56135ec33..4a0bed57433ae 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -260,12 +260,10 @@ impl OverloadedCallType { fn from_closure(tcx: &ty::ctxt, closure_did: ast::DefId) -> OverloadedCallType { let trait_did = - tcx.closures + tcx.closure_kinds .borrow() .get(&closure_did) - .expect("OverloadedCallType::from_closure: didn't \ - find closure id") - .kind + .expect("OverloadedCallType::from_closure: didn't find closure id") .trait_did(tcx); OverloadedCallType::from_trait_id(tcx, trait_did) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index f1e73ddc5a1e4..fb1d9fcada1ce 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -790,7 +790,11 @@ pub struct ctxt<'tcx> { /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. - pub closures: RefCell>>, + pub closure_kinds: RefCell>, + + /// Records the type of each closure. The def ID is the ID of the + /// expression defining the closure. + pub closure_tys: RefCell>>, pub node_lint_levels: RefCell>, @@ -2251,16 +2255,7 @@ pub struct ItemSubsts<'tcx> { pub substs: Substs<'tcx>, } -/// Records information about each closure. -#[derive(Clone)] -pub struct Closure<'tcx> { - /// The type of the closure. - pub closure_type: ClosureTy<'tcx>, - /// The kind of closure this is. - pub kind: ClosureKind, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)] pub enum ClosureKind { FnClosureKind, FnMutClosureKind, @@ -2399,7 +2394,8 @@ pub fn mk_ctxt<'tcx>(s: Session, extern_const_variants: RefCell::new(DefIdMap()), method_map: RefCell::new(FnvHashMap()), dependency_formats: RefCell::new(FnvHashMap()), - closures: RefCell::new(DefIdMap()), + closure_kinds: RefCell::new(DefIdMap()), + closure_tys: RefCell::new(DefIdMap()), node_lint_levels: RefCell::new(FnvHashMap()), transmute_restrictions: RefCell::new(Vec::new()), stability: RefCell::new(stability), @@ -2446,7 +2442,7 @@ impl<'tcx> ctxt<'tcx> { } pub fn closure_kind(&self, def_id: ast::DefId) -> ty::ClosureKind { - self.closures.borrow()[def_id].kind + self.closure_kinds.borrow()[def_id] } pub fn closure_type(&self, @@ -2454,7 +2450,7 @@ impl<'tcx> ctxt<'tcx> { substs: &subst::Substs<'tcx>) -> ty::ClosureTy<'tcx> { - self.closures.borrow()[def_id].closure_type.subst(self, substs) + self.closure_tys.borrow()[def_id].subst(self, substs) } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6c723a583807e..15cf37dc2f2f0 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -405,9 +405,9 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String { } ty_str => "str".to_string(), ty_closure(ref did, _, substs) => { - let closures = cx.closures.borrow(); - closures.get(did).map(|cl| { - closure_to_string(cx, &cl.closure_type.subst(cx, substs)) + let closure_tys = cx.closure_tys.borrow(); + closure_tys.get(did).map(|closure_type| { + closure_to_string(cx, &closure_type.subst(cx, substs)) }).unwrap_or_else(|| { if did.krate == ast::LOCAL_CRATE { let span = cx.map.span(did.node); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1b212aca33034..9ec3db0f602af 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -273,7 +273,7 @@ pub fn self_type_for_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } pub fn kind_for_closure(ccx: &CrateContext, closure_id: ast::DefId) -> ty::ClosureKind { - ccx.tcx().closures.borrow()[closure_id].kind + ccx.tcx().closure_kinds.borrow()[closure_id] } pub fn decl_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index a15ede095a709..8473ce1b797e3 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -125,7 +125,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc closure_id: ast::DefId, substs: &Substs<'tcx>) -> Option> { - if !ccx.tcx().closures.borrow().contains_key(&closure_id) { + if !ccx.tcx().closure_kinds.borrow().contains_key(&closure_id) { // Not a closure. return None } diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 906a8a33314cc..22390b9e98b11 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -129,12 +129,8 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fn_ty.sig.repr(fcx.tcx()), kind); - let closure = ty::Closure { - closure_type: fn_ty, - kind: kind, - }; - - fcx.inh.closures.borrow_mut().insert(expr_def_id, closure); + fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty); + fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); } fn deduce_expectations_from_expected_type<'a,'tcx>( diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 2e366f4450744..8000776ad45f9 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -598,9 +598,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { _ => continue, }; - let closures = self.fcx.inh.closures.borrow(); - let closure_data = match closures.get(&closure_def_id) { - Some(data) => data, + let closure_kinds = self.fcx.inh.closure_kinds.borrow(); + let closure_kind = match closure_kinds.get(&closure_def_id) { + Some(&k) => k, None => { self.tcx().sess.span_bug( self.span, @@ -610,7 +610,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { }; // this closure doesn't implement the right kind of `Fn` trait - if closure_data.kind != kind { + if closure_kind != kind { continue; } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c58377fc87bd7..ed195035a41df 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -160,7 +160,8 @@ pub struct Inherited<'a, 'tcx: 'a> { adjustments: RefCell>>, method_map: MethodMap<'tcx>, upvar_capture_map: RefCell, - closures: RefCell>>, + closure_tys: RefCell>>, + closure_kinds: RefCell>, object_cast_map: ObjectCastMap<'tcx>, // A mapping from each fn's id to its signature, with all bound @@ -352,7 +353,7 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> { def_id: ast::DefId) -> Option { - Some(self.inh.closures.borrow()[def_id].kind) + self.inh.closure_kinds.borrow().get(&def_id).cloned() } fn closure_type(&self, @@ -360,7 +361,7 @@ impl<'a, 'tcx> ty::ClosureTyper<'tcx> for FnCtxt<'a, 'tcx> { substs: &subst::Substs<'tcx>) -> ty::ClosureTy<'tcx> { - self.inh.closures.borrow()[def_id].closure_type.subst(self.tcx(), substs) + self.inh.closure_tys.borrow()[def_id].subst(self.tcx(), substs) } fn closure_upvars(&self, @@ -386,7 +387,8 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { method_map: RefCell::new(FnvHashMap()), object_cast_map: RefCell::new(NodeMap()), upvar_capture_map: RefCell::new(FnvHashMap()), - closures: RefCell::new(DefIdMap()), + closure_tys: RefCell::new(DefIdMap()), + closure_kinds: RefCell::new(DefIdMap()), fn_sig_map: RefCell::new(NodeMap()), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), deferred_resolutions: RefCell::new(Vec::new()), diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index f7e1afed8fc53..0eaecf8ac0574 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -204,14 +204,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { return } - for (def_id, closure) in self.fcx.inh.closures.borrow().iter() { - let closure_ty = self.resolve(&closure.closure_type, - ResolvingClosure(*def_id)); - let closure = ty::Closure { - closure_type: closure_ty, - kind: closure.kind, - }; - self.fcx.tcx().closures.borrow_mut().insert(*def_id, closure); + for (def_id, closure_ty) in self.fcx.inh.closure_tys.borrow().iter() { + let closure_ty = self.resolve(closure_ty, ResolvingClosure(*def_id)); + self.fcx.tcx().closure_tys.borrow_mut().insert(*def_id, closure_ty); + } + + for (def_id, &closure_kind) in self.fcx.inh.closure_kinds.borrow().iter() { + self.fcx.tcx().closure_kinds.borrow_mut().insert(*def_id, closure_kind); } } From e1f54f01d666279f50153536eb5a8494f26d89a7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 30 Jan 2015 20:23:17 -0500 Subject: [PATCH 06/10] Implement basic inference for closure kinds and some simple tests. Trickier cases not yet handled. --- src/librustc_typeck/check/callee.rs | 14 +++- src/librustc_typeck/check/closure.rs | 28 +++---- src/librustc_typeck/check/upvar.rs | 82 ++++++++++++++++--- src/librustc_typeck/check/vtable.rs | 2 +- ...nboxed-closures-infer-fnmut-missing-mut.rs | 18 ++++ ...nboxed-closures-infer-fnonce-call-twice.rs | 21 +++++ .../run-pass/unboxed-closures-infer-fnmut.rs | 24 ++++++ .../run-pass/unboxed-closures-infer-fnonce.rs | 37 +++++++++ 8 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs create mode 100644 src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-fnmut.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-fnonce.rs diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index dc8bde9f84d6d..d7a28f1843c09 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -150,6 +150,7 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, adjusted_ty: adjusted_ty, autoderefref: autoderefref, fn_sig: fn_sig.clone(), + closure_def_id: def_id, }); return Some(CallStep::Closure(fn_sig)); } @@ -321,17 +322,19 @@ struct CallResolution<'tcx> { adjusted_ty: Ty<'tcx>, autoderefref: ty::AutoDerefRef<'tcx>, fn_sig: ty::FnSig<'tcx>, + closure_def_id: ast::DefId, } impl<'tcx> Repr<'tcx> for CallResolution<'tcx> { fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { format!("CallResolution(call_expr={}, callee_expr={}, adjusted_ty={}, \ - autoderefref={}, fn_sig={})", + autoderefref={}, fn_sig={}, closure_def_id={})", self.call_expr.repr(tcx), self.callee_expr.repr(tcx), self.adjusted_ty.repr(tcx), self.autoderefref.repr(tcx), - self.fn_sig.repr(tcx)) + self.fn_sig.repr(tcx), + self.closure_def_id.repr(tcx)) } } @@ -340,6 +343,13 @@ impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> { debug!("attempt_resolution() {}", self.repr(fcx.tcx())); + match fcx.closure_kind(self.closure_def_id) { + Some(_) => { } + None => { + return false; + } + } + // We may now know enough to figure out fn vs fnmut etc. match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr, self.adjusted_ty, self.autoderefref.clone()) { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 22390b9e98b11..2bbf832fdef01 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -45,17 +45,10 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, // that, otherwise we'll error, requesting an annotation. match expected_sig_and_kind { None => { // don't have information about the kind, request explicit annotation - // NB We still need to typeck the body, so assume `FnMut` kind just for that - let kind = ty::FnMutClosureKind; - - check_closure(fcx, expr, kind, decl, body, None); - - span_err!(fcx.ccx.tcx.sess, expr.span, E0187, - "can't infer the \"kind\" of the closure; explicitly annotate it; e.g. \ - `|&:| {{}}`"); + check_closure(fcx, expr, None, decl, body, None); }, Some((sig, kind)) => { - check_closure(fcx, expr, kind, decl, body, Some(sig)); + check_closure(fcx, expr, Some(kind), decl, body, Some(sig)); } } } @@ -68,21 +61,21 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, }; let expected_sig = expected_sig_and_kind.map(|t| t.0); - check_closure(fcx, expr, kind, decl, body, expected_sig); + check_closure(fcx, expr, Some(kind), decl, body, expected_sig); } } } fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, - kind: ty::ClosureKind, + opt_kind: Option, decl: &'tcx ast::FnDecl, body: &'tcx ast::Block, expected_sig: Option>) { let expr_def_id = ast_util::local_def(expr.id); - debug!("check_closure kind={:?} expected_sig={}", - kind, + debug!("check_closure opt_kind={:?} expected_sig={}", + opt_kind, expected_sig.repr(fcx.tcx())); let mut fn_ty = astconv::ty_of_closure( @@ -124,13 +117,16 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, // the `closures` table. fn_ty.sig.0.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.0.inputs)]; - debug!("closure for {} --> sig={} kind={:?}", + debug!("closure for {} --> sig={} opt_kind={:?}", expr_def_id.repr(fcx.tcx()), fn_ty.sig.repr(fcx.tcx()), - kind); + opt_kind); fcx.inh.closure_tys.borrow_mut().insert(expr_def_id, fn_ty); - fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); + match opt_kind { + Some(kind) => { fcx.inh.closure_kinds.borrow_mut().insert(expr_def_id, kind); } + None => { } + } } fn deduce_expectations_from_expected_type<'a,'tcx>( diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 449220b1c8528..2b043076e4614 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -46,7 +46,9 @@ use middle::expr_use_visitor as euv; use middle::mem_categorization as mc; use middle::ty::{self}; use middle::infer::{InferCtxt, UpvarRegion}; +use std::collections::HashSet; use syntax::ast; +use syntax::ast_util; use syntax::codemap::Span; use syntax::visit::{self, Visitor}; use util::ppaux::Repr; @@ -56,13 +58,15 @@ use util::ppaux::Repr; pub fn closure_analyze_fn(fcx: &FnCtxt, _id: ast::NodeId, - decl: &ast::FnDecl, - body: &ast::Block) { + _decl: &ast::FnDecl, + body: &ast::Block) +{ let mut seed = SeedBorrowKind::new(fcx); seed.visit_block(body); + let closures_with_inferred_kinds = seed.closures_with_inferred_kinds; - let mut adjust = AdjustBorrowKind::new(fcx); - adjust.analyze_fn(decl, body); + let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds); + adjust.visit_block(body); } /////////////////////////////////////////////////////////////////////////// @@ -70,6 +74,7 @@ pub fn closure_analyze_fn(fcx: &FnCtxt, struct SeedBorrowKind<'a,'tcx:'a> { fcx: &'a FnCtxt<'a,'tcx>, + closures_with_inferred_kinds: HashSet, } impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { @@ -105,7 +110,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { fn new(fcx: &'a FnCtxt<'a,'tcx>) -> SeedBorrowKind<'a,'tcx> { - SeedBorrowKind { fcx: fcx } + SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() } } fn tcx(&self) -> &'a ty::ctxt<'tcx> { @@ -121,6 +126,14 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { capture_clause: ast::CaptureClause, _body: &ast::Block) { + let closure_def_id = ast_util::local_def(expr.id); + if !self.fcx.inh.closure_kinds.borrow().contains_key(&closure_def_id) { + self.closures_with_inferred_kinds.insert(expr.id); + self.fcx.inh.closure_kinds.borrow_mut().insert(closure_def_id, ty::FnClosureKind); + debug!("check_closure: adding closure_id={} to closures_with_inferred_kinds", + closure_def_id.repr(self.tcx())); + } + ty::with_freevars(self.tcx(), expr.id, |freevars| { for freevar in freevars.iter() { let var_node_id = freevar.def.local_node_id(); @@ -151,19 +164,22 @@ impl<'a,'tcx> SeedBorrowKind<'a,'tcx> { // ADJUST BORROW KIND struct AdjustBorrowKind<'a,'tcx:'a> { - fcx: &'a FnCtxt<'a,'tcx> + fcx: &'a FnCtxt<'a,'tcx>, + closures_with_inferred_kinds: &'a HashSet, } impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { - fn new(fcx: &'a FnCtxt<'a,'tcx>) -> AdjustBorrowKind<'a,'tcx> { - AdjustBorrowKind { fcx: fcx } + fn new(fcx: &'a FnCtxt<'a,'tcx>, + closures_with_inferred_kinds: &'a HashSet) + -> AdjustBorrowKind<'a,'tcx> { + AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds } } fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.fcx.tcx() } - fn analyze_fn(&mut self, decl: &ast::FnDecl, body: &ast::Block) { + fn analyze_closure(&mut self, id: ast::NodeId, decl: &ast::FnDecl, body: &ast::Block) { /*! * Analysis starting point. */ @@ -203,6 +219,9 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { setting upvar_id={:?} to by value", upvar_id); + // to move out of an upvar, this must be a FnOnce closure + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind); + let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue); } @@ -306,6 +325,13 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", upvar_id, upvar_capture, kind); + match kind { + ty::ImmBorrow => { } + ty::UniqueImmBorrow | ty::MutBorrow => { + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind); + } + } + match *upvar_capture { ty::UpvarCapture::ByValue => { // Upvar is already by-value, the strongest criteria. @@ -328,6 +354,40 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { } } } + + fn adjust_closure_kind(&self, + closure_id: ast::NodeId, + new_kind: ty::ClosureKind) { + debug!("adjust_closure_kind(closure_id={}, new_kind={:?})", + closure_id, new_kind); + + if !self.closures_with_inferred_kinds.contains(&closure_id) { + return; + } + + let closure_def_id = ast_util::local_def(closure_id); + let mut closure_kinds = self.fcx.inh.closure_kinds.borrow_mut(); + let existing_kind = closure_kinds[closure_def_id]; + + debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}", + closure_id, existing_kind, new_kind); + + match (existing_kind, new_kind) { + (ty::FnClosureKind, ty::FnClosureKind) | + (ty::FnMutClosureKind, ty::FnClosureKind) | + (ty::FnMutClosureKind, ty::FnMutClosureKind) | + (ty::FnOnceClosureKind, _) => { + // no change needed + } + + (ty::FnClosureKind, ty::FnMutClosureKind) | + (ty::FnClosureKind, ty::FnOnceClosureKind) | + (ty::FnMutClosureKind, ty::FnOnceClosureKind) => { + // new kind is stronger than the old kind + closure_kinds.insert(closure_def_id, new_kind); + } + } + } } impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> { @@ -336,14 +396,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for AdjustBorrowKind<'a, 'tcx> { decl: &'v ast::FnDecl, body: &'v ast::Block, span: Span, - _id: ast::NodeId) + id: ast::NodeId) { match fn_kind { visit::FkItemFn(..) | visit::FkMethod(..) => { // ignore nested fn items } visit::FkFnBlock => { - self.analyze_fn(decl, body); + self.analyze_closure(id, decl, body); visit::walk_fn(self, fn_kind, decl, body, span); } } diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 9921a331fa40d..60e673bcc7ba6 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -278,7 +278,7 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>, } pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) { - debug!("select_all_fcx_obligations_or_error"); + debug!("select_all_fcx_obligations_and_apply_defaults"); fcx.inh.deferred_resolutions.borrow_mut() .retain(|r| !r.attempt_resolution(fcx)); diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs new file mode 100644 index 0000000000000..9e4ed30799686 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-missing-mut.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + let tick = || counter += 1; + tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable +} diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs new file mode 100644 index 0000000000000..0050fbdde2639 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-call-twice.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +use std::mem; + +fn main() { + let mut counter: Vec = Vec::new(); + let tick = || mem::drop(counter); + tick(); + tick(); //~ ERROR use of moved value: `tick` +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut.rs new file mode 100644 index 0000000000000..67f36b9a9203c --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + + { + let mut tick = || counter += 1; + tick(); + tick(); + } + + assert_eq!(counter, 2); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce.rs b/src/test/run-pass/unboxed-closures-infer-fnonce.rs new file mode 100644 index 0000000000000..69beae7718462 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnonce.rs @@ -0,0 +1,37 @@ +// Copyright 2015 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. + +#![feature(unsafe_destructor)] + +// Test that we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +use std::mem; + +struct DropMe<'a>(&'a mut i32); + +#[unsafe_destructor] +impl<'a> Drop for DropMe<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn main() { + let mut counter = 0; + + { + let drop_me = DropMe(&mut counter); + let tick = || mem::drop(drop_me); + tick(); + } + + assert_eq!(counter, 1); +} From 5a39a0d266705d594ff31b8e386bc8c1e54ab019 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 31 Jan 2015 06:29:04 -0500 Subject: [PATCH 07/10] To handle more complex cases, modify the deferred call handler to be specialized to closures, and invoke them as soon as we know the closure kind. I thought initially we would need a fixed-point inference algorithm but it appears I was mistaken, so we can do this. --- src/librustc_typeck/check/callee.rs | 78 ++++++++++--------- src/librustc_typeck/check/mod.rs | 38 +++++++-- src/librustc_typeck/check/upvar.rs | 51 +++++++++++- src/librustc_typeck/check/vtable.rs | 6 +- .../unboxed-closures-failed-recursive-fn-1.rs | 33 ++++++++ .../unboxed-closures-failed-recursive-fn-2.rs | 39 ++++++++++ ...osures-infer-fnmut-calling-fnmut-no-mut.rs | 32 ++++++++ ...oxed-closures-recursive-fn-using-fn-mut.rs | 48 ++++++++++++ ...oxed-closures-infer-fnmut-calling-fnmut.rs | 29 +++++++ .../unboxed-closures-infer-recursive-fn.rs | 47 +++++++++++ 10 files changed, 354 insertions(+), 47 deletions(-) create mode 100644 src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs create mode 100644 src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs create mode 100644 src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs create mode 100644 src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-recursive-fn.rs diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index d7a28f1843c09..93c6445606eb8 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -14,7 +14,7 @@ use super::check_argument_types; use super::check_expr; use super::check_method_argument_types; use super::demand; -use super::DeferredResolution; +use super::DeferredCallResolution; use super::err_args; use super::Expectation; use super::expected_types_for_fn_args; @@ -99,8 +99,8 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected); } - Some(CallStep::Closure(fn_sig)) => { - confirm_closure_call(fcx, call_expr, arg_exprs, expected, fn_sig); + Some(CallStep::DeferredClosure(fn_sig)) => { + confirm_deferred_closure_call(fcx, call_expr, arg_exprs, expected, fn_sig); } Some(CallStep::Overloaded(method_callee)) => { @@ -112,7 +112,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, enum CallStep<'tcx> { Builtin, - Closure(ty::FnSig<'tcx>), + DeferredClosure(ty::FnSig<'tcx>), Overloaded(ty::MethodCallee<'tcx>) } @@ -138,21 +138,28 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } ty::ty_closure(def_id, _, substs) => { - let closure_ty = - fcx.closure_type(def_id, substs); - let fn_sig = - fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span, - infer::FnCall, - &closure_ty.sig).0; - fcx.record_deferred_resolution(box CallResolution { - call_expr: call_expr, - callee_expr: callee_expr, - adjusted_ty: adjusted_ty, - autoderefref: autoderefref, - fn_sig: fn_sig.clone(), - closure_def_id: def_id, - }); - return Some(CallStep::Closure(fn_sig)); + assert_eq!(def_id.krate, ast::LOCAL_CRATE); + + // Check whether this is a call to a closure where we + // haven't yet decided on whether the closure is fn vs + // fnmut vs fnonce. If so, we have to defer further processing. + if fcx.closure_kind(def_id).is_none() { + let closure_ty = + fcx.closure_type(def_id, substs); + let fn_sig = + fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span, + infer::FnCall, + &closure_ty.sig).0; + fcx.record_deferred_call_resolution( + def_id, + box CallResolution {call_expr: call_expr, + callee_expr: callee_expr, + adjusted_ty: adjusted_ty, + autoderefref: autoderefref, + fn_sig: fn_sig.clone(), + closure_def_id: def_id}); + return Some(CallStep::DeferredClosure(fn_sig)); + } } _ => {} @@ -258,11 +265,11 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, write_call(fcx, call_expr, fn_sig.output); } -fn confirm_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - call_expr: &ast::Expr, - arg_exprs: &'tcx [P], - expected: Expectation<'tcx>, - fn_sig: ty::FnSig<'tcx>) +fn confirm_deferred_closure_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + call_expr: &ast::Expr, + arg_exprs: &'tcx [P], + expected: Expectation<'tcx>, + fn_sig: ty::FnSig<'tcx>) { // `fn_sig` is the *signature* of the cosure being called. We // don't know the full details yet (`Fn` vs `FnMut` etc), but we @@ -338,22 +345,18 @@ impl<'tcx> Repr<'tcx> for CallResolution<'tcx> { } } -impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> { - fn attempt_resolution<'a>(&self, fcx: &FnCtxt<'a,'tcx>) -> bool { - debug!("attempt_resolution() {}", +impl<'tcx> DeferredCallResolution<'tcx> for CallResolution<'tcx> { + fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>) { + debug!("DeferredCallResolution::resolve() {}", self.repr(fcx.tcx())); - match fcx.closure_kind(self.closure_def_id) { - Some(_) => { } - None => { - return false; - } - } + // we should not be invoked until the closure kind has been + // determined by upvar inference + assert!(fcx.closure_kind(self.closure_def_id).is_some()); // We may now know enough to figure out fn vs fnmut etc. match try_overloaded_call_traits(fcx, self.call_expr, self.callee_expr, self.adjusted_ty, self.autoderefref.clone()) { - None => false, Some(method_callee) => { // One problem is that when we get here, we are going // to have a newly instantiated function signature @@ -382,8 +385,11 @@ impl<'tcx> DeferredResolution<'tcx> for CallResolution<'tcx> { self.fn_sig.output.unwrap()); write_overloaded_call_method_map(fcx, self.call_expr, method_callee); - - true + } + None => { + fcx.tcx().sess.span_bug( + self.call_expr.span, + "failed to find an overloaded call trait for closure call"); } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ed195035a41df..0aad1f99ce8f2 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -110,6 +110,7 @@ use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; use util::lev_distance::lev_distance; use std::cell::{Cell, Ref, RefCell}; +use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::mem::replace; use std::rc::Rc; use std::iter::repeat; @@ -172,15 +173,21 @@ pub struct Inherited<'a, 'tcx: 'a> { // Tracks trait obligations incurred during this function body. fulfillment_cx: RefCell>, - // - deferred_resolutions: RefCell>>, + // When we process a call like `c()` where `c` is a closure type, + // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + // `FnOnce` closure. In that case, we defer full resolution of the + // call until upvar inference can kick in and make the + // decision. We keep these deferred resolutions sorted by the + // def-id of the closure, so that once we decide, we can easily go + // back and process them. + deferred_call_resolutions: RefCell>>>, } -trait DeferredResolution<'tcx> { - fn attempt_resolution<'a>(&self, fcx: &FnCtxt<'a,'tcx>) -> bool; +trait DeferredCallResolution<'tcx> { + fn resolve<'a>(&mut self, fcx: &FnCtxt<'a,'tcx>); } -type DeferredResolutionHandler<'tcx> = Box+'tcx>; +type DeferredCallResolutionHandler<'tcx> = Box+'tcx>; /// When type-checking an expression, we propagate downward /// whatever type hint we are able in the form of an `Expectation`. @@ -391,7 +398,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { closure_kinds: RefCell::new(DefIdMap()), fn_sig_map: RefCell::new(NodeMap()), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), - deferred_resolutions: RefCell::new(Vec::new()), + deferred_call_resolutions: RefCell::new(DefIdMap()), } } @@ -1295,8 +1302,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) } } - fn record_deferred_resolution(&self, r: DeferredResolutionHandler<'tcx>) { - self.inh.deferred_resolutions.borrow_mut().push(r); + fn record_deferred_call_resolution(&self, + closure_def_id: ast::DefId, + r: DeferredCallResolutionHandler<'tcx>) { + let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut(); + let mut vec = match deferred_call_resolutions.entry(closure_def_id) { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Vec::new()), + }; + vec.push(r); + } + + fn remove_deferred_call_resolutions(&self, + closure_def_id: ast::DefId) + -> Vec> + { + let mut deferred_call_resolutions = self.inh.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.remove(&closure_def_id).unwrap_or(Vec::new()) } pub fn tag(&self) -> String { diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 2b043076e4614..961a2e39dc08f 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -67,6 +67,9 @@ pub fn closure_analyze_fn(fcx: &FnCtxt, let mut adjust = AdjustBorrowKind::new(fcx, &closures_with_inferred_kinds); adjust.visit_block(body); + + // it's our job to process these. + assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty()); } /////////////////////////////////////////////////////////////////////////// @@ -186,10 +189,56 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { self.visit_block(body); - debug!("analyzing fn body with id {}", body.id); + debug!("analyzing closure `{}` with fn body id `{}`", id, body.id); let mut euv = euv::ExprUseVisitor::new(self, self.fcx); euv.walk_fn(decl, body); + + // If we had not yet settled on a closure kind for this closure, + // then we should have by now. Process and remove any deferred resolutions. + // + // Interesting fact: all calls to this closure must come + // *after* its definition. Initially, I thought that some + // kind of fixed-point iteration would be required, due to the + // possibility of twisted examples like this one: + // + // ```rust + // let mut closure0 = None; + // let vec = vec!(1, 2, 3); + // + // loop { + // { + // let closure1 = || { + // match closure0.take() { + // Some(c) => { + // return c(); // (*) call to `closure0` before it is defined + // } + // None => { } + // } + // }; + // closure1(); + // } + // + // closure0 = || vec; + // } + // ``` + // + // However, this turns out to be wrong. Examples like this + // fail to compile because the type of the variable `c` above + // is an inference variable. And in fact since closure types + // cannot be written, there is no way to make this example + // work without a boxed closure. This implies that we can't + // have two closures that recursively call one another without + // some form of boxing (and hence explicit writing of a + // closure kind) involved. Huzzah. -nmatsakis + let closure_def_id = ast_util::local_def(id); + if self.closures_with_inferred_kinds.contains(&id) { + let mut deferred_call_resolutions = + self.fcx.remove_deferred_call_resolutions(closure_def_id); + for deferred_call_resolution in deferred_call_resolutions.iter_mut() { + deferred_call_resolution.resolve(self.fcx); + } + } } fn adjust_upvar_borrow_kind_for_consume(&self, diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index 60e673bcc7ba6..5cf71a9be6a02 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -280,8 +280,6 @@ fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>, pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) { debug!("select_all_fcx_obligations_and_apply_defaults"); - fcx.inh.deferred_resolutions.borrow_mut() - .retain(|r| !r.attempt_resolution(fcx)); select_fcx_obligations_where_possible(fcx); fcx.default_type_parameters(); select_fcx_obligations_where_possible(fcx); @@ -290,6 +288,10 @@ pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) { pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { debug!("select_all_fcx_obligations_or_error"); + // upvar inference should have ensured that all deferrred call + // resolutions are handled by now. + assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty()); + select_all_fcx_obligations_and_apply_defaults(fcx); let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut(); let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx); diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs new file mode 100644 index 0000000000000..7398e6f1089bc --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-1.rs @@ -0,0 +1,33 @@ +// Copyright 2015 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. + +// Various unsuccessful attempts to put the unboxed closure kind +// inference into an awkward position that might require fixed point +// iteration (basically where inferring the kind of a closure `c` +// would require knowing the kind of `c`). I currently believe this is +// impossible. + +fn a() { + // This case of recursion wouldn't even require fixed-point + // iteration, but it still doesn't work. The weird structure with + // the `Option` is to avoid giving any useful hints about the `Fn` + // kind via the expected type. + let mut factorial: Option u32>> = None; + + let f = |x: u32| -> u32 { + let g = factorial.as_ref().unwrap(); + if x == 0 {1} else {x * g(x-1)} + }; + + factorial = Some(Box::new(f)); + //~^ ERROR cannot assign to `factorial` because it is borrowed +} + +fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs new file mode 100644 index 0000000000000..f40c8fc747494 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-failed-recursive-fn-2.rs @@ -0,0 +1,39 @@ +// Copyright 2015 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. + +// Various unsuccessful attempts to put the unboxed closure kind +// inference into an awkward position that might require fixed point +// iteration (basically where inferring the kind of a closure `c` +// would require knowing the kind of `c`). I currently believe this is +// impossible. + +fn a() { + let mut closure0 = None; + let vec = vec!(1, 2, 3); + + loop { + { + let closure1 = || { + match closure0.take() { + Some(c) => { + return c(); + //~^ ERROR the type of this value must be known in this context + } + None => { } + } + }; + closure1(); + } + + closure0 = || vec; + } +} + +fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs new file mode 100644 index 0000000000000..afbc141b5d246 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs @@ -0,0 +1,32 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + + // Here this must be inferred to FnMut so that it can mutate counter, + // but we forgot the mut. + let tick1 = || { + counter += 1; + }; + + // In turn, tick2 must be inferred to FnMut so that it can call + // tick1, but we forgot the mut. The error message we currently + // get seems... suboptimal. + let tick2 = || { //~ ERROR closure cannot assign to immutable local variable `tick1` + tick1(); + }; + + tick2(); //~ ERROR cannot borrow +} + diff --git a/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs new file mode 100644 index 0000000000000..215b2c6798e40 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-recursive-fn-using-fn-mut.rs @@ -0,0 +1,48 @@ +// Copyright 2015 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. + +#![feature(core,unboxed_closures)] + +use std::marker::CovariantType; + +// A erroneous variant of `run-pass/unboxed_closures-infer-recursive-fn.rs` +// where we attempt to perform mutation in the recursive function. This fails to compile +// because it winds up requiring `FnMut` which enforces linearity. + +struct YCombinator { + func: F, + marker: CovariantType<(A,R)>, +} + +impl YCombinator { + fn new(f: F) -> YCombinator { + YCombinator { func: f, marker: CovariantType } + } +} + +impl R, A) -> R> FnMut<(A,)> for YCombinator { + type Output = R; + + extern "rust-call" fn call_mut(&mut self, (arg,): (A,)) -> R { + (self.func)(self, arg) + //~^ ERROR cannot borrow `*self` as mutable more than once at a time + } +} + +fn main() { + let mut counter = 0; + let factorial = |recur: &mut FnMut(u32) -> u32, arg: u32| -> u32 { + counter += 1; + if arg == 0 {1} else {arg * recur(arg-1)} + }; + let mut factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial); + let mut r = factorial(10); + assert_eq!(3628800, r); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs new file mode 100644 index 0000000000000..09b8c8f4454b5 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut-calling-fnmut.rs @@ -0,0 +1,29 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + + { + // Here this must be inferred to FnMut so that it can mutate counter: + let mut tick1 = || counter += 1; + + // In turn, tick2 must be inferred to FnMut so that it can call tick1: + let mut tick2 = || { tick1(); tick1(); }; + + tick2(); + } + + assert_eq!(counter, 2); +} + diff --git a/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs new file mode 100644 index 0000000000000..1f9b821178c46 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-recursive-fn.rs @@ -0,0 +1,47 @@ +// Copyright 2015 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. + +#![feature(core,unboxed_closures)] + +use std::marker::CovariantType; + +// Test that we are able to infer a suitable kind for a "recursive" +// closure. As far as I can tell, coding up a recursive closure +// requires the good ol' [Y Combinator]. +// +// [Y Combinator]: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator + +struct YCombinator { + func: F, + marker: CovariantType<(A,R)>, +} + +impl YCombinator { + fn new(f: F) -> YCombinator { + YCombinator { func: f, marker: CovariantType } + } +} + +impl R, A) -> R> Fn<(A,)> for YCombinator { + type Output = R; + + extern "rust-call" fn call(&self, (arg,): (A,)) -> R { + (self.func)(self, arg) + } +} + +fn main() { + let factorial = |recur: &Fn(u32) -> u32, arg: u32| -> u32 { + if arg == 0 {1} else {arg * recur(arg-1)} + }; + let factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial); + let r = factorial(10); + assert_eq!(3628800, r); +} From e778ea40701751ca15b19a8983949573ea9e3a07 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 31 Jan 2015 20:16:17 -0500 Subject: [PATCH 08/10] Tweak comments per eddyb's suggestion. --- src/librustc_typeck/check/closure.rs | 3 ++- src/librustc_typeck/check/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 2bbf832fdef01..808dbd4b31910 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -42,7 +42,8 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, // If users didn't specify what sort of closure they want, // examine the expected type. For now, if we see explicit // evidence than an unboxed closure is desired, we'll use - // that, otherwise we'll error, requesting an annotation. + // that. Otherwise, we leave it unspecified, to be filled + // in by upvar inference. match expected_sig_and_kind { None => { // don't have information about the kind, request explicit annotation check_closure(fcx, expr, None, decl, body, None); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0aad1f99ce8f2..268e61b994e14 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -177,7 +177,7 @@ pub struct Inherited<'a, 'tcx: 'a> { // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or // `FnOnce` closure. In that case, we defer full resolution of the // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions sorted by the + // decision. We keep these deferred resolutions grouped by the // def-id of the closure, so that once we decide, we can easily go // back and process them. deferred_call_resolutions: RefCell>>>, From a9c3841a501691f36f98c47402d679885989637f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 31 Jan 2015 20:58:26 -0500 Subject: [PATCH 09/10] Fix handling of `move` closures -- since they have one fewer deref, we weren't properly adjusting the closure kind in that case. --- src/librustc_typeck/check/upvar.rs | 98 ++++++++++++------- ...d-closures-infer-fnmut-move-missing-mut.rs | 18 ++++ ...d-closures-infer-fnonce-move-call-twice.rs | 21 ++++ .../unboxed-closures-infer-fnmut-move.rs | 25 +++++ .../unboxed-closures-infer-fnonce-move.rs | 37 +++++++ .../run-pass/unboxed-closures-infer-fnonce.rs | 2 +- 6 files changed, 167 insertions(+), 34 deletions(-) create mode 100644 src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs create mode 100644 src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-fnmut-move.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-fnonce-move.rs diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 961a2e39dc08f..f7babadd41fd1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -263,16 +263,29 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { match guarantor.cat { mc::cat_deref(_, _, mc::BorrowedPtr(..)) | mc::cat_deref(_, _, mc::Implicit(..)) => { - if let mc::NoteUpvarRef(upvar_id) = cmt.note { - debug!("adjust_upvar_borrow_kind_for_consume: \ - setting upvar_id={:?} to by value", - upvar_id); + match cmt.note { + mc::NoteUpvarRef(upvar_id) => { + debug!("adjust_upvar_borrow_kind_for_consume: \ + setting upvar_id={:?} to by value", + upvar_id); - // to move out of an upvar, this must be a FnOnce closure - self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind); + // to move out of an upvar, this must be a FnOnce closure + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind); - let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); - upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue); + let mut upvar_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); + upvar_capture_map.insert(upvar_id, ty::UpvarCapture::ByValue); + } + mc::NoteClosureEnv(upvar_id) => { + // we get just a closureenv ref if this is a + // `move` closure, or if the upvar has already + // been inferred to by-value. In any case, we + // must still adjust the kind of the closure + // to be a FnOnce closure to permit moves out + // of the environment. + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnOnceClosureKind); + } + mc::NoteNone => { + } } } _ => { } @@ -297,15 +310,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { 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_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); - let ub = &mut upvar_capture_map[upvar_id]; - self.adjust_upvar_borrow_kind(upvar_id, ub, ty::MutBorrow); - } else { + if !self.try_adjust_upvar_deref(&cmt.note, ty::MutBorrow) { // assignment to deref of an `&mut` // borrowed pointer implies that the // pointer itself must be unique, but not @@ -339,15 +344,7 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { 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_capture_map.borrow_mut(); - let ub = &mut ub[upvar_id]; - self.adjust_upvar_borrow_kind(upvar_id, ub, ty::UniqueImmBorrow); - } else { + if !self.try_adjust_upvar_deref(&cmt.note, ty::UniqueImmBorrow) { // for a borrowed pointer to be unique, its // base must be unique self.adjust_upvar_borrow_kind_for_unique(base); @@ -363,6 +360,48 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { } } + fn try_adjust_upvar_deref(&self, + note: &mc::Note, + borrow_kind: ty::BorrowKind) + -> bool + { + assert!(match borrow_kind { + ty::MutBorrow => true, + ty::UniqueImmBorrow => true, + + // imm borrows never require adjusting any kinds, so we don't wind up here + ty::ImmBorrow => false, + }); + + match *note { + mc::NoteUpvarRef(upvar_id) => { + // 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_capture_map = self.fcx.inh.upvar_capture_map.borrow_mut(); + let ub = &mut upvar_capture_map[upvar_id]; + self.adjust_upvar_borrow_kind(upvar_id, ub, borrow_kind); + + // also need to be in an FnMut closure since this is not an ImmBorrow + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind); + + true + } + mc::NoteClosureEnv(upvar_id) => { + // this kind of deref occurs in a `move` closure, or + // for a by-value upvar; in either case, to mutate an + // upvar, we need to be an FnMut closure + self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind); + + true + } + mc::NoteNone => { + false + } + } + } + /// 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 @@ -374,13 +413,6 @@ impl<'a,'tcx> AdjustBorrowKind<'a,'tcx> { debug!("adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", upvar_id, upvar_capture, kind); - match kind { - ty::ImmBorrow => { } - ty::UniqueImmBorrow | ty::MutBorrow => { - self.adjust_closure_kind(upvar_id.closure_expr_id, ty::FnMutClosureKind); - } - } - match *upvar_capture { ty::UpvarCapture::ByValue => { // Upvar is already by-value, the strongest criteria. diff --git a/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs new file mode 100644 index 0000000000000..de17d25b4c360 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnmut-move-missing-mut.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + let tick = move || counter += 1; + tick(); //~ ERROR cannot borrow immutable local variable `tick` as mutable +} diff --git a/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs new file mode 100644 index 0000000000000..f9709b8c59697 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-fnonce-move-call-twice.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this closure +// that is just called (`FnMut`). + +use std::mem; + +fn main() { + let mut counter: Vec = Vec::new(); + let tick = move || mem::drop(counter); + tick(); + tick(); //~ ERROR use of moved value: `tick` +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs new file mode 100644 index 0000000000000..794527249bffa --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnmut-move.rs @@ -0,0 +1,25 @@ +// Copyright 2015 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 we are able to infer a suitable kind for this `move` +// closure that is just called (`FnMut`). + +fn main() { + let mut counter = 0; + + let v = { + let mut tick = move || { counter += 1; counter }; + tick(); + tick() + }; + + assert_eq!(counter, 0); + assert_eq!(v, 2); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs new file mode 100644 index 0000000000000..9f8fc80819bfd --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-fnonce-move.rs @@ -0,0 +1,37 @@ +// Copyright 2015 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. + +#![feature(unsafe_destructor)] + +// Test that we are able to infer a suitable kind for this `move` +// closure that is just called (`FnOnce`). + +use std::mem; + +struct DropMe<'a>(&'a mut i32); + +#[unsafe_destructor] +impl<'a> Drop for DropMe<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn main() { + let mut counter = 0; + + { + let drop_me = DropMe(&mut counter); + let tick = move || mem::drop(drop_me); + tick(); + } + + assert_eq!(counter, 1); +} diff --git a/src/test/run-pass/unboxed-closures-infer-fnonce.rs b/src/test/run-pass/unboxed-closures-infer-fnonce.rs index 69beae7718462..f0f10139c5b86 100644 --- a/src/test/run-pass/unboxed-closures-infer-fnonce.rs +++ b/src/test/run-pass/unboxed-closures-infer-fnonce.rs @@ -11,7 +11,7 @@ #![feature(unsafe_destructor)] // Test that we are able to infer a suitable kind for this closure -// that is just called (`FnMut`). +// that is just called (`FnOnce`). use std::mem; From 870aea216bb6f6d6a238c03e186a01bff8820ac0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 1 Feb 2015 06:07:52 -0500 Subject: [PATCH 10/10] Do not ICE when e.g. `call_mut()` is called on a closure whose kind is not yet known. --- src/librustc_typeck/check/method/mod.rs | 10 +++- src/librustc_typeck/check/method/probe.rs | 60 +++++++++++-------- src/librustc_typeck/check/method/suggest.rs | 14 +++++ src/librustc_typeck/check/mod.rs | 3 +- ...-closures-infer-explicit-call-too-early.rs | 18 ++++++ 5 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 24e9f1c8720ee..88455b3385a1e 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -44,6 +44,9 @@ pub enum MethodError { // Multiple methods might apply. Ambiguity(Vec), + + // Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind. + ClosureAmbiguity(/* DefId of fn trait */ ast::DefId), } // A pared down enum describing just the places from which a method @@ -65,9 +68,10 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, -> bool { match probe::probe(fcx, span, method_name, self_ty, call_expr_id) { - Ok(_) => true, - Err(NoMatch(_, _)) => false, - Err(Ambiguity(_)) => true, + Ok(..) => true, + Err(NoMatch(..)) => false, + Err(Ambiguity(..)) => true, + Err(ClosureAmbiguity(..)) => true, } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 8000776ad45f9..e9ea0921bc9e8 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::{MethodError,Ambiguity,NoMatch}; +use super::{MethodError}; use super::MethodIndex; use super::{CandidateSource,ImplSource,TraitSource}; use super::suggest; @@ -129,7 +129,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // take place in the `fcx.infcx().probe` below. let steps = match create_steps(fcx, span, self_ty) { Some(steps) => steps, - None => return Err(NoMatch(Vec::new(), Vec::new())), + None => return Err(MethodError::NoMatch(Vec::new(), Vec::new())), }; // Create a list of simplified self types, if we can. @@ -158,7 +158,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let (steps, opt_simplified_steps) = dummy.take().unwrap(); let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps); probe_cx.assemble_inherent_candidates(); - probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id); + try!(probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id)); probe_cx.pick() }) } @@ -444,29 +444,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_id: ast::NodeId) + -> Result<(),MethodError> { let mut duplicates = HashSet::new(); let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id); for applicable_traits in opt_applicable_traits.into_iter() { for &trait_did in applicable_traits.iter() { if duplicates.insert(trait_did) { - self.assemble_extension_candidates_for_trait(trait_did); + try!(self.assemble_extension_candidates_for_trait(trait_did)); } } } + Ok(()) } - fn assemble_extension_candidates_for_all_traits(&mut self) { + fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> { let mut duplicates = HashSet::new(); for trait_info in suggest::all_traits(self.fcx.ccx) { if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait(trait_info.def_id) + try!(self.assemble_extension_candidates_for_trait(trait_info.def_id)); } } + Ok(()) } fn assemble_extension_candidates_for_trait(&mut self, - trait_def_id: ast::DefId) { + trait_def_id: ast::DefId) + -> Result<(),MethodError> + { debug!("assemble_extension_candidates_for_trait(trait_def_id={})", trait_def_id.repr(self.tcx())); @@ -478,26 +483,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { .position(|item| item.name() == self.method_name); let matching_index = match matching_index { Some(i) => i, - None => { return; } + None => { return Ok(()); } }; let method = match (&*trait_items)[matching_index].as_opt_method() { Some(m) => m, - None => { return; } + None => { return Ok(()); } }; // Check whether `trait_def_id` defines a method with suitable name: if !self.has_applicable_self(&*method) { debug!("method has inapplicable self"); - return self.record_static_candidate(TraitSource(trait_def_id)); + self.record_static_candidate(TraitSource(trait_def_id)); + return Ok(()); } self.assemble_extension_candidates_for_trait_impls(trait_def_id, method.clone(), matching_index); - self.assemble_closure_candidates(trait_def_id, - method.clone(), - matching_index); + try!(self.assemble_closure_candidates(trait_def_id, + method.clone(), + matching_index)); self.assemble_projection_candidates(trait_def_id, method.clone(), @@ -506,6 +512,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { self.assemble_where_clause_candidates(trait_def_id, method, matching_index); + + Ok(()) } fn assemble_extension_candidates_for_trait_impls(&mut self, @@ -576,6 +584,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { trait_def_id: ast::DefId, method_ty: Rc>, method_index: uint) + -> Result<(),MethodError> { // Check if this is one of the Fn,FnMut,FnOnce traits. let tcx = self.tcx(); @@ -586,7 +595,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { ty::FnOnceClosureKind } else { - return; + return Ok(()); }; // Check if there is an unboxed-closure self-type in the list of receivers. @@ -602,10 +611,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { let closure_kind = match closure_kinds.get(&closure_def_id) { Some(&k) => k, None => { - self.tcx().sess.span_bug( - self.span, - &format!("No entry for closure: {}", - closure_def_id.repr(self.tcx()))[]); + return Err(MethodError::ClosureAmbiguity(trait_def_id)); } }; @@ -630,6 +636,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { kind: ClosureCandidate(trait_def_id, method_index) }); } + + Ok(()) } fn assemble_projection_candidates(&mut self, @@ -735,11 +743,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { let span = self.span; let tcx = self.tcx(); - self.assemble_extension_candidates_for_all_traits(); + try!(self.assemble_extension_candidates_for_all_traits()); let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.method_ty.container.id()], - Some(Err(Ambiguity(v))) => v.into_iter().map(|source| { + Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| { match source { TraitSource(id) => id, ImplSource(impl_id) => { @@ -752,14 +760,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { } } }).collect(), - Some(Err(NoMatch(_, others))) => { + Some(Err(MethodError::NoMatch(_, others))) => { assert!(others.is_empty()); vec![] } + Some(Err(MethodError::ClosureAmbiguity(..))) => { + // this error only occurs when assembling candidates + tcx.sess.span_bug(span, "encountered ClosureAmbiguity from pick_core"); + } None => vec![], }; -; - Err(NoMatch(static_candidates, out_of_scope_traits)) + + Err(MethodError::NoMatch(static_candidates, out_of_scope_traits)) } fn pick_core(&mut self) -> Option> { @@ -895,7 +907,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { if applicable_candidates.len() > 1 { let sources = probes.iter().map(|p| p.to_source()).collect(); - return Some(Err(Ambiguity(sources))); + return Some(Err(MethodError::Ambiguity(sources))); } applicable_candidates.pop().map(|probe| { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 3cf9a1a945668..bd5060c940e50 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -22,6 +22,7 @@ use util::ppaux::UserString; use syntax::{ast, ast_util}; use syntax::codemap::Span; +use syntax::print::pprust; use std::cell; use std::cmp::Ordering; @@ -32,6 +33,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, rcvr_ty: Ty<'tcx>, method_name: ast::Name, + callee_expr: &ast::Expr, error: MethodError) { match error { @@ -84,6 +86,18 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, report_candidates(fcx, span, method_name, sources); } + + MethodError::ClosureAmbiguity(trait_def_id) => { + fcx.sess().span_err( + span, + &*format!("the `{}` method from the `{}` trait cannot be explicitly \ + invoked on this closure as we have not yet inferred what \ + kind of closure it is; use overloaded call notation instead \ + (e.g., `{}()`)", + method_name.user_string(fcx.tcx()), + ty::item_path_str(fcx.tcx(), trait_def_id), + pprust::expr_to_string(callee_expr))); + } } fn report_candidates(fcx: &FnCtxt, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 268e61b994e14..c193e1ef48364 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2695,7 +2695,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, method_ty } Err(error) => { - method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error); + method::report_error(fcx, method_name.span, expr_t, + method_name.node.name, rcvr, error); fcx.write_error(expr.id); fcx.tcx().types.err } diff --git a/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs new file mode 100644 index 0000000000000..f993b8fa8c4d1 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-explicit-call-too-early.rs @@ -0,0 +1,18 @@ +// 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. + +#![feature(unboxed_closures)] + +fn main() { + let mut zero = || {}; + let () = zero.call_mut(()); + //~^ ERROR we have not yet inferred what kind of closure it is +} +