]> git.lizzy.rs Git - rust.git/commitdiff
Make call notation use autoderef. Fixes #18742.
authorNiko Matsakis <niko@alum.mit.edu>
Fri, 2 Jan 2015 20:30:26 +0000 (15:30 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Sat, 3 Jan 2015 01:59:11 +0000 (20:59 -0500)
src/librustc/middle/cfg/construct.rs
src/librustc/middle/liveness.rs
src/librustc_trans/trans/callee.rs
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/mod.rs
src/test/compile-fail/issue-19692.rs
src/test/run-pass/unboxed-closures-call-fn-autoderef.rs [new file with mode: 0644]
src/test/run-pass/unboxed-closures-call-sugar-autoderef.rs [new file with mode: 0644]
src/test/run-pass/unboxed-closures-call-sugar-object-autoderef.rs [new file with mode: 0644]

index 92aa70548c82bdd4d4dc3e5f8838d416fb88de4b..0063b2a514e02a1484a804cdc73064a53845584d 100644 (file)
@@ -509,7 +509,7 @@ fn call<'b, I: Iterator<&'b ast::Expr>>(&mut self,
         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);
index 0c3438abb2b479873048863ad2a15a506da6f311..64d0facf951005218d972ec38e5aea3f2b10cbb5 100644 (file)
@@ -1149,7 +1149,7 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
 
           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 {
index 169e52bcfe5bef7e280477cb832d16a1f15b6ba3..aeaf215a89b2ae0e9f664cb9c33c26531397f51a 100644 (file)
@@ -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
index ee93c896433a18bf95a8621d4e2a05aef066a84d..153c6463fbebb84c0601638e672c3c08f0dbb01d 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::{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
@@ -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);
+}
+
index 19ec85dc61eaec9123482fc8a67dbe537b5e1068..d0a48558f4816a72c1adb042e9889c45ef6f6d09 100644 (file)
@@ -2210,7 +2210,8 @@ pub enum LvaluePreference {
 ///
 /// Note: this method does not modify the adjustments table. The caller is responsible for
 /// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
-pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
+pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
+                                 sp: Span,
                                  base_ty: Ty<'tcx>,
                                  expr_id: Option<ast::NodeId>,
                                  mut lvalue_pref: LvaluePreference,
@@ -2257,58 +2258,6 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
     (fcx.tcx().types.err, 0, None)
 }
 
-/// Attempts to resolve a call expression as an overloaded call.
-fn try_overloaded_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 call_expression: &ast::Expr,
-                                 callee: &ast::Expr,
-                                 callee_type: Ty<'tcx>,
-                                 args: &[&P<ast::Expr>])
-                                 -> bool {
-    // Bail out if the callee is a bare function or a closure. We check those
-    // manually.
-    match structurally_resolved_type(fcx, callee.span, callee_type).sty {
-        ty::ty_bare_fn(..) | ty::ty_closure(_) => return false,
-        _ => {}
-    }
-
-    // Try the options that are least restrictive on the caller first.
-    for &(maybe_function_trait, 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 function_trait = match maybe_function_trait {
-            None => continue,
-            Some(function_trait) => function_trait,
-        };
-        let method_callee =
-            match method::lookup_in_trait(fcx,
-                                          call_expression.span,
-                                          Some(&*callee),
-                                          method_name,
-                                          function_trait,
-                                          callee_type,
-                                          None) {
-                None => continue,
-                Some(method_callee) => method_callee,
-            };
-        let method_call = MethodCall::expr(call_expression.id);
-        let output_type = check_method_argument_types(fcx,
-                                                      call_expression.span,
-                                                      method_callee.ty,
-                                                      call_expression,
-                                                      args,
-                                                      AutorefArgs::No,
-                                                      TupleArguments);
-        fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
-        write_call(fcx, call_expression, output_type);
-
-        return true
-    }
-
-    false
-}
-
 fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   span: Span,
                                   method_call: Option<MethodCall>,
@@ -2671,7 +2620,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         check_argument_types(fcx,
                              sp,
                              err_inputs[],
-                             callee_expr,
                              args_no_rcvr,
                              autoref_args,
                              false,
@@ -2684,7 +2632,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 check_argument_types(fcx,
                                      sp,
                                      fty.sig.0.inputs.slice_from(1),
-                                     callee_expr,
                                      args_no_rcvr,
                                      autoref_args,
                                      fty.sig.0.variadic,
@@ -2704,7 +2651,6 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   sp: Span,
                                   fn_inputs: &[Ty<'tcx>],
-                                  _callee_expr: &ast::Expr,
                                   args: &[&P<ast::Expr>],
                                   autoref_args: AutorefArgs,
                                   variadic: bool,
@@ -3088,63 +3034,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     debug!(">> typechecking: expr={} expected={}",
            expr.repr(fcx.tcx()), expected.repr(fcx.tcx()));
 
-    // A generic function for doing all of the checking for call expressions
-    fn check_call(fcx: &FnCtxt,
-                  call_expr: &ast::Expr,
-                  f: &ast::Expr,
-                  args: &[&P<ast::Expr>]) {
-        // Store the type of `f` as the type of the callee
-        let fn_ty = fcx.expr_ty(f);
-
-        // Extract the function signature from `in_fty`.
-        let fn_ty = structurally_resolved_type(fcx, f.span, fn_ty);
-
-        // 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.
-        let error_fn_sig = ty::Binder(FnSig {
-            inputs: err_args(fcx.tcx(), args.len()),
-            output: ty::FnConverging(fcx.tcx().types.err),
-            variadic: false
-        });
-
-        let fn_sig = match fn_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)
-                }, fn_ty, None);
-                &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.
-        check_argument_types(fcx,
-                             call_expr.span,
-                             fn_sig.inputs[],
-                             f,
-                             args,
-                             AutorefArgs::No,
-                             fn_sig.variadic,
-                             DontTupleArguments);
-
-        write_call(fcx, call_expr, fn_sig.output);
-    }
-
     // Checks a method call.
     fn check_method_call(fcx: &FnCtxt,
                          expr: &ast::Expr,
@@ -4146,24 +4035,8 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
         check_block_with_expected(fcx, &**b, expected);
         fcx.write_ty(id, fcx.node_ty(b.id));
       }
-      ast::ExprCall(ref f, ref args) => {
-          // Index expressions need to be handled separately, to inform them
-          // that they appear in call position.
-          check_expr(fcx, &**f);
-          let f_ty = fcx.expr_ty(&**f);
-
-          let args: Vec<_> = args.iter().map(|x| x).collect();
-          if !try_overloaded_call(fcx, expr, &**f, f_ty, args[]) {
-              check_call(fcx, expr, &**f, args[]);
-              let args_err = args.iter().fold(false,
-                 |rest_err, a| {
-                     // is this not working?
-                     let a_ty = fcx.expr_ty(&***a);
-                     rest_err || ty::type_is_error(a_ty)});
-              if ty::type_is_error(f_ty) || args_err {
-                  fcx.write_error(id);
-              }
-          }
+      ast::ExprCall(ref callee, ref args) => {
+          callee::check_call(fcx, expr, &**callee, args.as_slice());
       }
       ast::ExprMethodCall(ident, ref tps, ref args) => {
         check_method_call(fcx, expr, ident, args[], tps[], lvalue_pref);
index 4069ea6b997c4cbc93d41dcaba35653165ee0687..7794c34a04b81bf388e23aa80ea457d3bd0e03e8 100644 (file)
@@ -12,7 +12,7 @@
 
 fn akemi(homura: Homura) {
     let Some(ref madoka) = Some(homura.kaname()); //~ ERROR does not implement any method
-    madoka.clone(); //~ ERROR the type of this value must be known
+    madoka.clone();
 }
 
 fn main() { }
diff --git a/src/test/run-pass/unboxed-closures-call-fn-autoderef.rs b/src/test/run-pass/unboxed-closures-call-fn-autoderef.rs
new file mode 100644 (file)
index 0000000..0303954
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the call operator autoderefs when calling a bounded type parameter.
+
+#![feature(unboxed_closures)]
+
+use std::ops::FnMut;
+
+fn call_with_2(x: &fn(int) -> int) -> int
+{
+    x(2) // look ma, no `*`
+}
+
+fn subtract_22(x: int) -> int { x - 22 }
+
+pub fn main() {
+    let subtract_22: fn(int) -> int = subtract_22;
+    let z = call_with_2(&subtract_22);
+    assert_eq!(z, -20);
+}
diff --git a/src/test/run-pass/unboxed-closures-call-sugar-autoderef.rs b/src/test/run-pass/unboxed-closures-call-sugar-autoderef.rs
new file mode 100644 (file)
index 0000000..305f496
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the call operator autoderefs when calling a bounded type parameter.
+
+#![feature(unboxed_closures)]
+
+use std::ops::FnMut;
+
+fn call_with_2<F>(x: &mut F) -> int
+    where F : FnMut(int) -> int
+{
+    x(2) // look ma, no `*`
+}
+
+pub fn main() {
+    let z = call_with_2(&mut |x| x - 22);
+    assert_eq!(z, -20);
+}
diff --git a/src/test/run-pass/unboxed-closures-call-sugar-object-autoderef.rs b/src/test/run-pass/unboxed-closures-call-sugar-object-autoderef.rs
new file mode 100644 (file)
index 0000000..8909f4e
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the call operator autoderefs when calling to an object type.
+
+#![feature(unboxed_closures)]
+
+use std::ops::FnMut;
+
+fn make_adder(x: int) -> Box<FnMut(int)->int + 'static> {
+    box move |y| { x + y }
+}
+
+pub fn main() {
+    let mut adder = make_adder(3);
+    let z = adder(2);
+    println!("{}", z);
+    assert_eq!(z, 5);
+}
+