Deduce the argument types based on the expected type, trawling through the fulfillment contect if necessary.

This commit is contained in:
Niko Matsakis 2014-11-18 16:13:24 -05:00
parent fe2fcb39f4
commit 8e44688889
8 changed files with 249 additions and 11 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T,F>(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
});
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T,F>(val: T, f: &F)
where F : Fn(T)
{
f.call((val,))
}
pub fn main() {
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>(val: T, f: &Fn(T)) { f.call((val,)) }
pub fn main() {
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T,F>(val: T, f: &F)
where F : Fn(&T)
{
f.call((&val,))
}
pub fn main() {
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
}