From 2c76710ecedb459d36ae110882931143a6fd5dd1 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Fri, 2 Jan 2015 03:38:02 +0200 Subject: [PATCH 1/4] rustc_typeck: make Expectation more composable by replacing map and map_to_option with to_option. --- src/librustc_typeck/check/closure.rs | 2 +- src/librustc_typeck/check/mod.rs | 39 +++++++++------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 0079115756993..bfe43086aab10 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -33,7 +33,7 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr.repr(fcx.tcx()), expected.repr(fcx.tcx())); - let expected_sig_and_kind = expected.map_to_option(fcx, |ty| { + let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| { deduce_unboxed_closure_expectations_from_expected_type(fcx, ty) }); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1d184131dede3..5c25b55287af9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2844,7 +2844,7 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, ast::LitInt(_, ast::SignedIntLit(t, _)) => ty::mk_mach_int(tcx, t), ast::LitInt(_, ast::UnsignedIntLit(t)) => ty::mk_mach_uint(tcx, t), ast::LitInt(_, ast::UnsuffixedIntLit(_)) => { - let opt_ty = expected.map_to_option(fcx, |ty| { + let opt_ty = expected.to_option(fcx).and_then(|ty| { match ty.sty { ty::ty_int(_) | ty::ty_uint(_) => Some(ty), ty::ty_char => Some(tcx.types.u8), @@ -2858,7 +2858,7 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } ast::LitFloat(_, t) => ty::mk_mach_float(tcx, t), ast::LitFloatUnsuffixed(_) => { - let opt_ty = expected.map_to_option(fcx, |ty| { + let opt_ty = expected.to_option(fcx).and_then(|ty| { match ty.sty { ty::ty_float(_) => Some(ty), _ => None @@ -3761,7 +3761,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } ast::ExprUnary(unop, ref oprnd) => { - let expected_inner = expected.map(fcx, |ty| { + let expected_inner = expected.to_option(fcx).map_or(NoExpectation, |ty| { match unop { ast::UnUniq => match ty.sty { ty::ty_uniq(ty) => { @@ -3851,8 +3851,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, oprnd_t); } ast::ExprAddrOf(mutbl, ref oprnd) => { - let expected = expected.only_has_type(); - let hint = expected.map(fcx, |ty| { + let hint = expected.only_has_type(fcx).map_or(NoExpectation, |ty| { match ty.sty { ty::ty_rptr(_, ref mt) | ty::ty_ptr(ref mt) => { if ty::expr_is_lval(fcx.tcx(), &**oprnd) { @@ -4065,7 +4064,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_cast(fcx, expr, &**e, &**t); } ast::ExprVec(ref args) => { - let uty = expected.map_to_option(fcx, |uty| { + let uty = expected.to_option(fcx).and_then(|uty| { match uty.sty { ty::ty_vec(ty, _) => Some(ty), _ => None @@ -4134,8 +4133,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } ast::ExprTup(ref elts) => { - let expected = expected.only_has_type(); - let flds = expected.map_to_option(fcx, |ty| { + let flds = expected.only_has_type(fcx).and_then(|ty| { match ty.sty { ty::ty_tup(ref flds) => Some(&flds[]), _ => None @@ -4428,13 +4426,6 @@ impl<'tcx> Expectation<'tcx> { } } - fn only_has_type(self) -> Expectation<'tcx> { - match self { - ExpectHasType(t) => ExpectHasType(t), - _ => NoExpectation - } - } - // Resolves `expected` by a single level if it is a variable. If // there is no expected type or resolution is not possible (e.g., // no constraints yet present), just returns `None`. @@ -4458,25 +4449,19 @@ impl<'tcx> Expectation<'tcx> { } } - fn map<'a, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Expectation<'tcx> where - F: FnOnce(Ty<'tcx>) -> Expectation<'tcx> - { + fn to_option<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { match self.resolve(fcx) { - NoExpectation => NoExpectation, + NoExpectation => None, ExpectCastableToType(ty) | ExpectHasType(ty) | - ExpectRvalueLikeUnsized(ty) => unpack(ty), + ExpectRvalueLikeUnsized(ty) => Some(ty), } } - fn map_to_option<'a, O, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Option where - F: FnOnce(Ty<'tcx>) -> Option, - { + fn only_has_type<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { match self.resolve(fcx) { - NoExpectation => None, - ExpectCastableToType(ty) | - ExpectHasType(ty) | - ExpectRvalueLikeUnsized(ty) => unpack(ty), + ExpectHasType(ty) => Some(ty), + _ => None } } } From 21ec0c85d94b259e6bd4efa19b66b39ab5ed61e4 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 8 Jan 2015 18:26:36 +0200 Subject: [PATCH 2/4] rustc_typeck: don't use double indirection to Expr's in check_argument_types. --- src/librustc_typeck/check/callee.rs | 6 ++--- src/librustc_typeck/check/mod.rs | 38 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 19a88dfc553b6..9d45a5516fa7b 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -192,11 +192,10 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, 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.as_slice(), - arg_exprs.as_slice(), + arg_exprs, AutorefArgs::No, fn_sig.variadic, TupleArgumentsFlag::DontTupleArguments); @@ -209,12 +208,11 @@ fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, 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(), + arg_exprs, AutorefArgs::No, TupleArgumentsFlag::TupleArguments); let method_call = ty::MethodCall::expr(call_expr.id); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5c25b55287af9..61d401a14364e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -112,6 +112,7 @@ use std::cell::{Cell, Ref, RefCell}; use std::mem::replace; use std::rc::Rc; use std::iter::repeat; +use std::slice; use syntax::{self, abi, attr}; use syntax::ast::{self, ProvidedMethod, RequiredMethod, TypeTraitItem, DefId}; use syntax::ast_util::{self, local_def, PostExpansionMethod}; @@ -2598,7 +2599,7 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, method_fn_ty: Ty<'tcx>, callee_expr: &ast::Expr, - args_no_rcvr: &[&P], + args_no_rcvr: &[P], autoref_args: AutorefArgs, tuple_arguments: TupleArgumentsFlag) -> ty::FnOutput<'tcx> { @@ -2624,7 +2625,7 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // HACK(eddyb) ignore self in the definition (see above). check_argument_types(fcx, sp, - fty.sig.0.inputs.slice_from(1), + &fty.sig.0.inputs[1..], args_no_rcvr, autoref_args, fty.sig.0.variadic, @@ -2644,7 +2645,7 @@ 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>], - args: &[&P], + args: &[P], autoref_args: AutorefArgs, variadic: bool, tuple_arguments: TupleArgumentsFlag) { @@ -2767,7 +2768,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, AutorefArgs::No => {} } - check_expr_coercable_to_type(fcx, &***arg, formal_ty); + check_expr_coercable_to_type(fcx, &**arg, formal_ty); } } } @@ -2776,12 +2777,12 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // arguments which we skipped above. if variadic { for arg in args.iter().skip(expected_arg_count) { - check_expr(fcx, &***arg); + check_expr(fcx, &**arg); // There are a few types which get autopromoted when passed via varargs // in C but we just error out instead and require explicit casts. let arg_ty = structurally_resolved_type(fcx, arg.span, - fcx.expr_ty(&***arg)); + fcx.expr_ty(&**arg)); match arg_ty.sty { ty::ty_float(ast::TyF32) => { fcx.type_error_message(arg.span, @@ -3064,12 +3065,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, }; // Call the generic checker. - let args: Vec<_> = args[1..].iter().map(|x| x).collect(); let ret_ty = check_method_argument_types(fcx, method_name.span, fn_ty, expr, - args.as_slice(), + &args[1..], AutorefArgs::No, DontTupleArguments); @@ -3167,8 +3167,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, None => None }; let args = match rhs { - Some(rhs) => vec![rhs], - None => vec![] + Some(rhs) => slice::ref_slice(rhs), + None => &[][] }; match method { Some(method) => { @@ -3177,12 +3177,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let method_call = ::middle::ty::MethodCall::expr(op_ex.id); fcx.inh.method_map.borrow_mut().insert(method_call, method); match check_method_argument_types(fcx, - op_ex.span, - method_ty, - op_ex, - args.as_slice(), - autoref_args, - DontTupleArguments) { + op_ex.span, + method_ty, + op_ex, + args, + autoref_args, + DontTupleArguments) { ty::FnConverging(result_type) => result_type, ty::FnDiverging => fcx.tcx().types.err } @@ -3196,7 +3196,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, op_ex.span, expected_ty, op_ex, - args.as_slice(), + args, autoref_args, DontTupleArguments); fcx.tcx().types.err @@ -4045,10 +4045,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, fcx.node_ty(b.id)); } ast::ExprCall(ref callee, ref args) => { - callee::check_call(fcx, expr, &**callee, args.as_slice()); + callee::check_call(fcx, expr, &**callee, &args[]); } ast::ExprMethodCall(ident, ref tps, ref args) => { - check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice(), lvalue_pref); + check_method_call(fcx, expr, ident, &args[], &tps[], lvalue_pref); let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a)); let args_err = arg_tys.fold(false, |rest_err, a| { From e73fbc69cd85ccd7e444929b7b7aa0ae74ee6198 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 8 Jan 2015 18:25:52 +0200 Subject: [PATCH 3/4] rustc_typeck: unify expected return types with formal return types to propagate coercions through calls of generic functions. --- src/librustc/middle/infer/mod.rs | 33 ++++++ src/librustc_typeck/check/callee.rs | 26 +++-- src/librustc_typeck/check/mod.rs | 115 ++++++++++++++++++--- src/test/run-pass/coerce-expect-unsized.rs | 3 + src/test/run-pass/coerce-unify-return.rs | 26 +++++ 5 files changed, 180 insertions(+), 23 deletions(-) create mode 100644 src/test/run-pass/coerce-unify-return.rs diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index ab1c41f69683e..8fd44f144e1ea 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -613,6 +613,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.commit_unconditionally(move || self.try(move |_| f())) } + /// Execute `f` and commit only the region bindings if successful. + /// The function f must be very careful not to leak any non-region + /// variables that get created. + pub fn commit_regions_if_ok(&self, f: F) -> Result where + F: FnOnce() -> Result + { + debug!("commit_regions_if_ok()"); + let CombinedSnapshot { type_snapshot, + int_snapshot, + float_snapshot, + region_vars_snapshot } = self.start_snapshot(); + + let r = self.try(move |_| f()); + + // Roll back any non-region bindings - they should be resolved + // inside `f`, with, e.g. `resolve_type_vars_if_possible`. + self.type_variables + .borrow_mut() + .rollback_to(type_snapshot); + self.int_unification_table + .borrow_mut() + .rollback_to(int_snapshot); + self.float_unification_table + .borrow_mut() + .rollback_to(float_snapshot); + + // Commit region vars that may escape through resolved types. + self.region_vars + .commit(region_vars_snapshot); + + r + } + /// Execute `f`, unroll bindings on panic pub fn try(&self, f: F) -> Result where F: FnOnce(&CombinedSnapshot) -> Result diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 9d45a5516fa7b..d851206f384e3 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -14,6 +14,8 @@ use super::check_argument_types; use super::check_expr; use super::check_method_argument_types; use super::err_args; +use super::Expectation; +use super::expected_types_for_fn_args; use super::FnCtxt; use super::LvaluePreference; use super::method; @@ -65,7 +67,8 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id: pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, call_expr: &ast::Expr, callee_expr: &ast::Expr, - arg_exprs: &[P]) + arg_exprs: &[P], + expected: Expectation<'tcx>) { check_expr(fcx, callee_expr); let original_callee_ty = fcx.expr_ty(callee_expr); @@ -84,15 +87,15 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, 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); + confirm_builtin_call(fcx, call_expr, original_callee_ty, arg_exprs, expected); } Some(CallStep::Builtin) => { - confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs); + confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected); } Some(CallStep::Overloaded(method_callee)) => { - confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee); + confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee, expected); } } } @@ -153,7 +156,8 @@ fn try_overloaded_call_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, call_expr: &ast::Expr, callee_ty: Ty<'tcx>, - arg_exprs: &[P]) + arg_exprs: &[P], + expected: Expectation<'tcx>) { let error_fn_sig; @@ -192,9 +196,15 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fcx.normalize_associated_types_in(call_expr.span, &fn_sig); // Call the generic checker. + let expected_arg_tys = expected_types_for_fn_args(fcx, + call_expr.span, + expected, + fn_sig.output, + fn_sig.inputs.as_slice()); check_argument_types(fcx, call_expr.span, fn_sig.inputs.as_slice(), + &expected_arg_tys[], arg_exprs, AutorefArgs::No, fn_sig.variadic, @@ -206,7 +216,8 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, call_expr: &ast::Expr, arg_exprs: &[P], - method_callee: ty::MethodCallee<'tcx>) + method_callee: ty::MethodCallee<'tcx>, + expected: Expectation<'tcx>) { let output_type = check_method_argument_types(fcx, call_expr.span, @@ -214,7 +225,8 @@ fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, call_expr, arg_exprs, AutorefArgs::No, - TupleArgumentsFlag::TupleArguments); + TupleArgumentsFlag::TupleArguments, + expected); 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 61d401a14364e..5a62bf3f3a844 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2559,7 +2559,8 @@ fn lookup_method_for_for_loop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, iterator_expr, &[], AutorefArgs::No, - DontTupleArguments); + DontTupleArguments, + NoExpectation); match method { Some(method) => { @@ -2601,7 +2602,8 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, callee_expr: &ast::Expr, args_no_rcvr: &[P], autoref_args: AutorefArgs, - tuple_arguments: TupleArgumentsFlag) + tuple_arguments: TupleArgumentsFlag, + expected: Expectation<'tcx>) -> ty::FnOutput<'tcx> { if ty::type_is_error(method_fn_ty) { let err_inputs = err_args(fcx.tcx(), args_no_rcvr.len()); @@ -2614,6 +2616,7 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, check_argument_types(fcx, sp, &err_inputs[], + &[], args_no_rcvr, autoref_args, false, @@ -2623,9 +2626,15 @@ fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, match method_fn_ty.sty { ty::ty_bare_fn(_, ref fty) => { // HACK(eddyb) ignore self in the definition (see above). + let expected_arg_tys = expected_types_for_fn_args(fcx, + sp, + expected, + fty.sig.0.output, + &fty.sig.0.inputs[1..]); check_argument_types(fcx, sp, &fty.sig.0.inputs[1..], + &expected_arg_tys[], args_no_rcvr, autoref_args, fty.sig.0.variadic, @@ -2645,6 +2654,7 @@ 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>], + expected_arg_tys: &[Ty<'tcx>], args: &[P], autoref_args: AutorefArgs, variadic: bool, @@ -2659,6 +2669,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, 1 }; + let mut expected_arg_tys = expected_arg_tys; let expected_arg_count = fn_inputs.len(); let formal_tys = if tuple_arguments == TupleArguments { let tuple_type = structurally_resolved_type(fcx, sp, fn_inputs[0]); @@ -2671,8 +2682,16 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if arg_types.len() == 1 {""} else {"s"}, args.len(), if args.len() == 1 {" was"} else {"s were"}); + expected_arg_tys = &[][]; err_args(fcx.tcx(), args.len()) } else { + expected_arg_tys = match expected_arg_tys.get(0) { + Some(&ty) => match ty.sty { + ty::ty_tup(ref tys) => &**tys, + _ => &[] + }, + None => &[] + }; (*arg_types).clone() } } @@ -2680,14 +2699,15 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span_err!(tcx.sess, sp, E0059, "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit"); + expected_arg_tys = &[][]; err_args(fcx.tcx(), args.len()) } } } else if expected_arg_count == supplied_arg_count { - fn_inputs.iter().map(|a| *a).collect() + fn_inputs.to_vec() } else if variadic { if supplied_arg_count >= expected_arg_count { - fn_inputs.iter().map(|a| *a).collect() + fn_inputs.to_vec() } else { span_err!(tcx.sess, sp, E0060, "this function takes at least {} parameter{} \ @@ -2696,6 +2716,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if expected_arg_count == 1 {""} else {"s"}, supplied_arg_count, if supplied_arg_count == 1 {" was"} else {"s were"}); + expected_arg_tys = &[][]; err_args(fcx.tcx(), supplied_arg_count) } } else { @@ -2705,6 +2726,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if expected_arg_count == 1 {""} else {"s"}, supplied_arg_count, if supplied_arg_count == 1 {" was"} else {"s were"}); + expected_arg_tys = &[][]; err_args(fcx.tcx(), supplied_arg_count) }; @@ -2768,7 +2790,25 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, AutorefArgs::No => {} } - check_expr_coercable_to_type(fcx, &**arg, formal_ty); + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expected = expected_arg_tys.get(i).map(|&ty| { + Expectation::rvalue_hint(ty) + }); + + check_expr_with_unifier(fcx, &**arg, + expected.unwrap_or(ExpectHasType(formal_ty)), + NoPreference, || { + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExprHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerce_ty = expected.and_then(|e| e.only_has_type(fcx)); + demand::coerce(fcx, arg.span, coerce_ty.unwrap_or(formal_ty), &**arg); + + // 3. Relate the expected type and the formal one, + // if the expected type was used for the coercion. + coerce_ty.map(|ty| demand::suptype(fcx, arg.span, formal_ty, ty)); + }); } } } @@ -3008,6 +3048,45 @@ enum TupleArgumentsFlag { TupleArguments, } +/// Unifies the return type with the expected type early, for more coercions +/// and forward type information on the argument expressions. +fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + call_span: Span, + expected_ret: Expectation<'tcx>, + formal_ret: ty::FnOutput<'tcx>, + formal_args: &[Ty<'tcx>]) + -> Vec> { + let expected_args = expected_ret.only_has_type(fcx).and_then(|ret_ty| { + if let ty::FnConverging(formal_ret_ty) = formal_ret { + fcx.infcx().commit_regions_if_ok(|| { + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = infer::Misc(call_span); + let ures = fcx.infcx().sub_types(false, origin, formal_ret_ty, ret_ty); + // FIXME(#15760) can't use try! here, FromError doesn't default + // to identity so the resulting type is not constrained. + if let Err(e) = ures { + return Err(e); + } + + // Record all the argument types, with the substitutions + // produced from the above subtyping unification. + Ok(formal_args.iter().map(|ty| { + fcx.infcx().resolve_type_vars_if_possible(ty) + }).collect()) + }).ok() + } else { + None + } + }).unwrap_or(vec![]); + debug!("expected_types_for_fn_args(formal={} -> {}, expected={} -> {})", + formal_args.repr(fcx.tcx()), formal_ret.repr(fcx.tcx()), + expected_args.repr(fcx.tcx()), expected_ret.repr(fcx.tcx())); + expected_args +} + /// Invariant: /// If an expression has any sub-expressions that result in a type error, /// inspecting that expression's type with `ty::type_is_error` will return @@ -3029,12 +3108,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, expr.repr(fcx.tcx()), expected.repr(fcx.tcx())); // Checks a method call. - fn check_method_call(fcx: &FnCtxt, - expr: &ast::Expr, - method_name: ast::SpannedIdent, - args: &[P], - tps: &[P], - lvalue_pref: LvaluePreference) { + fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + expr: &ast::Expr, + method_name: ast::SpannedIdent, + args: &[P], + tps: &[P], + expected: Expectation<'tcx>, + lvalue_pref: LvaluePreference) { let rcvr = &*args[0]; check_expr_with_lvalue_pref(fcx, &*rcvr, lvalue_pref); @@ -3071,7 +3151,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, expr, &args[1..], AutorefArgs::No, - DontTupleArguments); + DontTupleArguments, + expected); write_call(fcx, expr, ret_ty); } @@ -3182,7 +3263,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, op_ex, args, autoref_args, - DontTupleArguments) { + DontTupleArguments, + NoExpectation) { ty::FnConverging(result_type) => result_type, ty::FnDiverging => fcx.tcx().types.err } @@ -3198,7 +3280,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, op_ex, args, autoref_args, - DontTupleArguments); + DontTupleArguments, + NoExpectation); fcx.tcx().types.err } } @@ -4045,10 +4128,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.write_ty(id, fcx.node_ty(b.id)); } ast::ExprCall(ref callee, ref args) => { - callee::check_call(fcx, expr, &**callee, &args[]); + callee::check_call(fcx, expr, &**callee, &args[], expected); } ast::ExprMethodCall(ident, ref tps, ref args) => { - check_method_call(fcx, expr, ident, &args[], &tps[], lvalue_pref); + check_method_call(fcx, expr, ident, &args[], &tps[], expected, lvalue_pref); let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a)); let args_err = arg_tys.fold(false, |rest_err, a| { diff --git a/src/test/run-pass/coerce-expect-unsized.rs b/src/test/run-pass/coerce-expect-unsized.rs index 3964d54f8609c..f590e6e07283c 100644 --- a/src/test/run-pass/coerce-expect-unsized.rs +++ b/src/test/run-pass/coerce-expect-unsized.rs @@ -30,4 +30,7 @@ pub fn main() { let _: &Fn(int) -> _ = &{ |x| (x as u8) }; let _: &Show = &if true { false } else { true }; let _: &Show = &match true { true => 'a', false => 'b' }; + + let _: Box<[int]> = Box::new([1, 2, 3]); + let _: Box _> = Box::new(|x| (x as u8)); } diff --git a/src/test/run-pass/coerce-unify-return.rs b/src/test/run-pass/coerce-unify-return.rs new file mode 100644 index 0000000000000..eeba9042f7c3f --- /dev/null +++ b/src/test/run-pass/coerce-unify-return.rs @@ -0,0 +1,26 @@ +// Copyright 2014 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. + +// Check that coercions unify the expected return type of a polymorphic +// function call, instead of leaving the type variables as they were. + +struct Foo; +impl Foo { + fn foo(self, x: T) -> Option { Some(x) } +} + +pub fn main() { + let _: Option = Some(main); + let _: Option = Foo.foo(main); + + // The same two cases, with implicit type variables made explicit. + let _: Option = Some::<_>(main); + let _: Option = Foo.foo::<_>(main); +} From 474872160a49e4048e5017f0bbb8d93d19267b9a Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 8 Jan 2015 18:39:17 +0200 Subject: [PATCH 4/4] tests: update some compile-fail tests for the new behavior of type expectations. --- src/test/compile-fail/issue-15783.rs | 3 ++- src/test/compile-fail/regions-early-bound-error-method.rs | 2 ++ src/test/compile-fail/regions-early-bound-error.rs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/issue-15783.rs b/src/test/compile-fail/issue-15783.rs index 1b1b03023839d..d363f2f834bb6 100644 --- a/src/test/compile-fail/issue-15783.rs +++ b/src/test/compile-fail/issue-15783.rs @@ -14,7 +14,8 @@ pub fn foo(params: Option<&[&str]>) -> usize { fn main() { let name = "Foo"; - let msg = foo(Some(&[name.as_slice()])); + let x = Some(&[name.as_slice()]); + let msg = foo(x); //~^ ERROR mismatched types: expected `core::option::Option<&[&str]>` assert_eq!(msg, 3); } diff --git a/src/test/compile-fail/regions-early-bound-error-method.rs b/src/test/compile-fail/regions-early-bound-error-method.rs index 9de0ed070c7ca..c83fb09651298 100644 --- a/src/test/compile-fail/regions-early-bound-error-method.rs +++ b/src/test/compile-fail/regions-early-bound-error-method.rs @@ -28,6 +28,8 @@ impl<'a> GetRef<'a> for Box<'a> { impl<'a> Box<'a> { fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize { g2.get() //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to + //~^ ERROR mismatched types: expected `&'a isize`, found `&'b isize` (lifetime mismatch) + } } diff --git a/src/test/compile-fail/regions-early-bound-error.rs b/src/test/compile-fail/regions-early-bound-error.rs index 37b74aea53913..cc6acdca78e81 100644 --- a/src/test/compile-fail/regions-early-bound-error.rs +++ b/src/test/compile-fail/regions-early-bound-error.rs @@ -27,6 +27,7 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> { fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize { g1.get() //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to + //~^ ERROR mismatched types: expected `&'b isize`, found `&'a isize` (lifetime mismatch) } fn main() {