diff --git a/compiler/qsc_frontend/src/typeck/rules.rs b/compiler/qsc_frontend/src/typeck/rules.rs index ff9da3bc3b..e809c172f3 100644 --- a/compiler/qsc_frontend/src/typeck/rules.rs +++ b/compiler/qsc_frontend/src/typeck/rules.rs @@ -254,7 +254,9 @@ impl<'a> Context<'a> { self.diverge_if(callee.diverges || input.diverges, converge(output_ty)) } ExprKind::Conjugate(within, apply) => { + let within_span = within.span; let within = self.infer_block(within); + self.inferrer.eq(within_span, Ty::UNIT, within.ty); let apply = self.infer_block(apply); self.diverge_if(within.diverges, apply) } @@ -288,7 +290,9 @@ impl<'a> Context<'a> { item: item_ty, }, ); + let body_span = body.span; let body = self.infer_block(body); + self.inferrer.eq(body_span, Ty::UNIT, body.ty); self.diverge_if(container.diverges || body.diverges, converge(Ty::UNIT)) } ExprKind::If(cond, if_true, if_false) => { @@ -413,13 +417,21 @@ impl<'a> Context<'a> { self.diverge_if(diverges, converge(Ty::Prim(ty))) } ExprKind::Repeat(body, until, fixup) => { + let body_span = body.span; let body = self.infer_block(body); + self.inferrer.eq(body_span, Ty::UNIT, body.ty); let until_span = until.span; let until = self.infer_expr(until); self.inferrer.eq(until_span, Ty::Prim(Prim::Bool), until.ty); - let fixup_diverges = fixup - .as_ref() - .map_or(false, |f| self.infer_block(f).diverges); + let fixup_diverges = match fixup { + None => false, + Some(f) => { + let f_span = f.span; + let f = self.infer_block(f); + self.inferrer.eq(f_span, Ty::UNIT, f.ty); + f.diverges + } + }; self.diverge_if( body.diverges || until.diverges || fixup_diverges, converge(Ty::UNIT), @@ -511,7 +523,9 @@ impl<'a> Context<'a> { let cond_span = cond.span; let cond = self.infer_expr(cond); self.inferrer.eq(cond_span, Ty::Prim(Prim::Bool), cond.ty); + let body_span = body.span; let body = self.infer_block(body); + self.inferrer.eq(body_span, Ty::UNIT, body.ty); self.diverge_if(cond.diverges || body.diverges, converge(Ty::UNIT)) } ExprKind::Hole => { diff --git a/compiler/qsc_frontend/src/typeck/tests.rs b/compiler/qsc_frontend/src/typeck/tests.rs index 44761cf930..9b3515e2ac 100644 --- a/compiler/qsc_frontend/src/typeck/tests.rs +++ b/compiler/qsc_frontend/src/typeck/tests.rs @@ -887,6 +887,70 @@ fn for_loop_not_iterable() { ); } +#[test] +fn for_loop_body_should_be_unit_error() { + check( + "", + "for i in [1, 2, 3] { 4 }", + &expect![[r##" + #1 0-24 "for i in [1, 2, 3] { 4 }" : Unit + #2 4-5 "i" : Int + #4 9-18 "[1, 2, 3]" : Int[] + #5 10-11 "1" : Int + #6 13-14 "2" : Int + #7 16-17 "3" : Int + #8 19-24 "{ 4 }" : Int + #10 21-22 "4" : Int + Error(Type(Error(TyMismatch("Unit", "Int", Span { lo: 19, hi: 24 })))) + "##]], + ); +} + +#[test] +fn repeat_loop_non_bool_condition_error() { + check( + "", + "repeat { } until 1", + &expect![[r##" + #1 0-18 "repeat { } until 1" : Unit + #2 7-10 "{ }" : Unit + #3 17-18 "1" : Int + Error(Type(Error(TyMismatch("Bool", "Int", Span { lo: 17, hi: 18 })))) + "##]], + ); +} + +#[test] +fn repeat_loop_body_should_be_unit_error() { + check( + "", + "repeat { 1 } until false", + &expect![[r##" + #1 0-24 "repeat { 1 } until false" : Unit + #2 7-12 "{ 1 }" : Int + #4 9-10 "1" : Int + #5 19-24 "false" : Bool + Error(Type(Error(TyMismatch("Unit", "Int", Span { lo: 7, hi: 12 })))) + "##]], + ); +} + +#[test] +fn repeat_loop_fixup_should_be_unit_error() { + check( + "", + "repeat { } until false fixup { 1 }", + &expect![[r##" + #1 0-34 "repeat { } until false fixup { 1 }" : Unit + #2 7-10 "{ }" : Unit + #3 17-22 "false" : Bool + #4 29-34 "{ 1 }" : Int + #6 31-32 "1" : Int + Error(Type(Error(TyMismatch("Unit", "Int", Span { lo: 29, hi: 34 })))) + "##]], + ); +} + #[test] fn if_cond_error() { check( @@ -1389,6 +1453,21 @@ fn while_cond_error() { ); } +#[test] +fn while_body_should_be_unit_error() { + check( + "", + "while true { 1 }", + &expect![[r##" + #1 0-16 "while true { 1 }" : Unit + #2 6-10 "true" : Bool + #3 11-16 "{ 1 }" : Int + #5 13-14 "1" : Int + Error(Type(Error(TyMismatch("Unit", "Int", Span { lo: 11, hi: 16 })))) + "##]], + ); +} + #[test] fn controlled_spec_impl() { check( @@ -4299,3 +4378,40 @@ fn lambda_on_array_where_item_used_in_call_should_be_inferred() { "#]], ); } + +#[test] +fn within_apply_returns_type_from_apply_block() { + check( + "", + "{ let x = within { } apply { 4 }; let y = x + 1; }", + &expect![[r##" + #1 0-50 "{ let x = within { } apply { 4 }; let y = x + 1; }" : Unit + #2 0-50 "{ let x = within { } apply { 4 }; let y = x + 1; }" : Unit + #4 6-7 "x" : Int + #6 10-32 "within { } apply { 4 }" : Int + #7 17-20 "{ }" : Unit + #8 27-32 "{ 4 }" : Int + #10 29-30 "4" : Int + #12 38-39 "y" : Int + #14 42-47 "x + 1" : Int + #15 42-43 "x" : Int + #18 46-47 "1" : Int + "##]], + ); +} + +#[test] +fn within_block_should_be_unit_error() { + check( + "", + "within { 4 } apply { 0 }", + &expect![[r##" + #1 0-24 "within { 4 } apply { 0 }" : Int + #2 7-12 "{ 4 }" : Int + #4 9-10 "4" : Int + #5 19-24 "{ 0 }" : Int + #7 21-22 "0" : Int + Error(Type(Error(TyMismatch("Unit", "Int", Span { lo: 7, hi: 12 })))) + "##]], + ); +}