Skip to content

Commit 8e44688

Browse files
committed
Deduce the argument types based on the expected type, trawling through the fulfillment contect if necessary.
1 parent fe2fcb3 commit 8e44688

8 files changed

+249
-11
lines changed

src/librustc/middle/traits/fulfill.rs

+4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
109109
self.select(&mut selcx, false)
110110
}
111111

112+
pub fn pending_trait_obligations(&self) -> &[Obligation<'tcx>] {
113+
self.trait_obligations[]
114+
}
115+
112116
fn select<'a>(&mut self,
113117
selcx: &mut SelectionContext<'a, 'tcx>,
114118
only_new_obligations: bool)

src/librustc/middle/typeck/check/closure.rs

+125-8
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
*/
1414

1515
use super::check_fn;
16-
use super::Expectation;
16+
use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation};
1717
use super::FnCtxt;
1818

19-
use middle::ty;
19+
use middle::subst;
20+
use middle::ty::{mod, Ty};
2021
use middle::typeck::astconv;
2122
use middle::typeck::infer;
2223
use middle::typeck::rscope::RegionScope;
@@ -25,13 +26,40 @@ use syntax::ast;
2526
use syntax::ast_util;
2627
use util::ppaux::Repr;
2728

28-
pub fn check_unboxed_closure(fcx: &FnCtxt,
29-
expr: &ast::Expr,
30-
kind: ast::UnboxedClosureKind,
31-
decl: &ast::FnDecl,
32-
body: &ast::Block) {
29+
pub fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
30+
expr: &ast::Expr,
31+
kind: ast::UnboxedClosureKind,
32+
decl: &ast::FnDecl,
33+
body: &ast::Block,
34+
expected: Expectation<'tcx>) {
3335
let expr_def_id = ast_util::local_def(expr.id);
3436

37+
let expected_sig_and_kind = match expected.resolve(fcx) {
38+
NoExpectation => None,
39+
ExpectCastableToType(t) | ExpectHasType(t) => {
40+
deduce_unboxed_closure_expectations_from_expected_type(fcx, t)
41+
}
42+
};
43+
44+
let (expected_sig, expected_kind) = match expected_sig_and_kind {
45+
None => (None, None),
46+
Some((sig, kind)) => {
47+
// Avoid accidental capture of bound regions by renaming
48+
// them to fresh names, basically.
49+
let sig =
50+
ty::replace_late_bound_regions(
51+
fcx.tcx(),
52+
&sig,
53+
|_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)).0;
54+
(Some(sig), Some(kind))
55+
}
56+
};
57+
58+
debug!("check_unboxed_closure expected={} expected_sig={} expected_kind={}",
59+
expected.repr(fcx.tcx()),
60+
expected_sig.repr(fcx.tcx()),
61+
expected_kind);
62+
3563
let mut fn_ty = astconv::ty_of_closure(
3664
fcx,
3765
ast::NormalFn,
@@ -46,7 +74,7 @@ pub fn check_unboxed_closure(fcx: &FnCtxt,
4674

4775
decl,
4876
abi::RustCall,
49-
None);
77+
expected_sig);
5078

5179
let region = match fcx.infcx().anon_regions(expr.span, 1) {
5280
Err(_) => {
@@ -98,6 +126,95 @@ pub fn check_unboxed_closure(fcx: &FnCtxt,
98126
.insert(expr_def_id, unboxed_closure);
99127
}
100128

129+
fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
130+
expected_ty: Ty<'tcx>)
131+
-> Option<(ty::FnSig<'tcx>,
132+
ty::UnboxedClosureKind)>
133+
{
134+
match expected_ty.sty {
135+
ty::ty_trait(ref object_type) => {
136+
deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal)
137+
}
138+
ty::ty_infer(ty::TyVar(vid)) => {
139+
deduce_unboxed_closure_expectations_from_obligations(fcx, vid)
140+
}
141+
_ => {
142+
None
143+
}
144+
}
145+
}
146+
147+
fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
148+
fcx: &FnCtxt<'a,'tcx>,
149+
trait_ref: &ty::TraitRef<'tcx>)
150+
-> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)>
151+
{
152+
let tcx = fcx.tcx();
153+
154+
debug!("deduce_unboxed_closure_expectations_from_object_type({})",
155+
trait_ref.repr(tcx));
156+
157+
let def_id_kinds = [
158+
(tcx.lang_items.fn_trait(), ty::FnUnboxedClosureKind),
159+
(tcx.lang_items.fn_mut_trait(), ty::FnMutUnboxedClosureKind),
160+
(tcx.lang_items.fn_once_trait(), ty::FnOnceUnboxedClosureKind),
161+
];
162+
163+
for &(def_id, kind) in def_id_kinds.iter() {
164+
if Some(trait_ref.def_id) == def_id {
165+
debug!("found object type {}", kind);
166+
167+
let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0);
168+
let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty);
169+
debug!("arg_param_ty {}", arg_param_ty.repr(tcx));
170+
171+
let input_tys = match arg_param_ty.sty {
172+
ty::ty_tup(ref tys) => { (*tys).clone() }
173+
_ => { continue; }
174+
};
175+
debug!("input_tys {}", input_tys.repr(tcx));
176+
177+
let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1);
178+
let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty);
179+
debug!("ret_param_ty {}", ret_param_ty.repr(tcx));
180+
181+
let fn_sig = ty::FnSig {
182+
inputs: input_tys,
183+
output: ty::FnConverging(ret_param_ty),
184+
variadic: false
185+
};
186+
debug!("fn_sig {}", fn_sig.repr(tcx));
187+
188+
return Some((fn_sig, kind));
189+
}
190+
}
191+
192+
None
193+
}
194+
195+
fn deduce_unboxed_closure_expectations_from_obligations<'a,'tcx>(
196+
fcx: &FnCtxt<'a,'tcx>,
197+
expected_vid: ty::TyVid)
198+
-> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)>
199+
{
200+
// Here `expected_ty` is known to be a type inference variable.
201+
for obligation in fcx.inh.fulfillment_cx.borrow().pending_trait_obligations().iter() {
202+
let obligation_self_ty = fcx.infcx().shallow_resolve(obligation.self_ty());
203+
match obligation_self_ty.sty {
204+
ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { }
205+
_ => { continue; }
206+
}
207+
208+
match deduce_unboxed_closure_expectations_from_trait_ref(fcx, &*obligation.trait_ref) {
209+
Some(e) => { return Some(e); }
210+
None => { }
211+
}
212+
}
213+
214+
None
215+
}
216+
217+
101218
pub fn check_expr_fn<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
102219
expr: &ast::Expr,
103220
store: ty::TraitStore,

src/librustc/middle/typeck/check/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ type parameter).
7878

7979
pub use self::LvaluePreference::*;
8080
pub use self::DerefArgs::*;
81-
use self::Expectation::*;
81+
pub use self::Expectation::*;
8282
use self::IsBinopAssignment::*;
8383
use self::TupleArgumentsFlag::*;
8484

@@ -97,7 +97,7 @@ use middle::ty::{FnSig, VariantInfo};
9797
use middle::ty::{Polytype};
9898
use middle::ty::{Disr, ParamTy, ParameterEnvironment};
9999
use middle::ty::{mod, Ty};
100-
use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions};
100+
use middle::ty::liberate_late_bound_regions;
101101
use middle::ty_fold::TypeFolder;
102102
use middle::typeck::astconv::AstConv;
103103
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>,
41654165
expr,
41664166
kind,
41674167
&**decl,
4168-
&**body);
4168+
&**body,
4169+
expected);
41694170
}
41704171
ast::ExprProc(ref decl, ref body) => {
41714172
closure::check_expr_fn(fcx,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(unboxed_closures)]
12+
13+
fn with_int(f: &mut FnMut(&int)) {
14+
}
15+
16+
fn main() {
17+
let mut x: Option<&int> = None;
18+
with_int(&mut |&mut: y| x = Some(y)); //~ ERROR cannot infer
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// That a closure whose expected argument types include two distinct
12+
// bound regions.
13+
14+
#![feature(unboxed_closures)]
15+
16+
use std::cell::Cell;
17+
18+
fn doit<T,F>(val: T, f: &F)
19+
where F : Fn(&Cell<&T>, &T)
20+
{
21+
let x = Cell::new(&val);
22+
f.call((&x,&val))
23+
}
24+
25+
pub fn main() {
26+
doit(0i, &|&: x, y| {
27+
x.set(y); //~ ERROR cannot infer
28+
});
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer that the type of `x` is `int` based
12+
// on the expected type from the object.
13+
14+
#![feature(unboxed_closures)]
15+
16+
fn doit<T,F>(val: T, f: &F)
17+
where F : Fn(T)
18+
{
19+
f.call((val,))
20+
}
21+
22+
pub fn main() {
23+
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer that the type of `x` is `int` based
12+
// on the expected type from the object.
13+
14+
#![feature(unboxed_closures)]
15+
16+
fn doit<T>(val: T, f: &Fn(T)) { f.call((val,)) }
17+
18+
pub fn main() {
19+
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we are able to infer that the type of `x` is `int` based
12+
// on the expected type from the object.
13+
14+
#![feature(unboxed_closures)]
15+
16+
fn doit<T,F>(val: T, f: &F)
17+
where F : Fn(&T)
18+
{
19+
f.call((&val,))
20+
}
21+
22+
pub fn main() {
23+
doit(0i, &|&: x /*: int*/ | { x.to_int(); });
24+
}

0 commit comments

Comments
 (0)