Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 260e461

Browse files
committedJan 4, 2015
auto merge of #20443 : nikomatsakis/rust/autoderef-overloaded-calls, r=pcwalton
Use autoderef for call notation. This is consistent in that we now autoderef all postfix operators (`.`, `[]`, and `()`). It also means you can call closures without writing `(*f)()`. Note that this is rebased atop the rollup, so only the final commit is relevant. r? @pcwalton
2 parents 5e21e17 + dc97247 commit 260e461

File tree

9 files changed

+268
-135
lines changed

9 files changed

+268
-135
lines changed
 

‎src/librustc/middle/cfg/construct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
509509
let method_call = ty::MethodCall::expr(call_expr.id);
510510
let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().get(&method_call) {
511511
Some(method) => method.ty,
512-
None => ty::expr_ty(self.tcx, func_or_rcvr)
512+
None => ty::expr_ty_adjusted(self.tcx, func_or_rcvr)
513513
});
514514

515515
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);

‎src/librustc/middle/liveness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
11491149

11501150
ast::ExprCall(ref f, ref args) => {
11511151
let diverges = !self.ir.tcx.is_method_call(expr.id) && {
1152-
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
1152+
let t_ret = ty::ty_fn_ret(ty::expr_ty_adjusted(self.ir.tcx, &**f));
11531153
t_ret == ty::FnDiverging
11541154
};
11551155
let succ = if diverges {

‎src/librustc_trans/trans/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ pub fn trans_call<'a, 'blk, 'tcx>(in_cx: Block<'blk, 'tcx>,
576576
let _icx = push_ctxt("trans_call");
577577
trans_call_inner(in_cx,
578578
Some(common::expr_info(call_ex)),
579-
expr_ty(in_cx, f),
579+
expr_ty_adjusted(in_cx, f),
580580
|cx, _| trans(cx, f),
581581
args,
582582
Some(dest)).bcx

‎src/librustc_typeck/check/callee.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,25 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use super::autoderef;
12+
use super::AutorefArgs;
13+
use super::check_argument_types;
14+
use super::check_expr;
15+
use super::check_method_argument_types;
16+
use super::err_args;
17+
use super::FnCtxt;
18+
use super::LvaluePreference;
19+
use super::method;
20+
use super::structurally_resolved_type;
21+
use super::TupleArgumentsFlag;
22+
use super::write_call;
23+
24+
use middle::infer;
25+
use middle::ty::{mod, Ty};
1126
use syntax::ast;
1227
use syntax::codemap::Span;
28+
use syntax::parse::token;
29+
use syntax::ptr::P;
1330
use CrateCtxt;
1431

1532
/// Check that it is legal to call methods of the trait corresponding
@@ -44,3 +61,165 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
4461
"add `#![feature(unboxed_closures)]` to the crate attributes to enable");
4562
}
4663
}
64+
65+
pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
66+
call_expr: &ast::Expr,
67+
callee_expr: &ast::Expr,
68+
arg_exprs: &[P<ast::Expr>])
69+
{
70+
check_expr(fcx, callee_expr);
71+
let original_callee_ty = fcx.expr_ty(callee_expr);
72+
let (callee_ty, _, result) =
73+
autoderef(fcx,
74+
callee_expr.span,
75+
original_callee_ty,
76+
Some(callee_expr.id),
77+
LvaluePreference::NoPreference,
78+
|adj_ty, idx| {
79+
let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
80+
try_overloaded_call_step(fcx, call_expr, callee_expr,
81+
adj_ty, autoderefref)
82+
});
83+
84+
match result {
85+
None => {
86+
// this will report an error since original_callee_ty is not a fn
87+
confirm_builtin_call(fcx, call_expr, original_callee_ty, arg_exprs);
88+
}
89+
90+
Some(CallStep::Builtin) => {
91+
confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs);
92+
}
93+
94+
Some(CallStep::Overloaded(method_callee)) => {
95+
confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee);
96+
}
97+
}
98+
}
99+
100+
enum CallStep<'tcx> {
101+
Builtin,
102+
Overloaded(ty::MethodCallee<'tcx>)
103+
}
104+
105+
fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
106+
call_expr: &ast::Expr,
107+
callee_expr: &ast::Expr,
108+
adjusted_ty: Ty<'tcx>,
109+
autoderefref: ty::AutoDerefRef<'tcx>)
110+
-> Option<CallStep<'tcx>>
111+
{
112+
// If the callee is a bare function or a closure, then we're all set.
113+
match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty {
114+
ty::ty_bare_fn(..) | ty::ty_closure(_) => {
115+
fcx.write_adjustment(callee_expr.id,
116+
callee_expr.span,
117+
ty::AdjustDerefRef(autoderefref));
118+
return Some(CallStep::Builtin);
119+
}
120+
121+
_ => {}
122+
}
123+
124+
// Try the options that are least restrictive on the caller first.
125+
for &(opt_trait_def_id, method_name) in [
126+
(fcx.tcx().lang_items.fn_trait(), token::intern("call")),
127+
(fcx.tcx().lang_items.fn_mut_trait(), token::intern("call_mut")),
128+
(fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")),
129+
].iter() {
130+
let trait_def_id = match opt_trait_def_id {
131+
Some(def_id) => def_id,
132+
None => continue,
133+
};
134+
135+
match method::lookup_in_trait_adjusted(fcx,
136+
call_expr.span,
137+
Some(&*callee_expr),
138+
method_name,
139+
trait_def_id,
140+
autoderefref.clone(),
141+
adjusted_ty,
142+
None) {
143+
None => continue,
144+
Some(method_callee) => {
145+
return Some(CallStep::Overloaded(method_callee));
146+
}
147+
}
148+
}
149+
150+
None
151+
}
152+
153+
fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
154+
call_expr: &ast::Expr,
155+
callee_ty: Ty<'tcx>,
156+
arg_exprs: &[P<ast::Expr>])
157+
{
158+
let error_fn_sig;
159+
160+
let fn_sig = match callee_ty.sty {
161+
ty::ty_bare_fn(_, &ty::BareFnTy {ref sig, ..}) |
162+
ty::ty_closure(box ty::ClosureTy {ref sig, ..}) => {
163+
sig
164+
}
165+
_ => {
166+
fcx.type_error_message(call_expr.span, |actual| {
167+
format!("expected function, found `{}`", actual)
168+
}, callee_ty, None);
169+
170+
// This is the "default" function signature, used in case of error.
171+
// In that case, we check each argument against "error" in order to
172+
// set up all the node type bindings.
173+
error_fn_sig = ty::Binder(ty::FnSig {
174+
inputs: err_args(fcx.tcx(), arg_exprs.len()),
175+
output: ty::FnConverging(fcx.tcx().types.err),
176+
variadic: false
177+
});
178+
179+
&error_fn_sig
180+
}
181+
};
182+
183+
// Replace any late-bound regions that appear in the function
184+
// signature with region variables. We also have to
185+
// renormalize the associated types at this point, since they
186+
// previously appeared within a `Binder<>` and hence would not
187+
// have been normalized before.
188+
let fn_sig =
189+
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
190+
infer::FnCall,
191+
fn_sig).0;
192+
let fn_sig =
193+
fcx.normalize_associated_types_in(call_expr.span, &fn_sig);
194+
195+
// Call the generic checker.
196+
let arg_exprs: Vec<_> = arg_exprs.iter().collect(); // for some weird reason we take &[&P<...>].
197+
check_argument_types(fcx,
198+
call_expr.span,
199+
fn_sig.inputs[],
200+
arg_exprs.as_slice(),
201+
AutorefArgs::No,
202+
fn_sig.variadic,
203+
TupleArgumentsFlag::DontTupleArguments);
204+
205+
write_call(fcx, call_expr, fn_sig.output);
206+
}
207+
208+
fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
209+
call_expr: &ast::Expr,
210+
arg_exprs: &[P<ast::Expr>],
211+
method_callee: ty::MethodCallee<'tcx>)
212+
{
213+
let arg_exprs: Vec<_> = arg_exprs.iter().collect(); // for some weird reason we take &[&P<...>].
214+
let output_type = check_method_argument_types(fcx,
215+
call_expr.span,
216+
method_callee.ty,
217+
call_expr,
218+
arg_exprs.as_slice(),
219+
AutorefArgs::No,
220+
TupleArgumentsFlag::TupleArguments);
221+
let method_call = ty::MethodCall::expr(call_expr.id);
222+
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
223+
write_call(fcx, call_expr, output_type);
224+
}
225+

‎src/librustc_typeck/check/mod.rs

Lines changed: 4 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,7 +2229,8 @@ pub enum LvaluePreference {
22292229
///
22302230
/// Note: this method does not modify the adjustments table. The caller is responsible for
22312231
/// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
2232-
pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
2232+
pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
2233+
sp: Span,
22332234
base_ty: Ty<'tcx>,
22342235
expr_id: Option<ast::NodeId>,
22352236
mut lvalue_pref: LvaluePreference,
@@ -2276,58 +2277,6 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
22762277
(fcx.tcx().types.err, 0, None)
22772278
}
22782279

2279-
/// Attempts to resolve a call expression as an overloaded call.
2280-
fn try_overloaded_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
2281-
call_expression: &ast::Expr,
2282-
callee: &ast::Expr,
2283-
callee_type: Ty<'tcx>,
2284-
args: &[&P<ast::Expr>])
2285-
-> bool {
2286-
// Bail out if the callee is a bare function or a closure. We check those
2287-
// manually.
2288-
match structurally_resolved_type(fcx, callee.span, callee_type).sty {
2289-
ty::ty_bare_fn(..) | ty::ty_closure(_) => return false,
2290-
_ => {}
2291-
}
2292-
2293-
// Try the options that are least restrictive on the caller first.
2294-
for &(maybe_function_trait, method_name) in [
2295-
(fcx.tcx().lang_items.fn_trait(), token::intern("call")),
2296-
(fcx.tcx().lang_items.fn_mut_trait(), token::intern("call_mut")),
2297-
(fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")),
2298-
].iter() {
2299-
let function_trait = match maybe_function_trait {
2300-
None => continue,
2301-
Some(function_trait) => function_trait,
2302-
};
2303-
let method_callee =
2304-
match method::lookup_in_trait(fcx,
2305-
call_expression.span,
2306-
Some(&*callee),
2307-
method_name,
2308-
function_trait,
2309-
callee_type,
2310-
None) {
2311-
None => continue,
2312-
Some(method_callee) => method_callee,
2313-
};
2314-
let method_call = MethodCall::expr(call_expression.id);
2315-
let output_type = check_method_argument_types(fcx,
2316-
call_expression.span,
2317-
method_callee.ty,
2318-
call_expression,
2319-
args,
2320-
AutorefArgs::No,
2321-
TupleArguments);
2322-
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
2323-
write_call(fcx, call_expression, output_type);
2324-
2325-
return true
2326-
}
2327-
2328-
false
2329-
}
2330-
23312280
fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
23322281
span: Span,
23332282
method_call: Option<MethodCall>,
@@ -2689,7 +2638,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
26892638
check_argument_types(fcx,
26902639
sp,
26912640
err_inputs[],
2692-
callee_expr,
26932641
args_no_rcvr,
26942642
autoref_args,
26952643
false,
@@ -2702,7 +2650,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
27022650
check_argument_types(fcx,
27032651
sp,
27042652
fty.sig.0.inputs.slice_from(1),
2705-
callee_expr,
27062653
args_no_rcvr,
27072654
autoref_args,
27082655
fty.sig.0.variadic,
@@ -2722,7 +2669,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
27222669
fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
27232670
sp: Span,
27242671
fn_inputs: &[Ty<'tcx>],
2725-
_callee_expr: &ast::Expr,
27262672
args: &[&P<ast::Expr>],
27272673
autoref_args: AutorefArgs,
27282674
variadic: bool,
@@ -3106,63 +3052,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
31063052
debug!(">> typechecking: expr={} expected={}",
31073053
expr.repr(fcx.tcx()), expected.repr(fcx.tcx()));
31083054

3109-
// A generic function for doing all of the checking for call expressions
3110-
fn check_call(fcx: &FnCtxt,
3111-
call_expr: &ast::Expr,
3112-
f: &ast::Expr,
3113-
args: &[&P<ast::Expr>]) {
3114-
// Store the type of `f` as the type of the callee
3115-
let fn_ty = fcx.expr_ty(f);
3116-
3117-
// Extract the function signature from `in_fty`.
3118-
let fn_ty = structurally_resolved_type(fcx, f.span, fn_ty);
3119-
3120-
// This is the "default" function signature, used in case of error.
3121-
// In that case, we check each argument against "error" in order to
3122-
// set up all the node type bindings.
3123-
let error_fn_sig = ty::Binder(FnSig {
3124-
inputs: err_args(fcx.tcx(), args.len()),
3125-
output: ty::FnConverging(fcx.tcx().types.err),
3126-
variadic: false
3127-
});
3128-
3129-
let fn_sig = match fn_ty.sty {
3130-
ty::ty_bare_fn(_, &ty::BareFnTy {ref sig, ..}) |
3131-
ty::ty_closure(box ty::ClosureTy {ref sig, ..}) => sig,
3132-
_ => {
3133-
fcx.type_error_message(call_expr.span, |actual| {
3134-
format!("expected function, found `{}`", actual)
3135-
}, fn_ty, None);
3136-
&error_fn_sig
3137-
}
3138-
};
3139-
3140-
// Replace any late-bound regions that appear in the function
3141-
// signature with region variables. We also have to
3142-
// renormalize the associated types at this point, since they
3143-
// previously appeared within a `Binder<>` and hence would not
3144-
// have been normalized before.
3145-
let fn_sig =
3146-
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
3147-
infer::FnCall,
3148-
fn_sig).0;
3149-
let fn_sig =
3150-
fcx.normalize_associated_types_in(call_expr.span,
3151-
&fn_sig);
3152-
3153-
// Call the generic checker.
3154-
check_argument_types(fcx,
3155-
call_expr.span,
3156-
fn_sig.inputs[],
3157-
f,
3158-
args,
3159-
AutorefArgs::No,
3160-
fn_sig.variadic,
3161-
DontTupleArguments);
3162-
3163-
write_call(fcx, call_expr, fn_sig.output);
3164-
}
3165-
31663055
// Checks a method call.
31673056
fn check_method_call(fcx: &FnCtxt,
31683057
expr: &ast::Expr,
@@ -4164,24 +4053,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
41644053
check_block_with_expected(fcx, &**b, expected);
41654054
fcx.write_ty(id, fcx.node_ty(b.id));
41664055
}
4167-
ast::ExprCall(ref f, ref args) => {
4168-
// Index expressions need to be handled separately, to inform them
4169-
// that they appear in call position.
4170-
check_expr(fcx, &**f);
4171-
let f_ty = fcx.expr_ty(&**f);
4172-
4173-
let args: Vec<_> = args.iter().map(|x| x).collect();
4174-
if !try_overloaded_call(fcx, expr, &**f, f_ty, args[]) {
4175-
check_call(fcx, expr, &**f, args[]);
4176-
let args_err = args.iter().fold(false,
4177-
|rest_err, a| {
4178-
// is this not working?
4179-
let a_ty = fcx.expr_ty(&***a);
4180-
rest_err || ty::type_is_error(a_ty)});
4181-
if ty::type_is_error(f_ty) || args_err {
4182-
fcx.write_error(id);
4183-
}
4184-
}
4056+
ast::ExprCall(ref callee, ref args) => {
4057+
callee::check_call(fcx, expr, &**callee, args.as_slice());
41854058
}
41864059
ast::ExprMethodCall(ident, ref tps, ref args) => {
41874060
check_method_call(fcx, expr, ident, args[], tps[], lvalue_pref);

‎src/test/compile-fail/issue-19692.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ struct Homura;
1212

1313
fn akemi(homura: Homura) {
1414
let Some(ref madoka) = Some(homura.kaname()); //~ ERROR does not implement any method
15-
madoka.clone(); //~ ERROR the type of this value must be known
15+
madoka.clone();
1616
}
1717

1818
fn main() { }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
// Test that the call operator autoderefs when calling a bounded type parameter.
12+
13+
#![feature(unboxed_closures)]
14+
15+
use std::ops::FnMut;
16+
17+
fn call_with_2(x: &fn(int) -> int) -> int
18+
{
19+
x(2) // look ma, no `*`
20+
}
21+
22+
fn subtract_22(x: int) -> int { x - 22 }
23+
24+
pub fn main() {
25+
let subtract_22: fn(int) -> int = subtract_22;
26+
let z = call_with_2(&subtract_22);
27+
assert_eq!(z, -20);
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
// Test that the call operator autoderefs when calling a bounded type parameter.
12+
13+
#![feature(unboxed_closures)]
14+
15+
use std::ops::FnMut;
16+
17+
fn call_with_2<F>(x: &mut F) -> int
18+
where F : FnMut(int) -> int
19+
{
20+
x(2) // look ma, no `*`
21+
}
22+
23+
pub fn main() {
24+
let z = call_with_2(&mut |x| x - 22);
25+
assert_eq!(z, -20);
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
// Test that the call operator autoderefs when calling to an object type.
12+
13+
#![feature(unboxed_closures)]
14+
15+
use std::ops::FnMut;
16+
17+
fn make_adder(x: int) -> Box<FnMut(int)->int + 'static> {
18+
box move |y| { x + y }
19+
}
20+
21+
pub fn main() {
22+
let mut adder = make_adder(3);
23+
let z = adder(2);
24+
println!("{}", z);
25+
assert_eq!(z, 5);
26+
}
27+

0 commit comments

Comments
 (0)
Please sign in to comment.