Handle break somewhat better

Still no break-with-value or labels, but at least we know that `loop { break; }`
doesn't diverge.
This commit is contained in:
Florian Diebold 2020-05-08 17:59:58 +02:00
parent fe7bf993aa
commit b60970fd20
3 changed files with 105 additions and 3 deletions

View file

@ -211,6 +211,12 @@ struct InferenceContext<'a> {
/// so it doesn't make sense.
return_ty: Ty,
diverges: Diverges,
breakables: Vec<BreakableContext>,
}
#[derive(Clone, Debug)]
struct BreakableContext {
pub may_break: bool,
}
impl<'a> InferenceContext<'a> {
@ -226,6 +232,7 @@ impl<'a> InferenceContext<'a> {
body: db.body(owner),
resolver,
diverges: Diverges::Maybe,
breakables: Vec::new(),
}
}

View file

@ -21,7 +21,10 @@ use crate::{
Ty, TypeCtor, Uncertain,
};
use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges};
use super::{
BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
TypeMismatch,
};
impl<'a> InferenceContext<'a> {
pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
@ -90,24 +93,43 @@ impl<'a> InferenceContext<'a> {
Ty::Unknown
}
Expr::Loop { body } => {
self.breakables.push(BreakableContext { may_break: false });
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
let ctxt = self.breakables.pop().expect("breakable stack broken");
if ctxt.may_break {
self.diverges = Diverges::Maybe;
}
// FIXME handle break with value
Ty::simple(TypeCtor::Never)
if ctxt.may_break {
Ty::unit()
} else {
Ty::simple(TypeCtor::Never)
}
}
Expr::While { condition, body } => {
self.breakables.push(BreakableContext { may_break: false });
// while let is desugared to a match loop, so this is always simple while
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
let _ctxt = self.breakables.pop().expect("breakable stack broken");
// the body may not run, so it diverging doesn't mean we diverge
self.diverges = Diverges::Maybe;
Ty::unit()
}
Expr::For { iterable, body, pat } => {
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
self.breakables.push(BreakableContext { may_break: false });
let pat_ty =
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
self.infer_pat(*pat, &pat_ty, BindingMode::default());
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
let _ctxt = self.breakables.pop().expect("breakable stack broken");
// the body may not run, so it diverging doesn't mean we diverge
self.diverges = Diverges::Maybe;
Ty::unit()
}
Expr::Lambda { body, args, ret_type, arg_types } => {
@ -211,6 +233,9 @@ impl<'a> InferenceContext<'a> {
// FIXME handle break with value
self.infer_expr(*expr, &Expectation::none());
}
if let Some(ctxt) = self.breakables.last_mut() {
ctxt.may_break = true;
}
Ty::simple(TypeCtor::Never)
}
Expr::Return { expr } => {

View file

@ -361,8 +361,78 @@ fn test1() {
// should give type mismatch
let x: u32 = { loop { break; } };
}
fn test2() {
// should give type mismatch
let x: u32 = { for a in b { break; }; };
// should give type mismatch as well
let x: u32 = { for a in b {}; };
// should give type mismatch as well
let x: u32 = { for a in b { return; }; };
}
fn test3() {
// should give type mismatch
let x: u32 = { while true { break; }; };
// should give type mismatch as well -- there's an implicit break, even if it's never hit
let x: u32 = { while true {}; };
// should give type mismatch as well
let x: u32 = { while true { return; }; };
}
"#,
true,
);
assert_snapshot!(t, @r###""###);
assert_snapshot!(t, @r###"
25..99 '{ ...} }; }': ()
68..69 'x': u32
77..96 '{ loop...k; } }': ()
79..94 'loop { break; }': ()
84..94 '{ break; }': ()
86..91 'break': !
77..96: expected u32, got ()
79..94: expected u32, got ()
111..357 '{ ...; }; }': ()
154..155 'x': u32
163..189 '{ for ...; }; }': ()
165..186 'for a ...eak; }': ()
169..170 'a': {unknown}
174..175 'b': {unknown}
176..186 '{ break; }': ()
178..183 'break': !
240..241 'x': u32
249..267 '{ for ... {}; }': ()
251..264 'for a in b {}': ()
255..256 'a': {unknown}
260..261 'b': {unknown}
262..264 '{}': ()
318..319 'x': u32
327..354 '{ for ...; }; }': ()
329..351 'for a ...urn; }': ()
333..334 'a': {unknown}
338..339 'b': {unknown}
340..351 '{ return; }': ()
342..348 'return': !
163..189: expected u32, got ()
249..267: expected u32, got ()
327..354: expected u32, got ()
369..668 '{ ...; }; }': ()
412..413 'x': u32
421..447 '{ whil...; }; }': ()
423..444 'while ...eak; }': ()
429..433 'true': bool
434..444 '{ break; }': ()
436..441 'break': !
551..552 'x': u32
560..578 '{ whil... {}; }': ()
562..575 'while true {}': ()
568..572 'true': bool
573..575 '{}': ()
629..630 'x': u32
638..665 '{ whil...; }; }': ()
640..662 'while ...urn; }': ()
646..650 'true': bool
651..662 '{ return; }': ()
653..659 'return': !
421..447: expected u32, got ()
560..578: expected u32, got ()
638..665: expected u32, got ()
"###);
}