Use CoerceMany in BreakableContext

This commit is contained in:
Lukas Wirth 2021-07-09 14:56:49 +02:00
parent e968d834ca
commit 9272942b92
3 changed files with 26 additions and 36 deletions

View file

@ -35,9 +35,9 @@ use stdx::impl_from;
use syntax::SmolStr;
use crate::{
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, Substitution, TraitEnvironment,
TraitRef, Ty, TyBuilder, TyExt, TyKind,
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
to_assoc_type_id, AliasEq, AliasTy, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy,
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
// This lint has a false positive here. See the link below for details.
@ -349,7 +349,7 @@ struct InferenceContext<'a> {
#[derive(Clone, Debug)]
struct BreakableContext {
may_break: bool,
break_ty: Ty,
coerce: CoerceMany,
label: Option<name::Name>,
}

View file

@ -48,17 +48,6 @@ impl CoerceMany {
CoerceMany { expected_ty: expected }
}
pub(super) fn once(
ctx: &mut InferenceContext<'_>,
expected: Ty,
expr: Option<ExprId>,
expr_ty: &Ty,
) -> Ty {
let mut this = CoerceMany::new(expected);
this.coerce(ctx, expr, expr_ty);
this.complete()
}
/// Merge two types from different branches, with possible coercion.
///
/// Mostly this means trying to coerce one to the other, but

View file

@ -163,7 +163,7 @@ impl<'a> InferenceContext<'a> {
let break_ty = self.table.new_type_var();
self.breakables.push(BreakableContext {
may_break: false,
break_ty: break_ty.clone(),
coerce: CoerceMany::new(break_ty.clone()),
label: label.map(|label| self.body[label].name.clone()),
});
let ty = self.infer_block(
@ -174,7 +174,7 @@ impl<'a> InferenceContext<'a> {
);
let ctxt = self.breakables.pop().expect("breakable stack broken");
if ctxt.may_break {
ctxt.break_ty
ctxt.coerce.complete()
} else {
ty
}
@ -202,18 +202,16 @@ impl<'a> InferenceContext<'a> {
Expr::Loop { body, label } => {
self.breakables.push(BreakableContext {
may_break: false,
break_ty: self.table.new_type_var(),
coerce: CoerceMany::new(self.table.new_type_var()),
label: label.map(|label| self.body[label].name.clone()),
});
self.infer_expr(*body, &Expectation::has_type(TyBuilder::unit()));
let ctxt = self.breakables.pop().expect("breakable stack broken");
if ctxt.may_break {
self.diverges = Diverges::Maybe;
}
if ctxt.may_break {
ctxt.break_ty
self.diverges = Diverges::Maybe;
ctxt.coerce.complete()
} else {
TyKind::Never.intern(&Interner)
}
@ -221,8 +219,7 @@ impl<'a> InferenceContext<'a> {
Expr::While { condition, body, label } => {
self.breakables.push(BreakableContext {
may_break: false,
break_ty: self.err_ty(),
coerce: CoerceMany::new(self.err_ty()),
label: label.map(|label| self.body[label].name.clone()),
});
// while let is desugared to a match loop, so this is always simple while
@ -241,7 +238,7 @@ impl<'a> InferenceContext<'a> {
self.breakables.push(BreakableContext {
may_break: false,
break_ty: self.err_ty(),
coerce: CoerceMany::new(self.err_ty()),
label: label.map(|label| self.body[label].name.clone()),
});
let pat_ty =
@ -383,31 +380,35 @@ impl<'a> InferenceContext<'a> {
}
Expr::Continue { .. } => TyKind::Never.intern(&Interner),
Expr::Break { expr, label } => {
let expr = *expr;
let last_ty =
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
ctxt.break_ty.clone()
} else {
self.err_ty()
let mut coerce = match find_breakable(&mut self.breakables, label.as_ref()) {
Some(ctxt) => {
// avoiding the borrowck
mem::replace(
&mut ctxt.coerce,
CoerceMany::new(self.result.standard_types.unknown.clone()),
)
}
None => CoerceMany::new(self.result.standard_types.unknown.clone()),
};
let val_ty = if let Some(expr) = expr {
let val_ty = if let Some(expr) = *expr {
self.infer_expr(expr, &Expectation::none())
} else {
TyBuilder::unit()
};
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
let merged_type = CoerceMany::once(self, last_ty, expr, &val_ty);
coerce.coerce(self, *expr, &val_ty);
if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
ctxt.break_ty = merged_type;
ctxt.coerce = coerce;
ctxt.may_break = true;
} else {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
});
}
};
TyKind::Never.intern(&Interner)
}
Expr::Return { expr } => {