]> git.lizzy.rs Git - rust.git/commitdiff
Handle break somewhat better
authorFlorian Diebold <florian.diebold@freiheit.com>
Fri, 8 May 2020 15:59:58 +0000 (17:59 +0200)
committerFlorian Diebold <florian.diebold@freiheit.com>
Fri, 8 May 2020 16:28:01 +0000 (18:28 +0200)
Still no break-with-value or labels, but at least we know that `loop { break; }`
doesn't diverge.

crates/ra_hir_ty/src/infer.rs
crates/ra_hir_ty/src/infer/expr.rs
crates/ra_hir_ty/src/tests/never_type.rs

index d3a06626863aa30782e9975d6ea8621cb388d8b0..413904518b144d176f75116b01ae78de4e6f13ad 100644 (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 @@ fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Sel
             body: db.body(owner),
             resolver,
             diverges: Diverges::Maybe,
+            breakables: Vec::new(),
         }
     }
 
index f2f9883b2d7ebe8bb5eb1f1ab6a26468a0c25522..9cac0c787c70db3cedad05a25981ce37c5ca38b6 100644 (file)
     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 } => {
index 1721f97c5812fe8e893804cb088420e30f490178..082c472088488f0db3c29a17e777c617457abe65 100644 (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 ()
+    "###);
 }