coercion now depends on whether the expression diverges

Before I was checking this in `demand_coerce` but that's not really the
right place. The right place is to move that into the coercion routine
itself.
This commit is contained in:
Niko Matsakis 2017-03-24 11:48:44 -04:00
parent bad79484fb
commit f83706454f
5 changed files with 55 additions and 37 deletions

View file

@ -504,7 +504,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
arm_span: arm.body.span,
source: match_src
});
coercion.coerce(self, &cause, &arm.body, arm_ty);
coercion.coerce(self, &cause, &arm.body, arm_ty, self.diverges.get());
}
}

View file

@ -38,7 +38,7 @@
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
//! `U1` coerces to `U2`).
use super::FnCtxt;
use super::{Diverges, FnCtxt};
use lint;
use hir::def_id::DefId;
@ -376,7 +376,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
(None, Some(t_cast)) => {
if let ty::TyFnDef(.., f) = self.expr_ty.sty {
// Attempt a coercion to a fn pointer type.
let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f));
let res = fcx.try_coerce(self.expr,
self.expr_ty,
Diverges::Maybe, // TODO
fcx.tcx.mk_fn_ptr(f));
if !res.is_ok() {
return Err(CastError::NonScalar);
}
@ -542,7 +545,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
}
fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool {
fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok()
// TODO
fcx.try_coerce(self.expr, self.expr_ty, Diverges::Maybe, self.cast_ty).is_ok()
}
}

View file

@ -60,7 +60,7 @@
//! sort of a minor point so I've opted to leave it for later---after all
//! we may want to adjust precisely when coercions occur.
use check::FnCtxt;
use check::{Diverges, FnCtxt};
use rustc::hir;
use rustc::hir::def_id::DefId;
@ -156,7 +156,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
})
}
fn coerce<E>(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
fn coerce<E>(&self,
exprs: &[E],
a: Ty<'tcx>,
b: Ty<'tcx>)
-> CoerceResult<'tcx>
where E: AsCoercionSite
{
let a = self.shallow_resolve(a);
@ -689,11 +693,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn try_coerce(&self,
expr: &hir::Expr,
expr_ty: Ty<'tcx>,
expr_diverges: Diverges,
target: Ty<'tcx>)
-> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_type_vars_with_obligations(expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
// Special-ish case: we can coerce any type `T` into the `!`
// type, but only if the source expression diverges.
if target.is_never() && expr_diverges.always() {
debug!("permit coercion to `!` because expr diverges");
return Ok(target);
}
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause);
self.commit_if_ok(|_| {
@ -721,15 +733,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
exprs: &[E],
prev_ty: Ty<'tcx>,
new: &hir::Expr,
new_ty: Ty<'tcx>)
new_ty: Ty<'tcx>,
new_diverges: Diverges)
-> RelateResult<'tcx, Ty<'tcx>>
where E: AsCoercionSite
{
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
let new_ty = self.resolve_type_vars_with_obligations(new_ty);
debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
// Special-ish case: we can coerce any type `T` into the `!`
// type, but only if the source expression diverges.
if prev_ty.is_never() && new_diverges.always() {
debug!("permit coercion to `!` because expr diverges");
return Ok(prev_ty);
}
let trace = TypeTrace::types(cause, true, prev_ty, new_ty);
// Special-case that coercion alone cannot handle:
@ -982,9 +1001,10 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
cause: &ObligationCause<'tcx>,
expression: &'gcx hir::Expr,
expression_ty: Ty<'tcx>)
expression_ty: Ty<'tcx>,
expression_diverges: Diverges)
{
self.coerce_inner(fcx, cause, Some(expression), expression_ty)
self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges)
}
/// Indicates that one of the inputs is a "forced unit". This
@ -1002,7 +1022,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
self.coerce_inner(fcx,
cause,
None,
fcx.tcx.mk_nil())
fcx.tcx.mk_nil(),
Diverges::Maybe)
}
/// The inner coercion "engine". If `expression` is `None`, this
@ -1012,7 +1033,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
cause: &ObligationCause<'tcx>,
expression: Option<&'gcx hir::Expr>,
mut expression_ty: Ty<'tcx>)
mut expression_ty: Ty<'tcx>,
expression_diverges: Diverges)
{
// Incorporate whatever type inference information we have
// until now; in principle we might also want to process
@ -1035,7 +1057,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
if self.pushed == 0 {
// Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this.
fcx.try_coerce(expression, expression_ty, self.expected_ty)
fcx.try_coerce(expression, expression_ty, expression_diverges, self.expected_ty)
} else {
match self.expressions {
Expressions::Dynamic(ref exprs) =>
@ -1043,13 +1065,15 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
exprs,
self.merged_ty(),
expression,
expression_ty),
expression_ty,
expression_diverges),
Expressions::UpFront(ref coercion_sites) =>
fcx.try_find_coercion_lub(cause,
&coercion_sites[0..self.pushed],
self.merged_ty(),
expression,
expression_ty),
expression_ty,
expression_diverges),
}
}
} else {

View file

@ -77,21 +77,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expected: Ty<'tcx>) {
let expected = self.resolve_type_vars_with_obligations(expected);
// If we are "assigning" to a `!` location, then we can permit
// any type to be assigned there, so long as we are in
// dead-code. This applies to e.g. `fn foo() -> ! { return; 22
// }` but also `fn foo() { let x: ! = { return; 22 }; }`.
//
// You might imagine that we could just *always* bail if we
// are in dead-code, but we don't want to do that, because
// that leaves a lot of type variables unconstrained. See
// e.g. #39808 and #39984.
let in_dead_code = self.diverges.get().always();
if expected.is_never() && in_dead_code {
return;
}
if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
let cause = self.misc(expr.span);
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
let mode = probe::Mode::MethodCall;

View file

@ -366,7 +366,7 @@ impl UnsafetyState {
/// as diverging), with some manual adjustments for control-flow
/// primitives (approximating a CFG).
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Diverges {
pub enum Diverges {
/// Potentially unknown, some cases converge,
/// others require a CFG to determine them.
Maybe,
@ -2833,7 +2833,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.coerce(self,
&self.misc(return_expr.span),
return_expr,
return_expr_ty);
return_expr_ty,
self.diverges.get());
}
@ -2864,13 +2865,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
coerce.coerce(self, &if_cause, then_expr, then_ty);
coerce.coerce(self, &if_cause, then_expr, then_ty, then_diverges);
if let Some(else_expr) = opt_else_expr {
let else_ty = self.check_expr_with_expectation(else_expr, expected);
let else_diverges = self.diverges.get();
coerce.coerce(self, &if_cause, else_expr, else_ty);
coerce.coerce(self, &if_cause, else_expr, else_ty, else_diverges);
// We won't diverge unless both branches do (or the condition does).
self.diverges.set(cond_diverges | then_diverges & else_diverges);
@ -3553,7 +3554,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
hir::ExprBreak(destination, ref expr_opt) => {
if let Some(target_id) = destination.target_id.opt_id() {
let (e_ty, cause);
let (e_ty, e_diverges, cause);
if let Some(ref e) = *expr_opt {
// If this is a break with a value, we need to type-check
// the expression. Get an expected type from the loop context.
@ -3572,11 +3573,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Recurse without `enclosing_breakables` borrowed.
e_ty = self.check_expr_with_hint(e, coerce_to);
e_diverges = self.diverges.get();
cause = self.misc(e.span);
} else {
// Otherwise, this is a break *without* a value. That's
// always legal, and is equivalent to `break ()`.
e_ty = tcx.mk_nil();
e_diverges = Diverges::Maybe;
cause = self.misc(expr.span);
}
@ -3587,7 +3590,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let ctxt = enclosing_breakables.find_breakable(target_id);
if let Some(ref mut coerce) = ctxt.coerce {
if let Some(ref e) = *expr_opt {
coerce.coerce(self, &cause, e, e_ty);
coerce.coerce(self, &cause, e, e_ty, e_diverges);
} else {
assert!(e_ty.is_nil());
coerce.coerce_forced_unit(self, &cause);
@ -3769,10 +3772,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let coerce_to = uty.unwrap_or_else(
|| self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
assert_eq!(self.diverges.get(), Diverges::Maybe);
for e in args {
let e_ty = self.check_expr_with_hint(e, coerce_to);
let cause = self.misc(e.span);
coerce.coerce(self, &cause, e, e_ty);
coerce.coerce(self, &cause, e, e_ty, self.diverges.get());
}
coerce.complete(self)
} else {