Skip to content

Commit 1ae620b

Browse files
committed
do not eagerly convert ! to a diverging variable
Instead, wait until coercion time. This has some small effects on a few tests (one less temporary, generally better errors when trying to call methods or otherwise "force" the type).
1 parent 4967f1a commit 1ae620b

File tree

8 files changed

+71
-47
lines changed

8 files changed

+71
-47
lines changed

src/librustc_typeck/check/coercion.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,23 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
169169
}
170170

171171
if a.is_never() {
172-
return success(Adjust::NeverToAny, b, vec![]);
172+
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
173+
// type variable, we want `?T` to fallback to `!` if not
174+
// otherwise constrained. An example where this arises:
175+
//
176+
// let _: Option<?T> = Some({ return; });
177+
//
178+
// here, we would coerce from `!` to `?T`.
179+
let b = self.shallow_resolve(b);
180+
return if self.shallow_resolve(b).is_ty_var() {
181+
// micro-optimization: no need for this if `b` is
182+
// already resolved in some way.
183+
let diverging_ty = self.next_diverging_ty_var(
184+
TypeVariableOrigin::AdjustmentType(self.cause.span));
185+
self.unify_and(&b, &diverging_ty, Adjust::NeverToAny)
186+
} else {
187+
success(Adjust::NeverToAny, b, vec![])
188+
};
173189
}
174190

175191
// Consider coercing the subtype to a DST
@@ -687,11 +703,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
687703
let adjustment = self.register_infer_ok_obligations(ok);
688704
if !adjustment.is_identity() {
689705
debug!("Success, coerced with {:?}", adjustment);
690-
match self.tables.borrow().adjustments.get(&expr.id) {
691-
None |
692-
Some(&Adjustment { kind: Adjust::NeverToAny, .. }) => (),
693-
_ => bug!("expr already has an adjustment on it!"),
694-
};
706+
if self.tables.borrow().adjustments.get(&expr.id).is_some() {
707+
bug!("expr already has an adjustment on it!");
708+
}
695709
self.write_adjustment(expr.id, adjustment);
696710
}
697711
Ok(adjustment.target)

src/librustc_typeck/check/mod.rs

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,7 +2664,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26642664
pub fn check_expr_has_type(&self,
26652665
expr: &'gcx hir::Expr,
26662666
expected: Ty<'tcx>) -> Ty<'tcx> {
2667-
let ty = self.check_expr_with_hint(expr, expected);
2667+
let mut ty = self.check_expr_with_hint(expr, expected);
2668+
2669+
// While we don't allow *arbitrary* coercions here, we *do* allow
2670+
// coercions from ! to `expected`.
2671+
if ty.is_never() {
2672+
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id),
2673+
"expression with never type wound up being adjusted");
2674+
let adj_ty = self.next_diverging_ty_var(
2675+
TypeVariableOrigin::AdjustmentType(expr.span));
2676+
self.write_adjustment(expr.id, adjustment::Adjustment {
2677+
kind: adjustment::Adjust::NeverToAny,
2678+
target: adj_ty
2679+
});
2680+
ty = adj_ty;
2681+
}
2682+
26682683
self.demand_suptype(expr.span, expected, ty);
26692684
ty
26702685
}
@@ -3370,18 +3385,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
33703385
debug!("type of {} is...", self.tcx.hir.node_to_string(expr.id));
33713386
debug!("... {:?}, expected is {:?}", ty, expected);
33723387

3373-
// Add adjustments to !-expressions
3374-
if ty.is_never() {
3375-
if let Some(hir::map::NodeExpr(node_expr)) = self.tcx.hir.find(expr.id) {
3376-
let adj_ty = self.next_diverging_ty_var(
3377-
TypeVariableOrigin::AdjustmentType(node_expr.span));
3378-
self.write_adjustment(expr.id, adjustment::Adjustment {
3379-
kind: adjustment::Adjust::NeverToAny,
3380-
target: adj_ty
3381-
});
3382-
return adj_ty;
3383-
}
3384-
}
33853388
ty
33863389
}
33873390

@@ -4072,7 +4075,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
40724075
pub fn check_block_no_value(&self, blk: &'gcx hir::Block) {
40734076
let unit = self.tcx.mk_nil();
40744077
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
4075-
self.demand_suptype(blk.span, unit, ty);
4078+
4079+
// if the block produces a `!` value, that can always be
4080+
// (effectively) coerced to unit.
4081+
if !ty.is_never() {
4082+
self.demand_suptype(blk.span, unit, ty);
4083+
}
40764084
}
40774085

40784086
fn check_block_with_expected(&self,
@@ -4111,7 +4119,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41114119
},
41124120
None => {
41134121
e_ty = if self.diverges.get().always() {
4114-
self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span))
4122+
self.tcx.types.never
41154123
} else {
41164124
self.tcx.mk_nil()
41174125
};
@@ -4135,6 +4143,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41354143
Err(err) =>
41364144
self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
41374145
}
4146+
} else if self.diverges.get().always() {
4147+
// No tail expression and the body diverges; ignore
4148+
// the expected type, and keep `!` as the type of the
4149+
// block.
41384150
} else {
41394151
self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
41404152
};
@@ -4147,33 +4159,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41474159

41484160
let mut ty = match blk.expr {
41494161
Some(ref e) => self.check_expr_with_expectation(e, expected),
4150-
None => self.tcx.mk_nil()
4162+
None => if self.diverges.get().always() {
4163+
self.tcx.types.never
4164+
} else {
4165+
self.tcx.mk_nil()
4166+
},
41514167
};
41524168

4153-
if self.diverges.get().always() {
4154-
if let ExpectHasType(ety) = expected {
4155-
// Avoid forcing a type (only `!` for now) in unreachable code.
4156-
// FIXME(aburka) do we need this special case? and should it be is_uninhabited?
4157-
if !ety.is_never() {
4158-
if let Some(ref e) = blk.expr {
4159-
// Coerce the tail expression to the right type.
4160-
self.demand_coerce(e, ty, ety);
4161-
}
4162-
}
4163-
}
4164-
4165-
ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
4166-
} else if let ExpectHasType(ety) = expected {
4169+
if let ExpectHasType(ety) = expected {
41674170
if let Some(ref e) = blk.expr {
41684171
// Coerce the tail expression to the right type.
41694172
self.demand_coerce(e, ty, ety);
4173+
4174+
// We already applied the type (and potentially errored),
4175+
// use the expected type to avoid further errors out.
4176+
ty = ety;
4177+
} else if self.diverges.get().always() {
4178+
// No tail expression and the body diverges; ignore
4179+
// the expected type, and keep `!` as the type of the
4180+
// block.
41704181
} else {
41714182
self.check_block_no_expr(blk, ty, ety);
4172-
}
41734183

4174-
// We already applied the type (and potentially errored),
4175-
// use the expected type to avoid further errors out.
4176-
ty = ety;
4184+
// We already applied the type (and potentially errored),
4185+
// use the expected type to avoid further errors out.
4186+
ty = ety;
4187+
}
41774188
}
41784189
ty
41794190
};

src/test/compile-fail/index-bot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
(return)[0]; //~ ERROR the type of this value must be known in this context
12+
(return)[0]; //~ ERROR cannot index a value of type `!`
1313
}

src/test/compile-fail/issue-13847.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
return.is_failure //~ ERROR the type of this value must be known in this context
12+
return.is_failure //~ ERROR no field `is_failure` on type `!`
1313
}

src/test/compile-fail/issue-15207.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
fn main() {
1212
loop {
13-
break.push(1) //~ ERROR the type of this value must be known in this context
13+
break.push(1) //~ ERROR no method named `push` found for type `!`
1414
;
1515
}
1616
}

src/test/compile-fail/issue-15965.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
fn main() {
1212
return
1313
{ return () }
14-
//~^ ERROR the type of this value must be known in this context
14+
//~^ ERROR expected function, found `!`
1515
()
1616
;
1717
}

src/test/compile-fail/issue-17373.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
*return //~ ERROR the type of this value must be known in this context
12+
*return //~ ERROR type `!` cannot be dereferenced
1313
;
1414
}

src/test/compile-fail/issue-18532.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,5 @@
1313
// into it.
1414

1515
fn main() {
16-
(return)((),());
17-
//~^ ERROR the type of this value must be known
16+
(return)((),()); //~ ERROR expected function, found `!`
1817
}

0 commit comments

Comments
 (0)