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:
parent
bad79484fb
commit
f83706454f
5 changed files with 55 additions and 37 deletions
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue