diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index c1a0a6f70aa..42769a86364 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -419,11 +419,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } _ => result_ty }; + + let mut arm_tys = Vec::new(); for (i, arm) in arms.iter().enumerate() { if let Some(ref e) = arm.guard { self.check_expr_has_type(e, tcx.types.bool); } let arm_ty = self.check_expr_with_expectation(&arm.body, expected); + arm_tys.push(arm_ty); if result_ty.references_error() || arm_ty.references_error() { result_ty = tcx.types.err; @@ -453,10 +456,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) } else if i == 0 { // Special-case the first arm, as it has no "previous expressions". - self.try_coerce(&arm.body, coerce_first) + self.try_coerce(&arm.body, arm_ty, coerce_first) } else { - let prev_arms = || arms[..i].iter().map(|arm| &*arm.body); - self.try_find_coercion_lub(origin, prev_arms, result_ty, &arm.body) + let prev_arms = || arms[..i].iter().map(|arm| &*arm.body) + .zip(arm_tys.iter().cloned()); + self.try_find_coercion_lub(origin, prev_arms, result_ty, &arm.body, arm_ty) }; result_ty = match result { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 1fda38d8a33..0c9da86563a 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -321,7 +321,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { (None, Some(t_cast)) => { if let ty::TyFnDef(.., f) = self.expr_ty.sty { // Attempt a coercion to a fn pointer type. - let res = fcx.try_coerce(self.expr, fcx.tcx.mk_fn_ptr(f)); + let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f)); if !res.is_ok() { return Err(CastError::NonScalar); } @@ -471,7 +471,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { } fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool { - fcx.try_coerce(self.expr, self.cast_ty).is_ok() + fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok() } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 60ca9309eea..263a83389ea 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -630,9 +630,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// The expressions *must not* have any pre-existing adjustments. pub fn try_coerce(&self, expr: &hir::Expr, + expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - let source = self.resolve_type_vars_with_obligations(self.expr_ty(expr)); + let source = self.resolve_type_vars_with_obligations(expr_ty); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); let mut coerce = Coerce::new(self, TypeOrigin::ExprAssignable(expr.span)); @@ -658,14 +659,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { origin: TypeOrigin, exprs: E, prev_ty: Ty<'tcx>, - new: &'b hir::Expr) + new: &'b hir::Expr, + new_ty: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> // FIXME(eddyb) use copyable iterators when that becomes ergonomic. where E: Fn() -> I, - I: IntoIterator { + I: IntoIterator)> { let prev_ty = self.resolve_type_vars_with_obligations(prev_ty); - let new_ty = self.resolve_type_vars_with_obligations(self.expr_ty(new)); + let new_ty = self.resolve_type_vars_with_obligations(new_ty); debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty); let trace = TypeTrace::types(origin, true, prev_ty, new_ty); @@ -701,7 +703,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } // Reify both sides and return the reified fn pointer type. - for expr in exprs().into_iter().chain(Some(new)) { + for (expr, _) in exprs().into_iter().chain(Some((new, new_ty))) { // No adjustments can produce a fn item, so this should never trip. assert!(!self.tables.borrow().adjustments.contains_key(&expr.id)); self.write_adjustment(expr.id, AdjustReifyFnPointer); @@ -735,13 +737,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Then try to coerce the previous expressions to the type of the new one. // This requires ensuring there are no coercions applied to *any* of the // previous expressions, other than noop reborrows (ignoring lifetimes). - for expr in exprs() { + for (expr, expr_ty) in exprs() { let noop = match self.tables.borrow().adjustments.get(&expr.id) { Some(&AdjustDerefRef(AutoDerefRef { autoderefs: 1, autoref: Some(AutoPtr(_, mutbl_adj)), unsize: None - })) => match self.expr_ty(expr).sty { + })) => match expr_ty.sty { ty::TyRef(_, mt_orig) => { // Reborrow that we can safely ignore. mutbl_adj == mt_orig.mutbl @@ -765,7 +767,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - match self.commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) { + match self.commit_if_ok(|_| apply(&mut coerce, + &|| exprs().into_iter().map(|(e, _)| e), + prev_ty, new_ty)) { Err(_) => { // Avoid giving strange errors on failed attempts. if let Some(e) = first_error { @@ -783,7 +787,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } Ok((ty, adjustment)) => { if !adjustment.is_identity() { - for expr in exprs() { + for (expr, _) in exprs() { let previous = self.tables.borrow().adjustments.get(&expr.id).cloned(); if let Some(AdjustNeverToAny(_)) = previous { self.write_adjustment(expr.id, AdjustNeverToAny(ty)); diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 1f3a83ebc1d..d622bc7f751 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -53,11 +53,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } // Checks that the type of `expr` can be coerced to `expected`. - pub fn demand_coerce(&self, expr: &hir::Expr, expected: Ty<'tcx>) { + pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) { let expected = self.resolve_type_vars_with_obligations(expected); - if let Err(e) = self.try_coerce(expr, expected) { + if let Err(e) = self.try_coerce(expr, checked_ty, expected) { let origin = TypeOrigin::Misc(expr.span); - let expr_ty = self.resolve_type_vars_with_obligations(self.expr_ty(expr)); + let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); self.report_mismatched_types(origin, expected, expr_ty, e); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 79dd951a5d8..b1283339213 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2567,13 +2567,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Expectation::rvalue_hint(self, ty) }); - self.check_expr_with_expectation(&arg, - expected.unwrap_or(ExpectHasType(formal_ty))); + let checked_ty = self.check_expr_with_expectation(&arg, + expected.unwrap_or(ExpectHasType(formal_ty))); // 2. Coerce to the most detailed type that could be coerced // to, which is `expected_ty` if `rvalue_hint` returns an // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. let coerce_ty = expected.and_then(|e| e.only_has_type(self)); - self.demand_coerce(&arg, coerce_ty.unwrap_or(formal_ty)); + self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty)); // 3. Relate the expected type and the formal one, // if the expected type was used for the coercion. @@ -2715,7 +2715,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr: &'gcx hir::Expr, expected: Ty<'tcx>) -> Ty<'tcx> { let ty = self.check_expr_with_hint(expr, expected); - self.demand_coerce(expr, expected); + self.demand_coerce(expr, ty, expected); ty } @@ -2861,8 +2861,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Only try to coerce-unify if we have a then expression // to assign coercions to, otherwise it's () or diverging. let result = if let Some(ref then) = then_blk.expr { - let res = self.try_find_coercion_lub(origin, || Some(&**then), - then_ty, else_expr); + let res = self.try_find_coercion_lub(origin, || Some((&**then, then_ty)), + then_ty, else_expr, else_ty); // In case we did perform an adjustment, we have to update // the type of the block, because old trans still uses it. @@ -3594,16 +3594,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut unified = self.next_ty_var(); let coerce_to = uty.unwrap_or(unified); + let mut arg_tys = Vec::new(); for (i, e) in args.iter().enumerate() { let e_ty = self.check_expr_with_hint(e, coerce_to); + arg_tys.push(e_ty); let origin = TypeOrigin::Misc(e.span); // Special-case the first element, as it has no "previous expressions". let result = if i == 0 { - self.try_coerce(e, coerce_to) + self.try_coerce(e, e_ty, coerce_to) } else { - let prev_elems = || args[..i].iter().map(|e| &**e); - self.try_find_coercion_lub(origin, prev_elems, unified, e) + let prev_elems = || args[..i].iter().map(|e| &**e) + .zip(arg_tys.iter().cloned()); + self.try_find_coercion_lub(origin, prev_elems, unified, e, e_ty) }; match result {