diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 5b8edacb28d67..62382ac386fcd 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 new file mode 100644 index 0000000000000..6e6e2f1eee531 --- /dev/null +++ b/src/librustc/middle/typeck/check/closure.rs @@ -0,0 +1,310 @@ +// 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. + +/*! + * Code for type-checking closure expressions. + */ + +use super::check_fn; +use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation}; +use super::FnCtxt; + +use middle::subst; +use middle::ty::{mod, Ty}; +use middle::typeck::astconv; +use middle::typeck::infer; +use middle::typeck::rscope::RegionScope; +use syntax::abi; +use syntax::ast; +use syntax::ast_util; +use util::ppaux::Repr; + +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, + ast::Many, + + // The `RegionTraitStore` and region_existential_bounds + // are lies, but we ignore them so it doesn't matter. + // + // FIXME(pcwalton): Refactor this API. + ty::region_existential_bound(ty::ReStatic), + ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable), + + decl, + abi::RustCall, + expected_sig); + + let region = match fcx.infcx().anon_regions(expr.span, 1) { + Err(_) => { + fcx.ccx.tcx.sess.span_bug(expr.span, + "can't make anon regions here?!") + } + Ok(regions) => regions[0], + }; + + let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx, + expr_def_id, + region, + fcx.inh.param_env.free_substs.clone()); + + fcx.write_ty(expr.id, closure_type); + + check_fn(fcx.ccx, + ast::NormalFn, + expr.id, + &fn_ty.sig, + decl, + expr.id, + &*body, + fcx.inh); + + // Tuple up the arguments and insert the resulting function type into + // the `unboxed_closures` table. + fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; + + let kind = match kind { + ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, + ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, + ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, + }; + + debug!("unboxed_closure for {} --> sig={} kind={}", + expr_def_id.repr(fcx.tcx()), + fn_ty.sig.repr(fcx.tcx()), + kind); + + let unboxed_closure = ty::UnboxedClosure { + closure_type: fn_ty, + kind: kind, + }; + + fcx.inh + .unboxed_closures + .borrow_mut() + .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, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { + let tcx = fcx.ccx.tcx; + + // Find the expected input/output types (if any). Substitute + // fresh bound regions for any bound regions we find in the + // expected types so as to avoid capture. + let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone())); + let (expected_sig, + expected_onceness, + expected_bounds) = { + match expected_sty { + Some(ty::ty_closure(ref cenv)) => { + let (sig, _) = + ty::replace_late_bound_regions( + tcx, + &cenv.sig, + |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)); + let onceness = match (&store, &cenv.store) { + // As the closure type and onceness go, only three + // combinations are legit: + // once closure + // many closure + // once proc + // If the actual and expected closure type disagree with + // each other, set expected onceness to be always Once or + // Many according to the actual type. Otherwise, it will + // yield either an illegal "many proc" or a less known + // "once closure" in the error message. + (&ty::UniqTraitStore, &ty::UniqTraitStore) | + (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) => + cenv.onceness, + (&ty::UniqTraitStore, _) => ast::Once, + (&ty::RegionTraitStore(..), _) => ast::Many, + }; + (Some(sig), onceness, cenv.bounds) + } + _ => { + // Not an error! Means we're inferring the closure type + let (bounds, onceness) = match expr.node { + ast::ExprProc(..) => { + let mut bounds = ty::region_existential_bound(ty::ReStatic); + bounds.builtin_bounds.insert(ty::BoundSend); // FIXME + (bounds, ast::Once) + } + _ => { + let region = fcx.infcx().next_region_var( + infer::AddrOfRegion(expr.span)); + (ty::region_existential_bound(region), ast::Many) + } + }; + (None, onceness, bounds) + } + } + }; + + // construct the function type + let fn_ty = astconv::ty_of_closure(fcx, + ast::NormalFn, + expected_onceness, + expected_bounds, + store, + decl, + abi::Rust, + expected_sig); + let fty_sig = fn_ty.sig.clone(); + let fty = ty::mk_closure(tcx, fn_ty); + debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty)); + + fcx.write_ty(expr.id, fty); + + // If the closure is a stack closure and hasn't had some non-standard + // style inferred for it, then check it under its parent's style. + // Otherwise, use its own + let (inherited_style, inherited_style_id) = match store { + ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style, + fcx.ps.borrow().def), + ty::UniqTraitStore => (ast::NormalFn, expr.id) + }; + + check_fn(fcx.ccx, + inherited_style, + inherited_style_id, + &fty_sig, + &*decl, + expr.id, + &*body, + fcx.inh); +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 543eb44697c06..28648d6d4e22c 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}; @@ -147,6 +147,7 @@ pub mod regionck; pub mod demand; pub mod method; pub mod wf; +mod closure; /// Fields that are part of a `FnCtxt` which are inherited by /// closures defined within the function. For example: @@ -3519,174 +3520,6 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, }) } - fn check_unboxed_closure(fcx: &FnCtxt, - expr: &ast::Expr, - kind: ast::UnboxedClosureKind, - decl: &ast::FnDecl, - body: &ast::Block) { - let mut fn_ty = astconv::ty_of_closure( - fcx, - ast::NormalFn, - ast::Many, - - // The `RegionTraitStore` and region_existential_bounds - // are lies, but we ignore them so it doesn't matter. - // - // FIXME(pcwalton): Refactor this API. - ty::region_existential_bound(ty::ReStatic), - ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable), - - decl, - abi::RustCall, - None); - - let region = match fcx.infcx().anon_regions(expr.span, 1) { - Err(_) => { - fcx.ccx.tcx.sess.span_bug(expr.span, - "can't make anon regions here?!") - } - Ok(regions) => regions[0], - }; - let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx, - local_def(expr.id), - region, - fcx.inh.param_env.free_substs.clone()); - fcx.write_ty(expr.id, closure_type); - - check_fn(fcx.ccx, - ast::NormalFn, - expr.id, - &fn_ty.sig, - decl, - expr.id, - &*body, - fcx.inh); - - // Tuple up the arguments and insert the resulting function type into - // the `unboxed_closures` table. - fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; - - let kind = match kind { - ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, - ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, - ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, - }; - - debug!("unboxed_closure for {} --> sig={} kind={}", - local_def(expr.id).repr(fcx.tcx()), - fn_ty.sig.repr(fcx.tcx()), - kind); - - let unboxed_closure = ty::UnboxedClosure { - closure_type: fn_ty, - kind: kind, - }; - - fcx.inh - .unboxed_closures - .borrow_mut() - .insert(local_def(expr.id), unboxed_closure); - } - - fn check_expr_fn<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, - store: ty::TraitStore, - decl: &ast::FnDecl, - body: &ast::Block, - expected: Expectation<'tcx>) { - let tcx = fcx.ccx.tcx; - - debug!("check_expr_fn(expr={}, expected={})", - expr.repr(tcx), - expected.repr(tcx)); - - // Find the expected input/output types (if any). Substitute - // fresh bound regions for any bound regions we find in the - // expected types so as to avoid capture. - let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone())); - let (expected_sig, - expected_onceness, - expected_bounds) = { - match expected_sty { - Some(ty::ty_closure(ref cenv)) => { - let (sig, _) = - replace_late_bound_regions( - tcx, - &cenv.sig, - |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)); - let onceness = match (&store, &cenv.store) { - // As the closure type and onceness go, only three - // combinations are legit: - // once closure - // many closure - // once proc - // If the actual and expected closure type disagree with - // each other, set expected onceness to be always Once or - // Many according to the actual type. Otherwise, it will - // yield either an illegal "many proc" or a less known - // "once closure" in the error message. - (&ty::UniqTraitStore, &ty::UniqTraitStore) | - (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) => - cenv.onceness, - (&ty::UniqTraitStore, _) => ast::Once, - (&ty::RegionTraitStore(..), _) => ast::Many, - }; - (Some(sig), onceness, cenv.bounds) - } - _ => { - // Not an error! Means we're inferring the closure type - let (bounds, onceness) = match expr.node { - ast::ExprProc(..) => { - let mut bounds = ty::region_existential_bound(ty::ReStatic); - bounds.builtin_bounds.insert(ty::BoundSend); // FIXME - (bounds, ast::Once) - } - _ => { - let region = fcx.infcx().next_region_var( - infer::AddrOfRegion(expr.span)); - (ty::region_existential_bound(region), ast::Many) - } - }; - (None, onceness, bounds) - } - } - }; - - // construct the function type - let fn_ty = astconv::ty_of_closure(fcx, - ast::NormalFn, - expected_onceness, - expected_bounds, - store, - decl, - abi::Rust, - expected_sig); - let fty_sig = fn_ty.sig.clone(); - let fty = ty::mk_closure(tcx, fn_ty); - debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty)); - - fcx.write_ty(expr.id, fty); - - // If the closure is a stack closure and hasn't had some non-standard - // style inferred for it, then check it under its parent's style. - // Otherwise, use its own - let (inherited_style, inherited_style_id) = match store { - ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style, - fcx.ps.borrow().def), - ty::UniqTraitStore => (ast::NormalFn, expr.id) - }; - - check_fn(fcx.ccx, - inherited_style, - inherited_style_id, - &fty_sig, - &*decl, - expr.id, - &*body, - fcx.inh); - } - - // Check field access expressions fn check_field(fcx: &FnCtxt, expr: &ast::Expr, @@ -4320,27 +4153,28 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fcx.infcx(), expr.span, &None); - check_expr_fn(fcx, - expr, - ty::RegionTraitStore(region, ast::MutMutable), - &**decl, - &**body, - expected); + closure::check_expr_fn(fcx, + expr, + ty::RegionTraitStore(region, ast::MutMutable), + &**decl, + &**body, + expected); } ast::ExprUnboxedFn(_, kind, ref decl, ref body) => { - check_unboxed_closure(fcx, - expr, - kind, - &**decl, - &**body); + closure::check_unboxed_closure(fcx, + expr, + kind, + &**decl, + &**body, + expected); } ast::ExprProc(ref decl, ref body) => { - check_expr_fn(fcx, - expr, - ty::UniqTraitStore, - &**decl, - &**body, - expected); + closure::check_expr_fn(fcx, + expr, + ty::UniqTraitStore, + &**decl, + &**body, + expected); } ast::ExprBlock(ref b) => { check_block_with_expected(fcx, &**b, expected); 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 0000000000000..70f0d61b5ee1f --- /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 0000000000000..72109b22957e0 --- /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 0000000000000..465c324122a44 --- /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 0000000000000..440292d202e48 --- /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 0000000000000..b279eb5fbba90 --- /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(); }); +}