From dc97247d112dc77bd709417d6bc870bbcfde9b54 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 2 Jan 2015 15:30:26 -0500 Subject: [PATCH] Make call notation use autoderef. Fixes #18742. --- src/librustc/middle/cfg/construct.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc_trans/trans/callee.rs | 2 +- src/librustc_typeck/check/callee.rs | 179 ++++++++++++++++++ src/librustc_typeck/check/mod.rs | 135 +------------ src/test/compile-fail/issue-19692.rs | 2 +- .../unboxed-closures-call-fn-autoderef.rs | 28 +++ .../unboxed-closures-call-sugar-autoderef.rs | 26 +++ ...ed-closures-call-sugar-object-autoderef.rs | 27 +++ 9 files changed, 268 insertions(+), 135 deletions(-) create mode 100644 src/test/run-pass/unboxed-closures-call-fn-autoderef.rs create mode 100644 src/test/run-pass/unboxed-closures-call-sugar-autoderef.rs create mode 100644 src/test/run-pass/unboxed-closures-call-sugar-object-autoderef.rs diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 92aa70548c8..0063b2a514e 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -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); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 0c3438abb2b..64d0facf951 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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 { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 169e52bcfe5..aeaf215a89b 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -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 diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index ee93c896433..153c6463fbe 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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]) +{ + 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> +{ + // 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]) +{ + 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], + 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); +} + diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19ec85dc61e..d0a48558f48 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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, 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]) - -> 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, @@ -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], 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]) { - // 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_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, 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); diff --git a/src/test/compile-fail/issue-19692.rs b/src/test/compile-fail/issue-19692.rs index 4069ea6b997..7794c34a04b 100644 --- a/src/test/compile-fail/issue-19692.rs +++ b/src/test/compile-fail/issue-19692.rs @@ -12,7 +12,7 @@ struct Homura; 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 index 00000000000..0303954ce2a --- /dev/null +++ b/src/test/run-pass/unboxed-closures-call-fn-autoderef.rs @@ -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 or the MIT license +// , 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 index 00000000000..305f496e668 --- /dev/null +++ b/src/test/run-pass/unboxed-closures-call-sugar-autoderef.rs @@ -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 or the MIT license +// , 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: &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 index 00000000000..8909f4e261f --- /dev/null +++ b/src/test/run-pass/unboxed-closures-call-sugar-object-autoderef.rs @@ -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 or the MIT license +// , 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) -> Boxint + '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); +} +