Skip to content

Commit cbfccd0

Browse files
committed
change the strategy for diverging types
The new strategy is as follows. First, the `!` type is assigned in two cases: - a block with a diverging statement and no tail expression (e.g., `{return;}`); - any expression with the type `!` is considered diverging. Second, we track when we are in a diverging state, and we permit a value of any type to be coerced **into** `!` if the expression that produced it is diverging. This means that `fn foo() -> ! { panic!(); 22 }` type-checks, even though the block has a type of `usize`. Finally, coercions **from** the `!` type to any other are always permitted. Fixes rust-lang#39808.
1 parent 0d42329 commit cbfccd0

File tree

5 files changed

+66
-17
lines changed

5 files changed

+66
-17
lines changed

src/librustc_typeck/check/demand.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
6767
}
6868

6969
// Checks that the type of `expr` can be coerced to `expected`.
70-
pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
70+
//
71+
// NB: This code relies on `self.diverges` to be accurate. In
72+
// particular, assignments to `!` will be permitted if the
73+
// diverges flag is currently "always".
74+
pub fn demand_coerce(&self,
75+
expr: &hir::Expr,
76+
checked_ty: Ty<'tcx>,
77+
expected: Ty<'tcx>) {
7178
let expected = self.resolve_type_vars_with_obligations(expected);
79+
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+
7294
if let Err(e) = self.try_coerce(expr, checked_ty, expected) {
7395
let cause = self.misc(expr.span);
7496
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);

src/librustc_typeck/check/mod.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,18 +1533,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
15331533
#[inline]
15341534
pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) {
15351535
debug!("write_ty({}, {:?}) in fcx {}",
1536-
node_id, ty, self.tag());
1536+
node_id, self.resolve_type_vars_if_possible(&ty), self.tag());
15371537
self.tables.borrow_mut().node_types.insert(node_id, ty);
15381538

15391539
if ty.references_error() {
15401540
self.has_errors.set(true);
15411541
self.set_tainted_by_errors();
15421542
}
1543-
1544-
// FIXME(canndrew): This is_never should probably be an is_uninhabited
1545-
if ty.is_never() || self.type_var_diverges(ty) {
1546-
self.diverges.set(self.diverges.get() | Diverges::Always);
1547-
}
15481543
}
15491544

15501545
pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
@@ -3269,6 +3264,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
32693264
_ => self.warn_if_unreachable(expr.id, expr.span, "expression")
32703265
}
32713266

3267+
// Any expression that produces a value of type `!` must have diverged
3268+
if ty.is_never() {
3269+
self.diverges.set(self.diverges.get() | Diverges::Always);
3270+
}
3271+
32723272
// Record the type, which applies it effects.
32733273
// We need to do this after the warning above, so that
32743274
// we don't warn for the diverging expression itself.
@@ -3952,7 +3952,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
39523952
self.diverges.set(Diverges::Maybe);
39533953
self.has_errors.set(false);
39543954

3955-
let (node_id, span) = match stmt.node {
3955+
let (node_id, _span) = match stmt.node {
39563956
hir::StmtDecl(ref decl, id) => {
39573957
let span = match decl.node {
39583958
hir::DeclLocal(ref l) => {
@@ -3978,9 +3978,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
39783978

39793979
if self.has_errors.get() {
39803980
self.write_error(node_id);
3981-
} else if self.diverges.get().always() {
3982-
self.write_ty(node_id, self.next_diverging_ty_var(
3983-
TypeVariableOrigin::DivergingStmt(span)));
39843981
} else {
39853982
self.write_nil(node_id);
39863983
}
@@ -4011,12 +4008,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
40114008

40124009
let mut ty = match blk.expr {
40134010
Some(ref e) => self.check_expr_with_expectation(e, expected),
4014-
None => self.tcx.mk_nil()
4011+
None => if self.diverges.get().always() {
4012+
self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span))
4013+
} else {
4014+
self.tcx.mk_nil()
4015+
},
40154016
};
40164017

4017-
if self.diverges.get().always() {
4018-
ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
4019-
} else if let ExpectHasType(ety) = expected {
4018+
if let ExpectHasType(ety) = expected {
40204019
if let Some(ref e) = blk.expr {
40214020
// Coerce the tail expression to the right type.
40224021
self.demand_coerce(e, ty, ety);

src/test/run-pass/inference-changes-39485.rs renamed to src/test/compile-fail/diverging-tuple-parts-39485.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// After #39485, this test used to pass, but that change was reverted
12+
// due to numerous inference failures like #39808, so it now fails
13+
// again. #39485 made it so that diverging types never propagate
14+
// upward; but we now do propagate such types upward in many more
15+
// cases.
16+
1117
fn g() {
12-
&panic!()
18+
&panic!() //~ ERROR mismatched types
1319
}
1420

1521
fn f() -> isize {
16-
(return 1, return 2)
22+
(return 1, return 2) //~ ERROR mismatched types
1723
}
1824

1925
fn main() {}

src/test/compile-fail/never-assign-wrong-type.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// Test that we can't use another type in place of !
1212

1313
#![feature(never_type)]
14+
#![deny(warnings)]
1415

1516
fn main() {
1617
let x: ! = "hello"; //~ ERROR mismatched types

src/test/run-pass/issue-39808.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test: even though `Ok` is dead-code, its type needs to
12+
// be influenced by the result of `Err` or else we get a "type
13+
// variable unconstrained" error.
14+
15+
fn main() {
16+
let _ = if false {
17+
Ok(return)
18+
} else {
19+
Err("")
20+
};
21+
}

0 commit comments

Comments
 (0)