diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 5b8edacb28d..62382ac386f 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -109,6 +109,10 @@ impl<'tcx> FulfillmentContext<'tcx> { self.select(&mut selcx, false) } + pub fn pending_trait_obligations(&self) -> &[Obligation<'tcx>] { + self.trait_obligations[] + } + fn select<'a>(&mut self, selcx: &mut SelectionContext<'a, 'tcx>, only_new_obligations: bool) diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 12b0a90bf7c..6e6e2f1eee5 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -13,10 +13,11 @@ */ use super::check_fn; -use super::Expectation; +use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation}; use super::FnCtxt; -use middle::ty; +use middle::subst; +use middle::ty::{mod, Ty}; use middle::typeck::astconv; use middle::typeck::infer; use middle::typeck::rscope::RegionScope; @@ -25,13 +26,40 @@ use syntax::ast; use syntax::ast_util; use util::ppaux::Repr; -pub fn check_unboxed_closure(fcx: &FnCtxt, - expr: &ast::Expr, - kind: ast::UnboxedClosureKind, - decl: &ast::FnDecl, - body: &ast::Block) { +pub fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + kind: ast::UnboxedClosureKind, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { let expr_def_id = ast_util::local_def(expr.id); + let expected_sig_and_kind = match expected.resolve(fcx) { + NoExpectation => None, + ExpectCastableToType(t) | ExpectHasType(t) => { + deduce_unboxed_closure_expectations_from_expected_type(fcx, t) + } + }; + + let (expected_sig, expected_kind) = match expected_sig_and_kind { + None => (None, None), + Some((sig, kind)) => { + // Avoid accidental capture of bound regions by renaming + // them to fresh names, basically. + let sig = + ty::replace_late_bound_regions( + fcx.tcx(), + &sig, + |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)).0; + (Some(sig), Some(kind)) + } + }; + + debug!("check_unboxed_closure expected={} expected_sig={} expected_kind={}", + expected.repr(fcx.tcx()), + expected_sig.repr(fcx.tcx()), + expected_kind); + let mut fn_ty = astconv::ty_of_closure( fcx, ast::NormalFn, @@ -46,7 +74,7 @@ pub fn check_unboxed_closure(fcx: &FnCtxt, decl, abi::RustCall, - None); + expected_sig); let region = match fcx.infcx().anon_regions(expr.span, 1) { Err(_) => { @@ -98,6 +126,95 @@ pub fn check_unboxed_closure(fcx: &FnCtxt, .insert(expr_def_id, unboxed_closure); } +fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expected_ty: Ty<'tcx>) + -> Option<(ty::FnSig<'tcx>, + ty::UnboxedClosureKind)> +{ + match expected_ty.sty { + ty::ty_trait(ref object_type) => { + deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal) + } + ty::ty_infer(ty::TyVar(vid)) => { + deduce_unboxed_closure_expectations_from_obligations(fcx, vid) + } + _ => { + None + } + } +} + +fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + trait_ref: &ty::TraitRef<'tcx>) + -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)> +{ + let tcx = fcx.tcx(); + + debug!("deduce_unboxed_closure_expectations_from_object_type({})", + trait_ref.repr(tcx)); + + let def_id_kinds = [ + (tcx.lang_items.fn_trait(), ty::FnUnboxedClosureKind), + (tcx.lang_items.fn_mut_trait(), ty::FnMutUnboxedClosureKind), + (tcx.lang_items.fn_once_trait(), ty::FnOnceUnboxedClosureKind), + ]; + + for &(def_id, kind) in def_id_kinds.iter() { + if Some(trait_ref.def_id) == def_id { + debug!("found object type {}", kind); + + let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0); + let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty); + debug!("arg_param_ty {}", arg_param_ty.repr(tcx)); + + let input_tys = match arg_param_ty.sty { + ty::ty_tup(ref tys) => { (*tys).clone() } + _ => { continue; } + }; + debug!("input_tys {}", input_tys.repr(tcx)); + + let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1); + let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty); + debug!("ret_param_ty {}", ret_param_ty.repr(tcx)); + + let fn_sig = ty::FnSig { + inputs: input_tys, + output: ty::FnConverging(ret_param_ty), + variadic: false + }; + debug!("fn_sig {}", fn_sig.repr(tcx)); + + return Some((fn_sig, kind)); + } + } + + None +} + +fn deduce_unboxed_closure_expectations_from_obligations<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + expected_vid: ty::TyVid) + -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)> +{ + // Here `expected_ty` is known to be a type inference variable. + for obligation in fcx.inh.fulfillment_cx.borrow().pending_trait_obligations().iter() { + let obligation_self_ty = fcx.infcx().shallow_resolve(obligation.self_ty()); + match obligation_self_ty.sty { + ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { } + _ => { continue; } + } + + match deduce_unboxed_closure_expectations_from_trait_ref(fcx, &*obligation.trait_ref) { + Some(e) => { return Some(e); } + None => { } + } + } + + None +} + + pub fn check_expr_fn<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, store: ty::TraitStore, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 02ac6379a56..28648d6d4e2 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -78,7 +78,7 @@ type parameter). pub use self::LvaluePreference::*; pub use self::DerefArgs::*; -use self::Expectation::*; +pub use self::Expectation::*; use self::IsBinopAssignment::*; use self::TupleArgumentsFlag::*; @@ -97,7 +97,7 @@ use middle::ty::{FnSig, VariantInfo}; use middle::ty::{Polytype}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; use middle::ty::{mod, Ty}; -use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions}; +use middle::ty::liberate_late_bound_regions; use middle::ty_fold::TypeFolder; use middle::typeck::astconv::AstConv; use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty}; @@ -4165,7 +4165,8 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr, kind, &**decl, - &**body); + &**body, + expected); } ast::ExprProc(ref decl, ref body) => { closure::check_expr_fn(fcx, diff --git a/src/test/compile-fail/regions-escape-unboxed-closure.rs b/src/test/compile-fail/regions-escape-unboxed-closure.rs new file mode 100644 index 00000000000..70f0d61b5ee --- /dev/null +++ b/src/test/compile-fail/regions-escape-unboxed-closure.rs @@ -0,0 +1,19 @@ +// Copyright 2012 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 with_int(f: &mut FnMut(&int)) { +} + +fn main() { + let mut x: Option<&int> = None; + with_int(&mut |&mut: y| x = Some(y)); //~ ERROR cannot infer +} diff --git a/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs new file mode 100644 index 00000000000..72109b22957 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs @@ -0,0 +1,29 @@ +// 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. + +// That a closure whose expected argument types include two distinct +// bound regions. + +#![feature(unboxed_closures)] + +use std::cell::Cell; + +fn doit(val: T, f: &F) + where F : Fn(&Cell<&T>, &T) +{ + let x = Cell::new(&val); + f.call((&x,&val)) +} + +pub fn main() { + doit(0i, &|&: x, y| { + x.set(y); //~ ERROR cannot infer + }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs new file mode 100644 index 00000000000..465c324122a --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &F) + where F : Fn(T) +{ + f.call((val,)) +} + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs new file mode 100644 index 00000000000..440292d202e --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs @@ -0,0 +1,20 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &Fn(T)) { f.call((val,)) } + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs new file mode 100644 index 00000000000..b279eb5fbba --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs @@ -0,0 +1,24 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &F) + where F : Fn(&T) +{ + f.call((&val,)) +} + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +}