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).
This commit is contained in:
parent
4967f1ae57
commit
1ae620bbeb
@ -169,7 +169,23 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
if a.is_never() {
|
||||
return success(Adjust::NeverToAny, b, vec![]);
|
||||
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
|
||||
// type variable, we want `?T` to fallback to `!` if not
|
||||
// otherwise constrained. An example where this arises:
|
||||
//
|
||||
// let _: Option<?T> = Some({ return; });
|
||||
//
|
||||
// here, we would coerce from `!` to `?T`.
|
||||
let b = self.shallow_resolve(b);
|
||||
return if self.shallow_resolve(b).is_ty_var() {
|
||||
// micro-optimization: no need for this if `b` is
|
||||
// already resolved in some way.
|
||||
let diverging_ty = self.next_diverging_ty_var(
|
||||
TypeVariableOrigin::AdjustmentType(self.cause.span));
|
||||
self.unify_and(&b, &diverging_ty, Adjust::NeverToAny)
|
||||
} else {
|
||||
success(Adjust::NeverToAny, b, vec![])
|
||||
};
|
||||
}
|
||||
|
||||
// Consider coercing the subtype to a DST
|
||||
@ -687,11 +703,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
let adjustment = self.register_infer_ok_obligations(ok);
|
||||
if !adjustment.is_identity() {
|
||||
debug!("Success, coerced with {:?}", adjustment);
|
||||
match self.tables.borrow().adjustments.get(&expr.id) {
|
||||
None |
|
||||
Some(&Adjustment { kind: Adjust::NeverToAny, .. }) => (),
|
||||
_ => bug!("expr already has an adjustment on it!"),
|
||||
};
|
||||
if self.tables.borrow().adjustments.get(&expr.id).is_some() {
|
||||
bug!("expr already has an adjustment on it!");
|
||||
}
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
}
|
||||
Ok(adjustment.target)
|
||||
|
@ -2664,7 +2664,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn check_expr_has_type(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
expected: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty = self.check_expr_with_hint(expr, expected);
|
||||
let mut ty = self.check_expr_with_hint(expr, expected);
|
||||
|
||||
// While we don't allow *arbitrary* coercions here, we *do* allow
|
||||
// coercions from ! to `expected`.
|
||||
if ty.is_never() {
|
||||
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id),
|
||||
"expression with never type wound up being adjusted");
|
||||
let adj_ty = self.next_diverging_ty_var(
|
||||
TypeVariableOrigin::AdjustmentType(expr.span));
|
||||
self.write_adjustment(expr.id, adjustment::Adjustment {
|
||||
kind: adjustment::Adjust::NeverToAny,
|
||||
target: adj_ty
|
||||
});
|
||||
ty = adj_ty;
|
||||
}
|
||||
|
||||
self.demand_suptype(expr.span, expected, ty);
|
||||
ty
|
||||
}
|
||||
@ -3370,18 +3385,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
debug!("type of {} is...", self.tcx.hir.node_to_string(expr.id));
|
||||
debug!("... {:?}, expected is {:?}", ty, expected);
|
||||
|
||||
// Add adjustments to !-expressions
|
||||
if ty.is_never() {
|
||||
if let Some(hir::map::NodeExpr(node_expr)) = self.tcx.hir.find(expr.id) {
|
||||
let adj_ty = self.next_diverging_ty_var(
|
||||
TypeVariableOrigin::AdjustmentType(node_expr.span));
|
||||
self.write_adjustment(expr.id, adjustment::Adjustment {
|
||||
kind: adjustment::Adjust::NeverToAny,
|
||||
target: adj_ty
|
||||
});
|
||||
return adj_ty;
|
||||
}
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
@ -4072,7 +4075,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn check_block_no_value(&self, blk: &'gcx hir::Block) {
|
||||
let unit = self.tcx.mk_nil();
|
||||
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
|
||||
self.demand_suptype(blk.span, unit, ty);
|
||||
|
||||
// if the block produces a `!` value, that can always be
|
||||
// (effectively) coerced to unit.
|
||||
if !ty.is_never() {
|
||||
self.demand_suptype(blk.span, unit, ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block_with_expected(&self,
|
||||
@ -4111,7 +4119,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
},
|
||||
None => {
|
||||
e_ty = if self.diverges.get().always() {
|
||||
self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span))
|
||||
self.tcx.types.never
|
||||
} else {
|
||||
self.tcx.mk_nil()
|
||||
};
|
||||
@ -4135,6 +4143,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
Err(err) =>
|
||||
self.report_mismatched_types(&cause, ctxt.unified, e_ty, err).emit(),
|
||||
}
|
||||
} else if self.diverges.get().always() {
|
||||
// No tail expression and the body diverges; ignore
|
||||
// the expected type, and keep `!` as the type of the
|
||||
// block.
|
||||
} else {
|
||||
self.check_block_no_expr(blk, self.tcx.mk_nil(), e_ty);
|
||||
};
|
||||
@ -4147,33 +4159,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
let mut ty = match blk.expr {
|
||||
Some(ref e) => self.check_expr_with_expectation(e, expected),
|
||||
None => self.tcx.mk_nil()
|
||||
None => if self.diverges.get().always() {
|
||||
self.tcx.types.never
|
||||
} else {
|
||||
self.tcx.mk_nil()
|
||||
},
|
||||
};
|
||||
|
||||
if self.diverges.get().always() {
|
||||
if let ExpectHasType(ety) = expected {
|
||||
// Avoid forcing a type (only `!` for now) in unreachable code.
|
||||
// FIXME(aburka) do we need this special case? and should it be is_uninhabited?
|
||||
if !ety.is_never() {
|
||||
if let Some(ref e) = blk.expr {
|
||||
// Coerce the tail expression to the right type.
|
||||
self.demand_coerce(e, ty, ety);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty = self.next_diverging_ty_var(TypeVariableOrigin::DivergingBlockExpr(blk.span));
|
||||
} else if let ExpectHasType(ety) = expected {
|
||||
if let ExpectHasType(ety) = expected {
|
||||
if let Some(ref e) = blk.expr {
|
||||
// Coerce the tail expression to the right type.
|
||||
self.demand_coerce(e, ty, ety);
|
||||
|
||||
// We already applied the type (and potentially errored),
|
||||
// use the expected type to avoid further errors out.
|
||||
ty = ety;
|
||||
} else if self.diverges.get().always() {
|
||||
// No tail expression and the body diverges; ignore
|
||||
// the expected type, and keep `!` as the type of the
|
||||
// block.
|
||||
} else {
|
||||
self.check_block_no_expr(blk, ty, ety);
|
||||
}
|
||||
|
||||
// We already applied the type (and potentially errored),
|
||||
// use the expected type to avoid further errors out.
|
||||
ty = ety;
|
||||
// We already applied the type (and potentially errored),
|
||||
// use the expected type to avoid further errors out.
|
||||
ty = ety;
|
||||
}
|
||||
}
|
||||
ty
|
||||
};
|
||||
|
@ -9,5 +9,5 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
(return)[0]; //~ ERROR the type of this value must be known in this context
|
||||
(return)[0]; //~ ERROR cannot index a value of type `!`
|
||||
}
|
||||
|
@ -9,5 +9,5 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
return.is_failure //~ ERROR the type of this value must be known in this context
|
||||
return.is_failure //~ ERROR no field `is_failure` on type `!`
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
break.push(1) //~ ERROR the type of this value must be known in this context
|
||||
break.push(1) //~ ERROR no method named `push` found for type `!`
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
fn main() {
|
||||
return
|
||||
{ return () }
|
||||
//~^ ERROR the type of this value must be known in this context
|
||||
//~^ ERROR expected function, found `!`
|
||||
()
|
||||
;
|
||||
}
|
||||
|
@ -9,6 +9,6 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main() {
|
||||
*return //~ ERROR the type of this value must be known in this context
|
||||
*return //~ ERROR type `!` cannot be dereferenced
|
||||
;
|
||||
}
|
||||
|
@ -13,6 +13,5 @@
|
||||
// into it.
|
||||
|
||||
fn main() {
|
||||
(return)((),());
|
||||
//~^ ERROR the type of this value must be known
|
||||
(return)((),()); //~ ERROR expected function, found `!`
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user