]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/callee.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / librustc_typeck / check / callee.rs
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 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};
26 use syntax::ast;
27 use syntax::codemap::Span;
28 use syntax::parse::token;
29 use syntax::ptr::P;
30 use CrateCtxt;
31
32 /// Check that it is legal to call methods of the trait corresponding
33 /// to `trait_id` (this only cares about the trait, not the specific
34 /// method that is called)
35 pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id: ast::DefId) {
36     let tcx = ccx.tcx;
37     let did = Some(trait_id);
38     let li = &tcx.lang_items;
39
40     if did == li.drop_trait() {
41         span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
42     } else if !tcx.sess.features.borrow().unboxed_closures {
43         // the #[feature(unboxed_closures)] feature isn't
44         // activated so we need to enforce the closure
45         // restrictions.
46
47         let method = if did == li.fn_trait() {
48             "call"
49         } else if did == li.fn_mut_trait() {
50             "call_mut"
51         } else if did == li.fn_once_trait() {
52             "call_once"
53         } else {
54             return // not a closure method, everything is OK.
55         };
56
57         span_err!(tcx.sess, span, E0174,
58                   "explicit use of unboxed closure method `{}` is experimental",
59                   method);
60         span_help!(tcx.sess, span,
61                    "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
62     }
63 }
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