typeck: Unify if-else blocks, match arms and array elements by coercing where possible.
This commit is contained in:
parent
d2c6bef493
commit
eb926dd4b7
@ -139,11 +139,11 @@ fn relate_item_substs<'a,'tcx:'a,R>(relation: &mut R,
|
|||||||
relate_substs(relation, opt_variances, a_subst, b_subst)
|
relate_substs(relation, opt_variances, a_subst, b_subst)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
|
pub fn relate_substs<'a,'tcx:'a,R>(relation: &mut R,
|
||||||
variances: Option<&ty::ItemVariances>,
|
variances: Option<&ty::ItemVariances>,
|
||||||
a_subst: &Substs<'tcx>,
|
a_subst: &Substs<'tcx>,
|
||||||
b_subst: &Substs<'tcx>)
|
b_subst: &Substs<'tcx>)
|
||||||
-> RelateResult<'tcx, Substs<'tcx>>
|
-> RelateResult<'tcx, Substs<'tcx>>
|
||||||
where R: TypeRelation<'a,'tcx>
|
where R: TypeRelation<'a,'tcx>
|
||||||
{
|
{
|
||||||
let mut substs = Substs::empty();
|
let mut substs = Substs::empty();
|
||||||
|
@ -15,9 +15,10 @@ use middle::pat_util::pat_is_resolved_const;
|
|||||||
use middle::subst::Substs;
|
use middle::subst::Substs;
|
||||||
use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
|
use middle::ty::{self, Ty, TypeFoldable, LvaluePreference};
|
||||||
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
|
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::{check_expr_with_lvalue_pref};
|
||||||
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
|
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
|
||||||
|
use check::coercion;
|
||||||
use lint;
|
use lint;
|
||||||
use require_same_types;
|
use require_same_types;
|
||||||
use util::nodemap::FnvHashMap;
|
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
|
// of execution reach it, we will panic, so bottom is an appropriate
|
||||||
// type in that case)
|
// type in that case)
|
||||||
let expected = expected.adjust_for_branches(fcx);
|
let expected = expected.adjust_for_branches(fcx);
|
||||||
let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
|
let mut result_ty = fcx.infcx().next_diverging_ty_var();
|
||||||
let bty = match expected {
|
let coerce_first = match expected {
|
||||||
// We don't coerce to `()` so that if the match expression is a
|
// We don't coerce to `()` so that if the match expression is a
|
||||||
// statement it's branches can have any consistent type. That allows
|
// statement it's branches can have any consistent type. That allows
|
||||||
// us to give better error messages (pointing to a usually better
|
// us to give better error messages (pointing to a usually better
|
||||||
// arm for inconsistent arms or to the whole match when a `()` type
|
// arm for inconsistent arms or to the whole match when a `()` type
|
||||||
// is required).
|
// is required).
|
||||||
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
|
Expectation::ExpectHasType(ety) if ety != fcx.tcx().mk_nil() => {
|
||||||
check_expr_coercable_to_type(fcx, &arm.body, ety);
|
ety
|
||||||
ety
|
}
|
||||||
}
|
_ => result_ty
|
||||||
_ => {
|
};
|
||||||
check_expr_with_expectation(fcx, &arm.body, expected);
|
for (i, arm) in arms.iter().enumerate() {
|
||||||
fcx.node_ty(arm.body.id)
|
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 {
|
let origin = if is_if_let_fallback {
|
||||||
check_expr_has_type(fcx, &e, tcx.types.bool);
|
TypeOrigin::IfExpressionWithNoElse(expr.span)
|
||||||
}
|
|
||||||
|
|
||||||
if result_ty.references_error() || bty.references_error() {
|
|
||||||
tcx.types.err
|
|
||||||
} else {
|
} else {
|
||||||
let (origin, expected, found) = match match_src {
|
TypeOrigin::MatchExpressionArm(expr.span, arm.body.span, 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,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
infer::common_supertype(
|
let result = if is_if_let_fallback {
|
||||||
fcx.infcx(),
|
fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty)
|
||||||
origin,
|
} else if i == 0 {
|
||||||
true,
|
// Special-case the first arm, as it has no "previous expressions".
|
||||||
expected,
|
coercion::try(fcx, &arm.body, coerce_first)
|
||||||
found,
|
} 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);
|
fcx.write_ty(expr.id, result_ty);
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,7 @@ impl<'tcx> CastCheck<'tcx> {
|
|||||||
if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
|
if let ty::TyFnDef(_, _, f) = self.expr_ty.sty {
|
||||||
// Attempt a coercion to a fn pointer type.
|
// Attempt a coercion to a fn pointer type.
|
||||||
let res = coercion::try(fcx, self.expr,
|
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() {
|
if !res.is_ok() {
|
||||||
return Err(CastError::NonScalar);
|
return Err(CastError::NonScalar);
|
||||||
}
|
}
|
||||||
@ -390,7 +390,7 @@ impl<'tcx> CastCheck<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool {
|
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 check::{autoderef, FnCtxt, UnresolvedTypeAction};
|
||||||
|
|
||||||
use middle::infer::{self, Coercion, TypeOrigin};
|
use middle::infer::{Coercion, TypeOrigin, TypeTrace};
|
||||||
use middle::traits::{self, ObligationCause};
|
use middle::traits::{self, ObligationCause};
|
||||||
use middle::traits::{predicate_for_trait_def, report_selection_error};
|
use middle::traits::{predicate_for_trait_def, report_selection_error};
|
||||||
use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
|
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::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt};
|
||||||
use middle::ty::fold::TypeFoldable;
|
use middle::ty::fold::TypeFoldable;
|
||||||
use middle::ty::error::TypeError;
|
use middle::ty::error::TypeError;
|
||||||
use middle::ty::relate::RelateResult;
|
use middle::ty::relate::{relate_substs, RelateResult, TypeRelation};
|
||||||
use util::common::indent;
|
use util::common::indent;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -80,17 +80,30 @@ use rustc_front::hir;
|
|||||||
|
|
||||||
struct Coerce<'a, 'tcx: 'a> {
|
struct Coerce<'a, 'tcx: 'a> {
|
||||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||||
origin: infer::TypeOrigin,
|
origin: TypeOrigin,
|
||||||
|
use_lub: bool,
|
||||||
unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
|
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> {
|
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 {
|
Coerce {
|
||||||
fcx: fcx,
|
fcx: fcx,
|
||||||
origin: origin,
|
origin: origin,
|
||||||
|
use_lub: false,
|
||||||
unsizing_obligations: RefCell::new(vec![])
|
unsizing_obligations: RefCell::new(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,9 +112,26 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
self.fcx.tcx()
|
self.fcx.tcx()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
/// Unify two types (using sub or lub) and produce a noop coercion.
|
||||||
try!(self.fcx.infcx().sub_types(false, self.origin.clone(), a, b));
|
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||||
Ok(None) // No coercion required.
|
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,
|
fn coerce<'a, E, I>(&self,
|
||||||
@ -118,7 +148,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
|
|
||||||
// Just ignore error types.
|
// Just ignore error types.
|
||||||
if a.references_error() || b.references_error() {
|
if a.references_error() || b.references_error() {
|
||||||
return Ok(None);
|
return self.identity(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider coercing the subtype to a DST
|
// Consider coercing the subtype to a DST
|
||||||
@ -137,7 +167,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ty::TyRef(_, mt_b) => {
|
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)
|
self.coerce_from_fn_pointer(a, a_f, b)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Otherwise, just use subtyping rules.
|
// Otherwise, just use unification rules.
|
||||||
self.subtype(a, b)
|
self.unify(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +196,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
/// To match `A` with `B`, autoderef will be performed,
|
/// To match `A` with `B`, autoderef will be performed,
|
||||||
/// calling `deref`/`deref_mut` where necessary.
|
/// calling `deref`/`deref_mut` where necessary.
|
||||||
fn coerce_borrowed_pointer<'a, E, I>(&self,
|
fn coerce_borrowed_pointer<'a, E, I>(&self,
|
||||||
span: Span,
|
|
||||||
exprs: &E,
|
exprs: &E,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
b: Ty<'tcx>,
|
b: Ty<'tcx>,
|
||||||
@ -188,7 +217,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
ty::TyRef(_, mt_a) => {
|
ty::TyRef(_, mt_a) => {
|
||||||
try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
|
try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
|
||||||
}
|
}
|
||||||
_ => return self.subtype(a, b)
|
_ => return self.unify(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = self.origin.span();
|
let span = self.origin.span();
|
||||||
@ -210,19 +239,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
}
|
}
|
||||||
let ty = self.tcx().mk_ref(r_borrow,
|
let ty = self.tcx().mk_ref(r_borrow,
|
||||||
TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
|
TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
|
||||||
if let Err(err) = self.subtype(ty, b) {
|
match self.unify(ty, b) {
|
||||||
if first_error.is_none() {
|
Err(err) => {
|
||||||
first_error = Some(err);
|
if first_error.is_none() {
|
||||||
|
first_error = Some(err);
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
None
|
Ok((ty, _)) => Some(ty)
|
||||||
} else {
|
|
||||||
Some(())
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
match success {
|
match success {
|
||||||
Some(_) => {
|
Some(ty) => {
|
||||||
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
Ok((ty, AdjustDerefRef(AutoDerefRef {
|
||||||
autoderefs: autoderefs,
|
autoderefs: autoderefs,
|
||||||
autoref: autoref,
|
autoref: autoref,
|
||||||
unsize: None
|
unsize: None
|
||||||
@ -341,7 +371,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
unsize: Some(target)
|
unsize: Some(target)
|
||||||
};
|
};
|
||||||
debug!("Success, coerced with {:?}", adjustment);
|
debug!("Success, coerced with {:?}", adjustment);
|
||||||
Ok(Some(AdjustDerefRef(adjustment)))
|
Ok((target, AdjustDerefRef(adjustment)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_from_fn_pointer(&self,
|
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) {
|
match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
|
||||||
(hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
|
(hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
|
||||||
let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
|
let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
|
||||||
try!(self.subtype(unsafe_a, b));
|
return self.unify(unsafe_a, b).map(|(ty, _)| {
|
||||||
return Ok(Some(AdjustUnsafeFnPointer));
|
(ty, AdjustUnsafeFnPointer)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.subtype(a, b)
|
self.unify(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_from_fn_item(&self,
|
fn coerce_from_fn_item(&self,
|
||||||
@ -387,10 +418,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
match b.sty {
|
match b.sty {
|
||||||
ty::TyFnPtr(_) => {
|
ty::TyFnPtr(_) => {
|
||||||
let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a));
|
let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a));
|
||||||
try!(self.subtype(a_fn_pointer, b));
|
self.unify(a_fn_pointer, b).map(|(ty, _)| {
|
||||||
Ok(Some(AdjustReifyFnPointer))
|
(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::TyRef(_, mt) => (true, mt),
|
||||||
ty::TyRawPtr(mt) => (false, 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.
|
// 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 });
|
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));
|
try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
|
||||||
|
|
||||||
// Although references and unsafe ptrs have the same
|
// Although references and unsafe ptrs have the same
|
||||||
// representation, we still register an AutoDerefRef so that
|
// representation, we still register an AutoDerefRef so that
|
||||||
// regionck knows that the region for `a` must be valid here.
|
// regionck knows that the region for `a` must be valid here.
|
||||||
if is_ref {
|
Ok((ty, if is_ref {
|
||||||
Ok(Some(AdjustDerefRef(AutoDerefRef {
|
AdjustDerefRef(AutoDerefRef {
|
||||||
autoderefs: 1,
|
autoderefs: 1,
|
||||||
autoref: Some(AutoUnsafe(mutbl_b)),
|
autoref: Some(AutoUnsafe(mutbl_b)),
|
||||||
unsize: None
|
unsize: None
|
||||||
})))
|
})
|
||||||
} else if mt_a.mutbl != mutbl_b {
|
} else if mt_a.mutbl != mutbl_b {
|
||||||
Ok(Some(AdjustMutToConstPointer))
|
AdjustMutToConstPointer
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
noop
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +469,7 @@ fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>,
|
|||||||
exprs: &E,
|
exprs: &E,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
b: Ty<'tcx>)
|
b: Ty<'tcx>)
|
||||||
-> RelateResult<'tcx, Ty<'tcx>>
|
-> CoerceResult<'tcx>
|
||||||
where E: Fn() -> I,
|
where E: Fn() -> I,
|
||||||
I: IntoIterator<Item=&'b hir::Expr> {
|
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;
|
let fcx = coerce.fcx;
|
||||||
if let AdjustDerefRef(auto) = adjustment {
|
if let AdjustDerefRef(auto) = adjustment {
|
||||||
if auto.unsize.is_some() {
|
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);
|
fcx.register_predicate(obligation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !adjustment.is_identity() {
|
Ok((ty, adjustment))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to coerce an expression from a type (a) to another type (b).
|
/// Attempt to coerce an expression to a type, and return the
|
||||||
/// Adjustments are only recorded if the coercion was successful.
|
/// adjusted type of the expression, if successful.
|
||||||
|
/// Adjustments are only recorded if the coercion succeeded.
|
||||||
/// The expressions *must not* have any pre-existing adjustments.
|
/// The expressions *must not* have any pre-existing adjustments.
|
||||||
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
a: Ty<'tcx>,
|
target: Ty<'tcx>)
|
||||||
b: Ty<'tcx>)
|
-> RelateResult<'tcx, Ty<'tcx>> {
|
||||||
-> RelateResult<'tcx, ()> {
|
let source = fcx.resolve_type_vars_if_possible(fcx.expr_ty(expr));
|
||||||
debug!("coercion::try({:?} -> {:?})", a, b);
|
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
|
||||||
|
|
||||||
let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
|
let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
|
||||||
fcx.infcx().commit_if_ok(|_| {
|
fcx.infcx().commit_if_ok(|_| {
|
||||||
apply(&mut coerce, &|| Some(expr), a, b)
|
let (ty, adjustment) =
|
||||||
}).map(|_| ())
|
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,
|
/// Given some expressions, their known unified type and another expression,
|
||||||
to_mutbl: hir::Mutability)
|
/// tries to unify the types, potentially inserting coercions on any of the
|
||||||
-> CoerceResult<'tcx> {
|
/// provided expressions and returns their LUB (aka "common supertype").
|
||||||
match (from_mutbl, to_mutbl) {
|
pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>,
|
||||||
(hir::MutMutable, hir::MutMutable) |
|
origin: TypeOrigin,
|
||||||
(hir::MutImmutable, hir::MutImmutable) |
|
exprs: E,
|
||||||
(hir::MutMutable, hir::MutImmutable) => Ok(None),
|
prev_ty: Ty<'tcx>,
|
||||||
(hir::MutImmutable, hir::MutMutable) => Err(TypeError::Mutability)
|
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,
|
sp: Span,
|
||||||
expected: Ty<'tcx>,
|
expected: Ty<'tcx>,
|
||||||
expr: &hir::Expr) {
|
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 expected = fcx.resolve_type_vars_if_possible(expected);
|
||||||
let origin = TypeOrigin::Misc(sp);
|
if let Err(e) = coercion::try(fcx, expr, expected) {
|
||||||
if let Err(e) = coercion::try(fcx, expr, expr_ty, 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);
|
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::{self, Def};
|
||||||
use middle::def_id::DefId;
|
use middle::def_id::DefId;
|
||||||
use middle::infer;
|
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::pat_util::{self, pat_id_map};
|
||||||
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
|
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
|
||||||
use middle::traits::{self, report_fulfillment_errors};
|
use middle::traits::{self, report_fulfillment_errors};
|
||||||
@ -101,6 +101,7 @@ use middle::ty::{MethodCall, MethodCallee};
|
|||||||
use middle::ty::adjustment;
|
use middle::ty::adjustment;
|
||||||
use middle::ty::error::TypeError;
|
use middle::ty::error::TypeError;
|
||||||
use middle::ty::fold::{TypeFolder, TypeFoldable};
|
use middle::ty::fold::{TypeFolder, TypeFoldable};
|
||||||
|
use middle::ty::relate::TypeRelation;
|
||||||
use middle::ty::util::Representability;
|
use middle::ty::util::Representability;
|
||||||
use require_c_abi_if_variadic;
|
use require_c_abi_if_variadic;
|
||||||
use rscope::{ElisionFailureInfo, RegionScope};
|
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`
|
// (i.e. it is an inference variable) because `Ty::builtin_deref`
|
||||||
// and `try_overloaded_deref` both simply return `None`
|
// and `try_overloaded_deref` both simply return `None`
|
||||||
// in such a case without producing spurious errors.
|
// 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() {
|
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
|
/// 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
|
/// actual type we assign to the *expression* is `T`. So this function just peels off the return
|
||||||
/// type by one layer to yield `T`.
|
/// 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>)
|
method: MethodCallee<'tcx>)
|
||||||
-> ty::TypeAndMut<'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);
|
check_block_with_expected(fcx, then_blk, expected);
|
||||||
let then_ty = fcx.node_ty(then_blk.id);
|
let then_ty = fcx.node_ty(then_blk.id);
|
||||||
|
|
||||||
let branches_ty = match opt_else_expr {
|
let unit = fcx.tcx().mk_nil();
|
||||||
Some(ref else_expr) => {
|
let (origin, expected, found, result) =
|
||||||
check_expr_with_expectation(fcx, &else_expr, expected);
|
if let Some(else_expr) = opt_else_expr {
|
||||||
let else_ty = fcx.expr_ty(&else_expr);
|
check_expr_with_expectation(fcx, else_expr, expected);
|
||||||
infer::common_supertype(fcx.infcx(),
|
let else_ty = fcx.expr_ty(else_expr);
|
||||||
TypeOrigin::IfExpression(sp),
|
let origin = TypeOrigin::IfExpression(sp);
|
||||||
true,
|
|
||||||
then_ty,
|
// Only try to coerce-unify if we have a then expression
|
||||||
else_ty)
|
// to assign coercions to, otherwise it's () or diverging.
|
||||||
}
|
let result = if let Some(ref then) = then_blk.expr {
|
||||||
None => {
|
let res = coercion::try_find_lub(fcx, origin, || Some(&**then),
|
||||||
infer::common_supertype(fcx.infcx(),
|
then_ty, else_expr);
|
||||||
TypeOrigin::IfExpressionWithNoElse(sp),
|
|
||||||
false,
|
// In case we did perform an adjustment, we have to update
|
||||||
then_ty,
|
// the type of the block, because old trans still uses it.
|
||||||
fcx.tcx().mk_nil())
|
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 = match result {
|
||||||
let if_ty = if cond_ty.references_error() {
|
Ok(ty) => {
|
||||||
fcx.tcx().types.err
|
if fcx.expr_ty(cond_expr).references_error() {
|
||||||
} else {
|
fcx.tcx().types.err
|
||||||
branches_ty
|
} else {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
fcx.infcx().report_mismatched_types(origin, expected, found, e);
|
||||||
|
fcx.tcx().types.err
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fcx.write_ty(id, if_ty);
|
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 {
|
let mut unified = fcx.infcx().next_ty_var();
|
||||||
Some(uty) => {
|
let coerce_to = uty.unwrap_or(unified);
|
||||||
for e in args {
|
|
||||||
check_expr_coercable_to_type(fcx, &e, uty);
|
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();
|
fcx.write_ty(id, tcx.mk_array(unified, args.len()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
hir::ExprRepeat(ref element, ref count_expr) => {
|
hir::ExprRepeat(ref element, ref count_expr) => {
|
||||||
check_expr_has_type(fcx, &count_expr, tcx.types.usize);
|
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 */ }
|
impl<T> Foo for T { /* `foo` is still default here */ }
|
||||||
|
|
||||||
fn main() {
|
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>);
|
eq(foo::<u8>, bar::<u8>);
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| expected `fn(isize) -> isize {foo::<u8>}`
|
//~| expected `fn(isize) -> isize {foo::<u8>}`
|
||||||
|
@ -17,7 +17,7 @@ fn main() {
|
|||||||
let y = match x {
|
let y = match x {
|
||||||
[] => None,
|
[] => None,
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| expected `[_#0i; 2]`
|
//~| expected `[_#1i; 2]`
|
||||||
//~| found `[_#7t; 0]`
|
//~| found `[_#7t; 0]`
|
||||||
//~| expected an array with a fixed size of 2 elements
|
//~| expected an array with a fixed size of 2 elements
|
||||||
//~| found one with 0 elements
|
//~| found one with 0 elements
|
||||||
|
@ -107,7 +107,7 @@ impl Debug for Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn str_to_direction(to_parse: &str) -> RoomDirection {
|
fn str_to_direction(to_parse: &str) -> RoomDirection {
|
||||||
match to_parse {
|
match to_parse { //~ ERROR match arms have incompatible types
|
||||||
"w" | "west" => RoomDirection::West,
|
"w" | "west" => RoomDirection::West,
|
||||||
"e" | "east" => RoomDirection::East,
|
"e" | "east" => RoomDirection::East,
|
||||||
"n" | "north" => RoomDirection::North,
|
"n" | "north" => RoomDirection::North,
|
||||||
@ -116,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
|
|||||||
"out" => RoomDirection::Out,
|
"out" => RoomDirection::Out,
|
||||||
"up" => RoomDirection::Up,
|
"up" => RoomDirection::Up,
|
||||||
"down" => RoomDirection::Down,
|
"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() {
|
fn main() {
|
||||||
["hi"].bind(|x| [x] );
|
["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…
Reference in New Issue
Block a user