]> git.lizzy.rs Git - rust.git/commitdiff
rustc_typeck: unify expected return types with formal return types to propagate coerc...
authorEduard Burtescu <edy.burt@gmail.com>
Thu, 8 Jan 2015 16:25:52 +0000 (18:25 +0200)
committerEduard Burtescu <edy.burt@gmail.com>
Sun, 11 Jan 2015 20:09:46 +0000 (22:09 +0200)
src/librustc/middle/infer/mod.rs
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/mod.rs
src/test/run-pass/coerce-expect-unsized.rs
src/test/run-pass/coerce-unify-return.rs [new file with mode: 0644]

index ab1c41f69683eb074ecb8dd5a9eaa857bdebd84a..8fd44f144e1ea577fc30a832bef7d1b9addb4282 100644 (file)
@@ -613,6 +613,39 @@ pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> where
         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<T, E, F>(&self, f: F) -> Result<T, E> where
+        F: FnOnce() -> Result<T, E>
+    {
+        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<T, E, F>(&self, f: F) -> Result<T, E> where
         F: FnOnce(&CombinedSnapshot) -> Result<T, E>
index 9d45a5516fa7ba69e1af68fb1f13355db350a684..d851206f384e316ab116e1ad7a208a12220939e9 100644 (file)
@@ -14,6 +14,8 @@
 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<ast::Expr>])
+                            arg_exprs: &[P<ast::Expr>],
+                            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<ast::Expr>])
+                                 arg_exprs: &[P<ast::Expr>],
+                                 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<ast::Expr>],
-                                    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);
index 61d401a14364ebfa9427124bd4dbf1056148b15e..5a62bf3f3a8448ffac6dcee149de48e98b3d4520 100644 (file)
@@ -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<ast::Expr>],
                                          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<ast::Expr>],
                                   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<Ty<'tcx>> {
+    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<ast::Expr>],
-                         tps: &[P<ast::Ty>],
-                         lvalue_pref: LvaluePreference) {
+    fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                   expr: &ast::Expr,
+                                   method_name: ast::SpannedIdent,
+                                   args: &[P<ast::Expr>],
+                                   tps: &[P<ast::Ty>],
+                                   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_method_call(fcx: &FnCtxt,
                                                  expr,
                                                  &args[1..],
                                                  AutorefArgs::No,
-                                                 DontTupleArguments);
+                                                 DontTupleArguments,
+                                                 expected);
 
         write_call(fcx, expr, ret_ty);
     }
@@ -3182,7 +3263,8 @@ fn lookup_op_method<'a, 'tcx, F>(fcx: &'a 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 lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
                                             op_ex,
                                             args,
                                             autoref_args,
-                                            DontTupleArguments);
+                                            DontTupleArguments,
+                                            NoExpectation);
                 fcx.tcx().types.err
             }
         }
@@ -4045,10 +4128,10 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
         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| {
index 3964d54f8609cc8ec03eb005e781ef42104df9f0..f590e6e07283cc208f26e2e63e8aacc22754104d 100644 (file)
@@ -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<Fn(int) -> _> = 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 (file)
index 0000000..eeba904
--- /dev/null
@@ -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 <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.
+
+// 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<T>(self, x: T) -> Option<T> { Some(x) }
+}
+
+pub fn main() {
+    let _: Option<fn()> = Some(main);
+    let _: Option<fn()> = Foo.foo(main);
+
+    // The same two cases, with implicit type variables made explicit.
+    let _: Option<fn()> = Some::<_>(main);
+    let _: Option<fn()> = Foo.foo::<_>(main);
+}