Skip to content

Commit

Permalink
Make call notation use autoderef. Fixes rust-lang#18742.
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Jan 3, 2015
1 parent 40746f7 commit 15f7cbf
Show file tree
Hide file tree
Showing 9 changed files with 268 additions and 135 deletions.
2 changes: 1 addition & 1 deletion src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
let method_call = ty::MethodCall::expr(call_expr.id);
let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().get(&method_call) {
Some(method) => method.ty,
None => ty::expr_ty(self.tcx, func_or_rcvr)
None => ty::expr_ty_adjusted(self.tcx, func_or_rcvr)
});

let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {

ast::ExprCall(ref f, ref args) => {
let diverges = !self.ir.tcx.is_method_call(expr.id) && {
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
let t_ret = ty::ty_fn_ret(ty::expr_ty_adjusted(self.ir.tcx, &**f));
t_ret == ty::FnDiverging
};
let succ = if diverges {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ pub fn trans_call<'a, 'blk, 'tcx>(in_cx: Block<'blk, 'tcx>,
let _icx = push_ctxt("trans_call");
trans_call_inner(in_cx,
Some(common::expr_info(call_ex)),
expr_ty(in_cx, f),
expr_ty_adjusted(in_cx, f),
|cx, _| trans(cx, f),
args,
Some(dest)).bcx
Expand Down
179 changes: 179 additions & 0 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,25 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::autoderef;
use super::AutorefArgs;
use super::check_argument_types;
use super::check_expr;
use super::check_method_argument_types;
use super::err_args;
use super::FnCtxt;
use super::LvaluePreference;
use super::method;
use super::structurally_resolved_type;
use super::TupleArgumentsFlag;
use super::write_call;

use middle::infer;
use middle::ty::{mod, Ty};
use syntax::ast;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ptr::P;
use CrateCtxt;

/// Check that it is legal to call methods of the trait corresponding
Expand Down Expand Up @@ -44,3 +61,165 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
"add `#![feature(unboxed_closures)]` to the crate attributes to enable");
}
}

pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
arg_exprs: &[P<ast::Expr>])
{
check_expr(fcx, callee_expr);
let original_callee_ty = fcx.expr_ty(callee_expr);
let (callee_ty, _, result) =
autoderef(fcx,
callee_expr.span,
original_callee_ty,
Some(callee_expr.id),
LvaluePreference::NoPreference,
|adj_ty, idx| {
let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
try_overloaded_call_step(fcx, call_expr, callee_expr,
adj_ty, autoderefref)
});

match result {
None => {
// this will report an error since original_callee_ty is not a fn
confirm_builtin_call(fcx, call_expr, original_callee_ty, arg_exprs);
}

Some(CallStep::Builtin) => {
confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs);
}

Some(CallStep::Overloaded(method_callee)) => {
confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee);
}
}
}

enum CallStep<'tcx> {
Builtin,
Overloaded(ty::MethodCallee<'tcx>)
}

fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
adjusted_ty: Ty<'tcx>,
autoderefref: ty::AutoDerefRef<'tcx>)
-> Option<CallStep<'tcx>>
{
// If the callee is a bare function or a closure, then we're all set.
match structurally_resolved_type(fcx, callee_expr.span, adjusted_ty).sty {
ty::ty_bare_fn(..) | ty::ty_closure(_) => {
fcx.write_adjustment(callee_expr.id,
callee_expr.span,
ty::AdjustDerefRef(autoderefref));
return Some(CallStep::Builtin);
}

_ => {}
}

// Try the options that are least restrictive on the caller first.
for &(opt_trait_def_id, method_name) in [
(fcx.tcx().lang_items.fn_trait(), token::intern("call")),
(fcx.tcx().lang_items.fn_mut_trait(), token::intern("call_mut")),
(fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")),
].iter() {
let trait_def_id = match opt_trait_def_id {
Some(def_id) => def_id,
None => continue,
};

match method::lookup_in_trait_adjusted(fcx,
call_expr.span,
Some(&*callee_expr),
method_name,
trait_def_id,
autoderefref.clone(),
adjusted_ty,
None) {
None => continue,
Some(method_callee) => {
return Some(CallStep::Overloaded(method_callee));
}
}
}

None
}

fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
call_expr: &ast::Expr,
callee_ty: Ty<'tcx>,
arg_exprs: &[P<ast::Expr>])
{
let error_fn_sig;

let fn_sig = match callee_ty.sty {
ty::ty_bare_fn(_, &ty::BareFnTy {ref sig, ..}) |
ty::ty_closure(box ty::ClosureTy {ref sig, ..}) => {
sig
}
_ => {
fcx.type_error_message(call_expr.span, |actual| {
format!("expected function, found `{}`", actual)
}, callee_ty, None);

// This is the "default" function signature, used in case of error.
// In that case, we check each argument against "error" in order to
// set up all the node type bindings.
error_fn_sig = ty::Binder(ty::FnSig {
inputs: err_args(fcx.tcx(), arg_exprs.len()),
output: ty::FnConverging(fcx.tcx().types.err),
variadic: false
});

&error_fn_sig
}
};

// Replace any late-bound regions that appear in the function
// signature with region variables. We also have to
// renormalize the associated types at this point, since they
// previously appeared within a `Binder<>` and hence would not
// have been normalized before.
let fn_sig =
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
infer::FnCall,
fn_sig).0;
let fn_sig =
fcx.normalize_associated_types_in(call_expr.span, &fn_sig);

// Call the generic checker.
let arg_exprs: Vec<_> = arg_exprs.iter().collect(); // for some weird reason we take &[&P<...>].
check_argument_types(fcx,
call_expr.span,
fn_sig.inputs[],
arg_exprs.as_slice(),
AutorefArgs::No,
fn_sig.variadic,
TupleArgumentsFlag::DontTupleArguments);

write_call(fcx, call_expr, fn_sig.output);
}

fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
arg_exprs: &[P<ast::Expr>],
method_callee: ty::MethodCallee<'tcx>)
{
let arg_exprs: Vec<_> = arg_exprs.iter().collect(); // for some weird reason we take &[&P<...>].
let output_type = check_method_argument_types(fcx,
call_expr.span,
method_callee.ty,
call_expr,
arg_exprs.as_slice(),
AutorefArgs::No,
TupleArgumentsFlag::TupleArguments);
let method_call = ty::MethodCall::expr(call_expr.id);
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
write_call(fcx, call_expr, output_type);
}

Loading

0 comments on commit 15f7cbf

Please sign in to comment.