diff --git a/src/librustc/middle/ty/relate.rs b/src/librustc/middle/ty/relate.rs index c85d0a1a90d5a..6da65c85f91db 100644 --- a/src/librustc/middle/ty/relate.rs +++ b/src/librustc/middle/ty/relate.rs @@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R, relate_substs(relation, opt_variances, a_subst, b_subst) } -fn relate_substs<'a,'tcx:'a,R>(relation: &mut R, - variances: Option<&ty::ItemVariances>, - a_subst: &Substs<'tcx>, - b_subst: &Substs<'tcx>) - -> RelateResult<'tcx, Substs<'tcx>> +pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R, + variances: Option<&ty::ItemVariances>, + a_subst: &Substs<'tcx>, + b_subst: &Substs<'tcx>) + -> RelateResult<'tcx, Substs<'tcx>> where R: TypeRelation<'a,'tcx> { let mut substs = Substs::empty(); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 548f5ee5e949d..305970db9e72e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const; use middle::subst::Substs; use middle::ty::{self, Ty, TypeFoldable, LvaluePreference}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; -use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; +use check::{demand, FnCtxt, Expectation}; use check::{check_expr_with_lvalue_pref}; use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type}; +use check::coercion; use lint; use require_same_types; use util::nodemap::FnvHashMap; @@ -492,54 +493,67 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // of execution reach it, we will panic, so bottom is an appropriate // type in that case) let expected = expected.adjust_for_branches(fcx); - let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| { - let bty = match expected { - // We don't coerce to `()` so that if the match expression is a - // statement it's branches can have any consistent type. That allows - // us to give better error messages (pointing to a usually better - // arm for inconsistent arms or to the whole match when a `()` type - // is required). - Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => { - check_expr_coercable_to_type(fcx, &arm.body, ety); - ety - } - _ => { - check_expr_with_expectation(fcx, &arm.body, expected); - fcx.node_ty(arm.body.id) + let mut result_ty = fcx.infcx().next_diverging_ty_var(); + let coerce_first = match expected { + // We don't coerce to `()` so that if the match expression is a + // statement it's branches can have any consistent type. That allows + // us to give better error messages (pointing to a usually better + // arm for inconsistent arms or to the whole match when a `()` type + // is required). + Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => { + ety + } + _ => result_ty + }; + for (i, arm) in arms.iter().enumerate() { + if let Some(ref e) = arm.guard { + check_expr_has_type(fcx, e, tcx.types.bool); + } + check_expr_with_expectation(fcx, &arm.body, expected); + let arm_ty = fcx.expr_ty(&arm.body); + + if result_ty.references_error() || arm_ty.references_error() { + result_ty = tcx.types.err; + continue; + } + + // Handle the fallback arm of a desugared if-let like a missing else. + let is_if_let_fallback = match match_src { + hir::MatchSource::IfLetDesugar { contains_else_clause: false } => { + i == arms.len() - 1 && arm_ty.is_nil() } + _ => false }; - if let Some(ref e) = arm.guard { - check_expr_has_type(fcx, &e, tcx.types.bool); - } + let origin = if is_if_let_fallback { + TypeOrigin::IfExpressionWithNoElse(expr.span) + } else { + TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src) + }; - if result_ty.references_error() || bty.references_error() { - tcx.types.err + let result = if is_if_let_fallback { + fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty) + } else if i == 0 { + // Special-case the first arm, as it has no "previous expressions". + coercion::try(fcx, &arm.body, coerce_first) } else { - let (origin, expected, found) = match match_src { - /* if-let construct without an else block */ - hir::MatchSource::IfLetDesugar { contains_else_clause } - if !contains_else_clause => ( - TypeOrigin::IfExpressionWithNoElse(expr.span), - bty, - result_ty, - ), - _ => ( - TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src), - result_ty, - bty, - ), - }; + let prev_arms = || arms[..i].iter().map(|arm| &*arm.body); + coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body) + }; - infer::common_supertype( - fcx.infcx(), - origin, - true, - expected, - found, - ) - } - }); + result_ty = match result { + Ok(ty) => ty, + Err(e) => { + let (expected, found) = if is_if_let_fallback { + (arm_ty, result_ty) + } else { + (result_ty, arm_ty) + }; + fcx.infcx().report_mismatched_types(origin, expected, found, e); + fcx.tcx().types.err + } + }; + } fcx.write_ty(expr.id, result_ty); } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 6468713d07aab..b5cd5d7f8e5a3 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -240,7 +240,7 @@ impl<'tcx> CastCheck<'tcx> { if let ty::TyFnDef(_, _, f) = self.expr_ty.sty { // Attempt a coercion to a fn pointer type. let res = coercion::try(fcx, self.expr, - self.expr_ty, fcx.tcx().mk_ty(ty::TyFnPtr(f))); + fcx.tcx().mk_ty(ty::TyFnPtr(f))); if !res.is_ok() { return Err(CastError::NonScalar); } @@ -390,7 +390,7 @@ impl<'tcx> CastCheck<'tcx> { } fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool { - coercion::try(fcx, self.expr, self.expr_ty, self.cast_ty).is_ok() + coercion::try(fcx, self.expr, self.cast_ty).is_ok() } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 42aaea9db5f6c..aa359c95e2d14 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -62,7 +62,7 @@ use check::{autoderef, FnCtxt, UnresolvedTypeAction}; -use middle::infer::{self, Coercion, TypeOrigin}; +use middle::infer::{Coercion, TypeOrigin, TypeTrace}; use middle::traits::{self, ObligationCause}; use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; @@ -71,7 +71,7 @@ use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt}; use middle::ty::fold::TypeFoldable; use middle::ty::error::TypeError; -use middle::ty::relate::RelateResult; +use middle::ty::relate::{relate_substs, RelateResult, TypeRelation}; use util::common::indent; use std::cell::RefCell; @@ -80,17 +80,30 @@ use rustc_front::hir; struct Coerce<'a, 'tcx: 'a> { fcx: &'a FnCtxt<'a, 'tcx>, - origin: infer::TypeOrigin, + origin: TypeOrigin, + use_lub: bool, unsizing_obligations: RefCell>>, } -type CoerceResult<'tcx> = RelateResult<'tcx, Option>>; +type CoerceResult<'tcx> = RelateResult<'tcx, (Ty<'tcx>, AutoAdjustment<'tcx>)>; + +fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability, + to_mutbl: hir::Mutability) + -> RelateResult<'tcx, ()> { + match (from_mutbl, to_mutbl) { + (hir::MutMutable, hir::MutMutable) | + (hir::MutImmutable, hir::MutImmutable) | + (hir::MutMutable, hir::MutImmutable) => Ok(()), + (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability) + } +} impl<'f, 'tcx> Coerce<'f, 'tcx> { - fn new(fcx: &'a FnCtxt<'a, 'tcx>, origin: TypeOrigin) -> Self { + fn new(fcx: &'f FnCtxt<'f, 'tcx>, origin: TypeOrigin) -> Self { Coerce { fcx: fcx, origin: origin, + use_lub: false, unsizing_obligations: RefCell::new(vec![]) } } @@ -99,9 +112,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.fcx.tcx() } - fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b)); - Ok(None) // No coercion required. + /// Unify two types (using sub or lub) and produce a noop coercion. + fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + let infcx = self.fcx.infcx(); + infcx.commit_if_ok(|_| { + let trace = TypeTrace::types(self.origin, false, a, b); + if self.use_lub { + infcx.lub(false, trace).relate(&a, &b) + } else { + infcx.sub(false, trace).relate(&a, &b) + } + }).and_then(|ty| self.identity(ty)) + } + + /// Synthesize an identity adjustment. + fn identity(&self, ty: Ty<'tcx>) -> CoerceResult<'tcx> { + Ok((ty, AdjustDerefRef(AutoDerefRef { + autoderefs: 0, + autoref: None, + unsize: None + }))) } fn coerce<'a, E, I>(&self, @@ -118,7 +148,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Just ignore error types. if a.references_error() || b.references_error() { - return Ok(None); + return self.identity(b); } // Consider coercing the subtype to a DST @@ -137,7 +167,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } ty::TyRef(_, mt_b) => { - return self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl); + return self.coerce_borrowed_pointer(exprs, a, b, mt_b.mutbl); } _ => {} @@ -156,8 +186,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.coerce_from_fn_pointer(a, a_f, b) } _ => { - // Otherwise, just use subtyping rules. - self.subtype(a, b) + // Otherwise, just use unification rules. + self.unify(a, b) } } } @@ -166,7 +196,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. fn coerce_borrowed_pointer<'a, E, I>(&self, - span: Span, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>, @@ -188,7 +217,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TyRef(_, mt_a) => { try!(coerce_mutbls(mt_a.mutbl, mutbl_b)); } - _ => return self.subtype(a, b) + _ => return self.unify(a, b) } let span = self.origin.span(); @@ -210,19 +239,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } let ty = self.tcx().mk_ref(r_borrow, TypeAndMut {ty: inner_ty, mutbl: mutbl_b}); - if let Err(err) = self.subtype(ty, b) { - if first_error.is_none() { - first_error = Some(err); + match self.unify(ty, b) { + Err(err) => { + if first_error.is_none() { + first_error = Some(err); + } + None } - None - } else { - Some(()) + Ok((ty, _)) => Some(ty) } }); match success { - Some(_) => { - Ok(Some(AdjustDerefRef(AutoDerefRef { + Some(ty) => { + Ok((ty, AdjustDerefRef(AutoDerefRef { autoderefs: autoderefs, autoref: autoref, unsize: None @@ -341,7 +371,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { unsize: Some(target) }; debug!("Success, coerced with {:?}", adjustment); - Ok(Some(AdjustDerefRef(adjustment))) + Ok((target, AdjustDerefRef(adjustment))) } fn coerce_from_fn_pointer(&self, @@ -362,13 +392,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match (fn_ty_a.unsafety, fn_ty_b.unsafety) { (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => { let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a); - try!(self.subtype(unsafe_a, b)); - return Ok(Some(AdjustUnsafeFnPointer)); + return self.unify(unsafe_a, b).map(|(ty, _)| { + (ty, AdjustUnsafeFnPointer) + }); } _ => {} } } - self.subtype(a, b) + self.unify(a, b) } fn coerce_from_fn_item(&self, @@ -387,10 +418,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match b.sty { ty::TyFnPtr(_) => { let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a)); - try!(self.subtype(a_fn_pointer, b)); - Ok(Some(AdjustReifyFnPointer)) + self.unify(a_fn_pointer, b).map(|(ty, _)| { + (ty, AdjustReifyFnPointer) + }) } - _ => self.subtype(a, b) + _ => self.unify(a, b) } } @@ -407,29 +439,29 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::TyRef(_, mt) => (true, mt), ty::TyRawPtr(mt) => (false, mt), _ => { - return self.subtype(a, b); + return self.unify(a, b); } }; // Check that the types which they point at are compatible. let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty }); - try!(self.subtype(a_unsafe, b)); + let (ty, noop) = try!(self.unify(a_unsafe, b)); try!(coerce_mutbls(mt_a.mutbl, mutbl_b)); // Although references and unsafe ptrs have the same // representation, we still register an AutoDerefRef so that // regionck knows that the region for `a` must be valid here. - if is_ref { - Ok(Some(AdjustDerefRef(AutoDerefRef { + Ok((ty, if is_ref { + AdjustDerefRef(AutoDerefRef { autoderefs: 1, autoref: Some(AutoUnsafe(mutbl_b)), unsize: None - }))) + }) } else if mt_a.mutbl != mutbl_b { - Ok(Some(AdjustMutToConstPointer)) + AdjustMutToConstPointer } else { - Ok(None) - } + noop + })) } } @@ -437,7 +469,7 @@ fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) - -> RelateResult<'tcx, Ty<'tcx>> + -> CoerceResult<'tcx> where E: Fn() -> I, I: IntoIterator { @@ -446,44 +478,153 @@ fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>, let fcx = coerce.fcx; if let AdjustDerefRef(auto) = adjustment { if auto.unsize.is_some() { - for obligation in coerce.unsizing_obligations.borrow_mut().drain() { + let mut obligations = coerce.unsizing_obligations.borrow_mut(); + for obligation in obligations.drain(..) { fcx.register_predicate(obligation); } } } - if !adjustment.is_identity() { - debug!("Success, coerced with {:?}", adjustment); - for expr in exprs() { - assert!(!fcx.inh.tables.borrow().adjustments.contains(&expr.id)); - fcx.write_adjustment(expr.id, adjustment); - } - } - Ok(ty) + Ok((ty, adjustment)) } -/// Attempt to coerce an expression from a type (a) to another type (b). -/// Adjustments are only recorded if the coercion was successful. +/// Attempt to coerce an expression to a type, and return the +/// adjusted type of the expression, if successful. +/// Adjustments are only recorded if the coercion succeeded. /// The expressions *must not* have any pre-existing adjustments. pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &hir::Expr, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> RelateResult<'tcx, ()> { - debug!("coercion::try({:?} -> {:?})", a, b); + target: Ty<'tcx>) + -> RelateResult<'tcx, Ty<'tcx>> { + let source = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr)); + debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); + let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span)); fcx.infcx().commit_if_ok(|_| { - apply(&mut coerce, &|| Some(expr), a, b) - }).map(|_| ()) + let (ty, adjustment) = + try!(apply(&mut coerce, &|| Some(expr), source, target)); + if !adjustment.is_identity() { + debug!("Success, coerced with {:?}", adjustment); + assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id)); + fcx.write_adjustment(expr.id, adjustment); + } + Ok(ty) + }) } -fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability, - to_mutbl: hir::Mutability) - -> CoerceResult<'tcx> { - match (from_mutbl, to_mutbl) { - (hir::MutMutable, hir::MutMutable) | - (hir::MutImmutable, hir::MutImmutable) | - (hir::MutMutable, hir::MutImmutable) => Ok(None), - (hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability) +/// Given some expressions, their known unified type and another expression, +/// tries to unify the types, potentially inserting coercions on any of the +/// provided expressions and returns their LUB (aka "common supertype"). +pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>, + origin: TypeOrigin, + exprs: E, + prev_ty: Ty<'tcx>, + new: &'b hir::Expr) + -> RelateResult<'tcx, Ty<'tcx>> + // FIXME(eddyb) use copyable iterators when that becomes ergonomic. + where E: Fn() -> I, + I: IntoIterator { + + let prev_ty = fcx.resolve_type_vars_if_possible(prev_ty); + let new_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(new)); + debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty); + + let trace = TypeTrace::types(origin, true, prev_ty, new_ty); + let mut lub = fcx.infcx().lub(true, trace); + + // Special-case that coercion alone cannot handle: + // Two function item types of differing IDs or Substs. + match (&prev_ty.sty, &new_ty.sty) { + (&ty::TyFnDef(a_def_id, a_substs, a_fty), + &ty::TyFnDef(b_def_id, b_substs, b_fty)) => { + // The signature must always match. + let fty = try!(lub.relate(a_fty, b_fty)); + + if a_def_id == b_def_id { + // Same function, maybe the parameters match. + let substs = fcx.infcx().commit_if_ok(|_| { + relate_substs(&mut lub, None, a_substs, b_substs) + }).map(|s| fcx.tcx().mk_substs(s)); + + if let Ok(substs) = substs { + // We have a LUB of prev_ty and new_ty, just return it. + return Ok(fcx.tcx().mk_fn_def(a_def_id, substs, fty)); + } + } + + // Reify both sides and return the reified fn pointer type. + for expr in exprs().into_iter().chain(Some(new)) { + // No adjustments can produce a fn item, so this should never trip. + assert!(!fcx.inh.tables.borrow().adjustments.contains_key(&expr.id)); + fcx.write_adjustment(expr.id, AdjustReifyFnPointer); + } + return Ok(fcx.tcx().mk_fn_ptr(fty)); + } + _ => {} + } + + let mut coerce = Coerce::new(fcx, origin); + coerce.use_lub = true; + + // First try to coerce the new expression to the type of the previous ones, + // but only if the new expression has no coercion already applied to it. + let mut first_error = None; + if !fcx.inh.tables.borrow().adjustments.contains_key(&new.id) { + let result = fcx.infcx().commit_if_ok(|_| { + apply(&mut coerce, &|| Some(new), new_ty, prev_ty) + }); + match result { + Ok((ty, adjustment)) => { + if !adjustment.is_identity() { + fcx.write_adjustment(new.id, adjustment); + } + return Ok(ty); + } + Err(e) => first_error = Some(e) + } + } + + // 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() { + let noop = match fcx.inh.tables.borrow().adjustments.get(&expr.id) { + Some(&AdjustDerefRef(AutoDerefRef { + autoderefs: 1, + autoref: Some(AutoPtr(_, mutbl_adj)), + unsize: None + })) => match fcx.expr_ty(expr).sty { + ty::TyRef(_, mt_orig) => { + // Reborrow that we can safely ignore. + mutbl_adj == mt_orig.mutbl + } + _ => false + }, + Some(_) => false, + None => true + }; + + if !noop { + return fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty)); + } + } + + match fcx.infcx().commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) { + Err(_) => { + // Avoid giving strange errors on failed attempts. + if let Some(e) = first_error { + Err(e) + } else { + fcx.infcx().commit_if_ok(|_| lub.relate(&prev_ty, &new_ty)) + } + } + Ok((ty, adjustment)) => { + if !adjustment.is_identity() { + for expr in exprs() { + fcx.write_adjustment(expr.id, adjustment); + } + } + Ok(ty) + } } } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 4dd5a6f5909b3..1f61198bef921 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -39,14 +39,10 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, expected: Ty<'tcx>, expr: &hir::Expr) { - let expr_ty = fcx.expr_ty(expr); - debug!("demand::coerce(expected = {:?}, expr_ty = {:?})", - expected, - expr_ty); - let expr_ty = fcx.resolve_type_vars_if_possible(expr_ty); let expected = fcx.resolve_type_vars_if_possible(expected); - let origin = TypeOrigin::Misc(sp); - if let Err(e) = coercion::try(fcx, expr, expr_ty, expected) { + if let Err(e) = coercion::try(fcx, expr, expected) { + let origin = TypeOrigin::Misc(sp); + let expr_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr)); fcx.infcx().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 0f16b18461847..0743c0b9e187b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -89,7 +89,7 @@ use middle::cstore::LOCAL_CRATE; use middle::def::{self, Def}; use middle::def_id::DefId; use middle::infer; -use middle::infer::{TypeOrigin, type_variable}; +use middle::infer::{TypeOrigin, TypeTrace, type_variable}; use middle::pat_util::{self, pat_id_map}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; use middle::traits::{self, report_fulfillment_errors}; @@ -101,6 +101,7 @@ use middle::ty::{MethodCall, MethodCallee}; use middle::ty::adjustment; use middle::ty::error::TypeError; use middle::ty::fold::{TypeFolder, TypeFoldable}; +use middle::ty::relate::TypeRelation; use middle::ty::util::Representability; use require_c_abi_if_variadic; use rscope::{ElisionFailureInfo, RegionScope}; @@ -2080,7 +2081,7 @@ pub fn autoderef<'a, 'b, 'tcx, E, I, T, F>(fcx: &FnCtxt<'a, 'tcx>, // (i.e. it is an inference variable) because `Ty::builtin_deref` // and `try_overloaded_deref` both simply return `None` // in such a case without producing spurious errors. - fcx.resolve_type_vars_if_possible(t) + fcx.infcx().resolve_type_vars_if_possible(&t) } }; if resolved_t.references_error() { @@ -2164,7 +2165,7 @@ fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, /// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the /// actual type we assign to the *expression* is `T`. So this function just peels off the return /// type by one layer to yield `T`. -fn make_overloaded_lvalue_return_type<'tcx>(tcx: &ty::ctxt<'tcx>, +fn make_overloaded_lvalue_return_type<'tcx>(tcx: &TyCtxt<'tcx>, method: MethodCallee<'tcx>) -> ty::TypeAndMut<'tcx> { @@ -2834,30 +2835,52 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, check_block_with_expected(fcx, then_blk, expected); let then_ty = fcx.node_ty(then_blk.id); - let branches_ty = match opt_else_expr { - Some(ref else_expr) => { - check_expr_with_expectation(fcx, &else_expr, expected); - let else_ty = fcx.expr_ty(&else_expr); - infer::common_supertype(fcx.infcx(), - TypeOrigin::IfExpression(sp), - true, - then_ty, - else_ty) - } - None => { - infer::common_supertype(fcx.infcx(), - TypeOrigin::IfExpressionWithNoElse(sp), - false, - then_ty, - fcx.tcx().mk_nil()) - } - }; + let unit = fcx.tcx().mk_nil(); + let (origin, expected, found, result) = + if let Some(else_expr) = opt_else_expr { + check_expr_with_expectation(fcx, else_expr, expected); + let else_ty = fcx.expr_ty(else_expr); + let origin = TypeOrigin::IfExpression(sp); + + // 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 = coercion::try_find_lub(fcx, origin, || Some(&**then), + then_ty, else_expr); + + // In case we did perform an adjustment, we have to update + // the type of the block, because old trans still uses it. + let adj = fcx.inh.tables.borrow().adjustments.get(&then.id).cloned(); + if res.is_ok() && adj.is_some() { + fcx.write_ty(then_blk.id, fcx.adjust_expr_ty(then, adj.as_ref())); + } - let cond_ty = fcx.expr_ty(cond_expr); - let if_ty = if cond_ty.references_error() { - fcx.tcx().types.err + res + } else { + fcx.infcx().commit_if_ok(|_| { + let trace = TypeTrace::types(origin, true, then_ty, else_ty); + fcx.infcx().lub(true, trace).relate(&then_ty, &else_ty) + }) + }; + (origin, then_ty, else_ty, result) } else { - branches_ty + let origin = TypeOrigin::IfExpressionWithNoElse(sp); + (origin, unit, then_ty, + fcx.infcx().eq_types(true, origin, unit, then_ty).map(|_| unit)) + }; + + let if_ty = match result { + Ok(ty) => { + if fcx.expr_ty(cond_expr).references_error() { + fcx.tcx().types.err + } else { + ty + } + } + Err(e) => { + fcx.infcx().report_mismatched_types(origin, expected, found, e); + fcx.tcx().types.err + } }; fcx.write_ty(id, if_ty); @@ -3497,23 +3520,30 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } }); - let typ = match uty { - Some(uty) => { - for e in args { - check_expr_coercable_to_type(fcx, &e, uty); - } - uty - } - None => { - let t: Ty = fcx.infcx().next_ty_var(); - for e in args { - check_expr_has_type(fcx, &e, t); + let mut unified = fcx.infcx().next_ty_var(); + let coerce_to = uty.unwrap_or(unified); + + for (i, e) in args.iter().enumerate() { + check_expr_with_hint(fcx, e, coerce_to); + let e_ty = fcx.expr_ty(e); + let origin = TypeOrigin::Misc(e.span); + + // Special-case the first element, as it has no "previous expressions". + let result = if i == 0 { + coercion::try(fcx, e, coerce_to) + } else { + let prev_elems = || args[..i].iter().map(|e| &**e); + coercion::try_find_lub(fcx, origin, prev_elems, unified, e) + }; + + match result { + Ok(ty) => unified = ty, + Err(e) => { + fcx.infcx().report_mismatched_types(origin, unified, e_ty, e); } - t } - }; - let typ = tcx.mk_array(typ, args.len()); - fcx.write_ty(id, typ); + } + fcx.write_ty(id, tcx.mk_array(unified, args.len())); } hir::ExprRepeat(ref element, ref count_expr) => { check_expr_has_type(fcx, &count_expr, tcx.types.usize); diff --git a/src/test/compile-fail/fn-item-type.rs b/src/test/compile-fail/fn-item-type.rs index 775e22f0428b9..2fbd1ddb1e6b9 100644 --- a/src/test/compile-fail/fn-item-type.rs +++ b/src/test/compile-fail/fn-item-type.rs @@ -20,13 +20,6 @@ trait Foo { fn foo() { /* this is a default fn */ } } impl Foo for T { /* `foo` is still default here */ } fn main() { - let f = if true { foo:: } else { bar:: }; - //~^ ERROR if and else have incompatible types - //~| expected `fn(isize) -> isize {foo::}` - //~| found `fn(isize) -> isize {bar::}` - //~| expected fn item, - //~| found a different fn item - eq(foo::, bar::); //~^ ERROR mismatched types //~| expected `fn(isize) -> isize {foo::}` diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index f907be161fa08..e1fe2d06993d5 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -17,7 +17,7 @@ fn main() { let y = match x { [] => None, //~^ ERROR mismatched types -//~| expected `[_#0i; 2]` +//~| expected `[_#1i; 2]` //~| found `[_#7t; 0]` //~| expected an array with a fixed size of 2 elements //~| found one with 0 elements diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs index 83e52216be29b..787eb7a3b8878 100644 --- a/src/test/compile-fail/issue-17728.rs +++ b/src/test/compile-fail/issue-17728.rs @@ -107,7 +107,7 @@ impl Debug for Player { } fn str_to_direction(to_parse: &str) -> RoomDirection { - match to_parse { + match to_parse { //~ ERROR match arms have incompatible types "w" | "west" => RoomDirection::West, "e" | "east" => RoomDirection::East, "n" | "north" => RoomDirection::North, @@ -116,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection { "out" => RoomDirection::Out, "up" => RoomDirection::Up, "down" => RoomDirection::Down, - _ => None //~ ERROR mismatched types + _ => None //~ NOTE match arm with an incompatible type } } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index 256c5d8e6f72c..9143a226a2483 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -21,5 +21,5 @@ impl vec_monad for Vec { } fn main() { ["hi"].bind(|x| [x] ); - //~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope + //~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope } diff --git a/src/test/run-pass/coerce-unify.rs b/src/test/run-pass/coerce-unify.rs new file mode 100644 index 0000000000000..3d690146931d9 --- /dev/null +++ b/src/test/run-pass/coerce-unify.rs @@ -0,0 +1,77 @@ +// Copyright 2016 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 can unify if-else, match arms and array elements. + +// Try to construct if-else chains, matches and arrays out of given expressions. +macro_rules! check { + ($last:expr $(, $rest:expr)+) => { + // Last expression comes first because of whacky ifs and matches. + let _ = $(if false { $rest })else+ else { $last }; + + let _ = match 0 { $(_ if false => $rest,)+ _ => $last }; + + let _ = [$($rest,)+ $last]; + } +} + +// Check all non-uniform cases of 2 and 3 expressions of 2 types. +macro_rules! check2 { + ($a:expr, $b:expr) => { + check!($a, $b); + check!($b, $a); + + check!($a, $a, $b); + check!($a, $b, $a); + check!($a, $b, $b); + + check!($b, $a, $a); + check!($b, $a, $b); + check!($b, $b, $a); + } +} + +// Check all non-uniform cases of 2 and 3 expressions of 3 types. +macro_rules! check3 { + ($a:expr, $b:expr, $c:expr) => { + // Delegate to check2 for cases where a type repeats. + check2!($a, $b); + check2!($b, $c); + check2!($a, $c); + + // Check the remaining cases, i.e. permutations of ($a, $b, $c). + check!($a, $b, $c); + check!($a, $c, $b); + check!($b, $a, $c); + check!($b, $c, $a); + check!($c, $a, $b); + check!($c, $b, $a); + } +} + +use std::mem::size_of; + +fn foo() {} +fn bar() {} + +pub fn main() { + check3!(foo, bar, foo as fn()); + check3!(size_of::, size_of::, size_of:: as fn() -> usize); + + let s = String::from("bar"); + check2!("foo", &s); + + let a = [1, 2, 3]; + let v = vec![1, 2, 3]; + check2!(&a[..], &v); + + // Make sure in-array coercion still works. + let _ = [("a", Default::default()), (Default::default(), "b"), (&s, &s)]; +}