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:
parent
fe7bf993aa
commit
b60970fd20
@ -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 @@ fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Sel
|
||||
body: db.body(owner),
|
||||
resolver,
|
||||
diverges: Diverges::Maybe,
|
||||
breakables: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,10 @@
|
||||
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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
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 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
// 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 } => {
|
||||
|
@ -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 ()
|
||||
"###);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user