Skip to content

Commit f837064

Browse files
committed
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.
1 parent bad7948 commit f837064

File tree

5 files changed

+55
-37
lines changed

5 files changed

+55
-37
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
504504
arm_span: arm.body.span,
505505
source: match_src
506506
});
507-
coercion.coerce(self, &cause, &arm.body, arm_ty);
507+
coercion.coerce(self, &cause, &arm.body, arm_ty, self.diverges.get());
508508
}
509509
}
510510

src/librustc_typeck/check/cast.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
3939
//! `U1` coerces to `U2`).
4040
41-
use super::FnCtxt;
41+
use super::{Diverges, FnCtxt};
4242

4343
use lint;
4444
use hir::def_id::DefId;
@@ -376,7 +376,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
376376
(None, Some(t_cast)) => {
377377
if let ty::TyFnDef(.., f) = self.expr_ty.sty {
378378
// Attempt a coercion to a fn pointer type.
379-
let res = fcx.try_coerce(self.expr, self.expr_ty, fcx.tcx.mk_fn_ptr(f));
379+
let res = fcx.try_coerce(self.expr,
380+
self.expr_ty,
381+
Diverges::Maybe, // TODO
382+
fcx.tcx.mk_fn_ptr(f));
380383
if !res.is_ok() {
381384
return Err(CastError::NonScalar);
382385
}
@@ -542,7 +545,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
542545
}
543546

544547
fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool {
545-
fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok()
548+
// TODO
549+
fcx.try_coerce(self.expr, self.expr_ty, Diverges::Maybe, self.cast_ty).is_ok()
546550
}
547551
}
548552

src/librustc_typeck/check/coercion.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
//! sort of a minor point so I've opted to leave it for later---after all
6161
//! we may want to adjust precisely when coercions occur.
6262
63-
use check::FnCtxt;
63+
use check::{Diverges, FnCtxt};
6464

6565
use rustc::hir;
6666
use rustc::hir::def_id::DefId;
@@ -156,7 +156,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
156156
})
157157
}
158158

159-
fn coerce<E>(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
159+
fn coerce<E>(&self,
160+
exprs: &[E],
161+
a: Ty<'tcx>,
162+
b: Ty<'tcx>)
163+
-> CoerceResult<'tcx>
160164
where E: AsCoercionSite
161165
{
162166
let a = self.shallow_resolve(a);
@@ -689,11 +693,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
689693
pub fn try_coerce(&self,
690694
expr: &hir::Expr,
691695
expr_ty: Ty<'tcx>,
696+
expr_diverges: Diverges,
692697
target: Ty<'tcx>)
693698
-> RelateResult<'tcx, Ty<'tcx>> {
694699
let source = self.resolve_type_vars_with_obligations(expr_ty);
695700
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
696701

702+
// Special-ish case: we can coerce any type `T` into the `!`
703+
// type, but only if the source expression diverges.
704+
if target.is_never() && expr_diverges.always() {
705+
debug!("permit coercion to `!` because expr diverges");
706+
return Ok(target);
707+
}
708+
697709
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
698710
let coerce = Coerce::new(self, cause);
699711
self.commit_if_ok(|_| {
@@ -721,15 +733,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
721733
exprs: &[E],
722734
prev_ty: Ty<'tcx>,
723735
new: &hir::Expr,
724-
new_ty: Ty<'tcx>)
736+
new_ty: Ty<'tcx>,
737+
new_diverges: Diverges)
725738
-> RelateResult<'tcx, Ty<'tcx>>
726739
where E: AsCoercionSite
727740
{
728-
729741
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
730742
let new_ty = self.resolve_type_vars_with_obligations(new_ty);
731743
debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
732744

745+
// Special-ish case: we can coerce any type `T` into the `!`
746+
// type, but only if the source expression diverges.
747+
if prev_ty.is_never() && new_diverges.always() {
748+
debug!("permit coercion to `!` because expr diverges");
749+
return Ok(prev_ty);
750+
}
751+
733752
let trace = TypeTrace::types(cause, true, prev_ty, new_ty);
734753

735754
// Special-case that coercion alone cannot handle:
@@ -982,9 +1001,10 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
9821001
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
9831002
cause: &ObligationCause<'tcx>,
9841003
expression: &'gcx hir::Expr,
985-
expression_ty: Ty<'tcx>)
1004+
expression_ty: Ty<'tcx>,
1005+
expression_diverges: Diverges)
9861006
{
987-
self.coerce_inner(fcx, cause, Some(expression), expression_ty)
1007+
self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges)
9881008
}
9891009

9901010
/// 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>
10021022
self.coerce_inner(fcx,
10031023
cause,
10041024
None,
1005-
fcx.tcx.mk_nil())
1025+
fcx.tcx.mk_nil(),
1026+
Diverges::Maybe)
10061027
}
10071028

10081029
/// The inner coercion "engine". If `expression` is `None`, this
@@ -1012,7 +1033,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
10121033
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
10131034
cause: &ObligationCause<'tcx>,
10141035
expression: Option<&'gcx hir::Expr>,
1015-
mut expression_ty: Ty<'tcx>)
1036+
mut expression_ty: Ty<'tcx>,
1037+
expression_diverges: Diverges)
10161038
{
10171039
// Incorporate whatever type inference information we have
10181040
// until now; in principle we might also want to process
@@ -1035,21 +1057,23 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
10351057
if self.pushed == 0 {
10361058
// Special-case the first expression we are coercing.
10371059
// To be honest, I'm not entirely sure why we do this.
1038-
fcx.try_coerce(expression, expression_ty, self.expected_ty)
1060+
fcx.try_coerce(expression, expression_ty, expression_diverges, self.expected_ty)
10391061
} else {
10401062
match self.expressions {
10411063
Expressions::Dynamic(ref exprs) =>
10421064
fcx.try_find_coercion_lub(cause,
10431065
exprs,
10441066
self.merged_ty(),
10451067
expression,
1046-
expression_ty),
1068+
expression_ty,
1069+
expression_diverges),
10471070
Expressions::UpFront(ref coercion_sites) =>
10481071
fcx.try_find_coercion_lub(cause,
10491072
&coercion_sites[0..self.pushed],
10501073
self.merged_ty(),
10511074
expression,
1052-
expression_ty),
1075+
expression_ty,
1076+
expression_diverges),
10531077
}
10541078
}
10551079
} else {

src/librustc_typeck/check/demand.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
7777
expected: Ty<'tcx>) {
7878
let expected = self.resolve_type_vars_with_obligations(expected);
7979

80-
// If we are "assigning" to a `!` location, then we can permit
81-
// any type to be assigned there, so long as we are in
82-
// dead-code. This applies to e.g. `fn foo() -> ! { return; 22
83-
// }` but also `fn foo() { let x: ! = { return; 22 }; }`.
84-
//
85-
// You might imagine that we could just *always* bail if we
86-
// are in dead-code, but we don't want to do that, because
87-
// that leaves a lot of type variables unconstrained. See
88-
// e.g. #39808 and #39984.
89-
let in_dead_code = self.diverges.get().always();
90-
if expected.is_never() && in_dead_code {
91-
return;
92-
}
93-
94-
if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
80+
if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
9581
let cause = self.misc(expr.span);
9682
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
9783
let mode = probe::Mode::MethodCall;

src/librustc_typeck/check/mod.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ impl UnsafetyState {
366366
/// as diverging), with some manual adjustments for control-flow
367367
/// primitives (approximating a CFG).
368368
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
369-
enum Diverges {
369+
pub enum Diverges {
370370
/// Potentially unknown, some cases converge,
371371
/// others require a CFG to determine them.
372372
Maybe,
@@ -2833,7 +2833,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28332833
.coerce(self,
28342834
&self.misc(return_expr.span),
28352835
return_expr,
2836-
return_expr_ty);
2836+
return_expr_ty,
2837+
self.diverges.get());
28372838
}
28382839

28392840

@@ -2864,13 +2865,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28642865
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
28652866

28662867
let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
2867-
coerce.coerce(self, &if_cause, then_expr, then_ty);
2868+
coerce.coerce(self, &if_cause, then_expr, then_ty, then_diverges);
28682869

28692870
if let Some(else_expr) = opt_else_expr {
28702871
let else_ty = self.check_expr_with_expectation(else_expr, expected);
28712872
let else_diverges = self.diverges.get();
28722873

2873-
coerce.coerce(self, &if_cause, else_expr, else_ty);
2874+
coerce.coerce(self, &if_cause, else_expr, else_ty, else_diverges);
28742875

28752876
// We won't diverge unless both branches do (or the condition does).
28762877
self.diverges.set(cond_diverges | then_diverges & else_diverges);
@@ -3553,7 +3554,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35533554
}
35543555
hir::ExprBreak(destination, ref expr_opt) => {
35553556
if let Some(target_id) = destination.target_id.opt_id() {
3556-
let (e_ty, cause);
3557+
let (e_ty, e_diverges, cause);
35573558
if let Some(ref e) = *expr_opt {
35583559
// If this is a break with a value, we need to type-check
35593560
// the expression. Get an expected type from the loop context.
@@ -3572,11 +3573,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35723573

35733574
// Recurse without `enclosing_breakables` borrowed.
35743575
e_ty = self.check_expr_with_hint(e, coerce_to);
3576+
e_diverges = self.diverges.get();
35753577
cause = self.misc(e.span);
35763578
} else {
35773579
// Otherwise, this is a break *without* a value. That's
35783580
// always legal, and is equivalent to `break ()`.
35793581
e_ty = tcx.mk_nil();
3582+
e_diverges = Diverges::Maybe;
35803583
cause = self.misc(expr.span);
35813584
}
35823585

@@ -3587,7 +3590,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35873590
let ctxt = enclosing_breakables.find_breakable(target_id);
35883591
if let Some(ref mut coerce) = ctxt.coerce {
35893592
if let Some(ref e) = *expr_opt {
3590-
coerce.coerce(self, &cause, e, e_ty);
3593+
coerce.coerce(self, &cause, e, e_ty, e_diverges);
35913594
} else {
35923595
assert!(e_ty.is_nil());
35933596
coerce.coerce_forced_unit(self, &cause);
@@ -3769,10 +3772,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
37693772
let coerce_to = uty.unwrap_or_else(
37703773
|| self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
37713774
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
3775+
assert_eq!(self.diverges.get(), Diverges::Maybe);
37723776
for e in args {
37733777
let e_ty = self.check_expr_with_hint(e, coerce_to);
37743778
let cause = self.misc(e.span);
3775-
coerce.coerce(self, &cause, e, e_ty);
3779+
coerce.coerce(self, &cause, e, e_ty, self.diverges.get());
37763780
}
37773781
coerce.complete(self)
37783782
} else {

0 commit comments

Comments
 (0)