typeck: Unify if-else blocks, match arms and array elements by coercing where possible.
This commit is contained in:
parent
d2c6bef493
commit
eb926dd4b7
11 changed files with 423 additions and 172 deletions
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
if result_ty.references_error() || bty.references_error() {
|
||||
tcx.types.err
|
||||
let origin = if is_if_let_fallback {
|
||||
TypeOrigin::IfExpressionWithNoElse(expr.span)
|
||||
} 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,
|
||||
),
|
||||
};
|
||||
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, match_src)
|
||||
};
|
||||
|
||||
infer::common_supertype(
|
||||
fcx.infcx(),
|
||||
origin,
|
||||
true,
|
||||
expected,
|
||||
found,
|
||||
)
|
||||
}
|
||||
});
|
||||
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 prev_arms = || arms[..i].iter().map(|arm| &*arm.body);
|
||||
coercion::try_find_lub(fcx, origin, prev_arms, result_ty, &arm.body)
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Vec<traits::PredicateObligation<'tcx>>>,
|
||||
}
|
||||
|
||||
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
|
||||
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<Item=&'b hir::Expr> {
|
||||
|
||||
|
@ -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<Item=&'b hir::Expr> {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
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 {
|
||||
let origin = TypeOrigin::IfExpressionWithNoElse(sp);
|
||||
(origin, unit, then_ty,
|
||||
fcx.infcx().eq_types(true, origin, unit, then_ty).map(|_| unit))
|
||||
};
|
||||
|
||||
let cond_ty = fcx.expr_ty(cond_expr);
|
||||
let if_ty = if cond_ty.references_error() {
|
||||
fcx.tcx().types.err
|
||||
} else {
|
||||
branches_ty
|
||||
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);
|
||||
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);
|
||||
}
|
||||
uty
|
||||
}
|
||||
None => {
|
||||
let t: Ty = fcx.infcx().next_ty_var();
|
||||
for e in args {
|
||||
check_expr_has_type(fcx, &e, t);
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -20,13 +20,6 @@ trait Foo { fn foo() { /* this is a default fn */ } }
|
|||
impl<T> Foo for T { /* `foo` is still default here */ }
|
||||
|
||||
fn main() {
|
||||
let f = if true { foo::<u8> } else { bar::<u8> };
|
||||
//~^ ERROR if and else have incompatible types
|
||||
//~| expected `fn(isize) -> isize {foo::<u8>}`
|
||||
//~| found `fn(isize) -> isize {bar::<u8>}`
|
||||
//~| expected fn item,
|
||||
//~| found a different fn item
|
||||
|
||||
eq(foo::<u8>, bar::<u8>);
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected `fn(isize) -> isize {foo::<u8>}`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
77
src/test/run-pass/coerce-unify.rs
Normal file
77
src/test/run-pass/coerce-unify.rs
Normal file
|
@ -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 <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 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::<u8>, size_of::<u16>, size_of::<usize> 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)];
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue