]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/callee.rs
rollup merge of #20594: nikomatsakis/orphan-ordered
[rust.git] / src / librustc_typeck / check / callee.rs
index ee93c896433a18bf95a8621d4e2a05aef066a84d..b1dc033b567bbf659f58053bb30d68b553c33efe 100644 (file)
@@ -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::{self, 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
@@ -44,3 +61,164 @@ 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(..) => {
+            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, ..}) => {
+            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);
+}
+