Skip to content

Commit

Permalink
Blocks in while-expr should enforce Unit type
Browse files Browse the repository at this point in the history
In addition to fixing while-expr, this applies the same fix to for-loop, repeat-loop, and within-block, including unit tests for the desired type inference behavior.

Fixes #1251
  • Loading branch information
swernli committed Aug 15, 2024
1 parent 3b7b713 commit 1c42df3
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 3 deletions.
20 changes: 17 additions & 3 deletions compiler/qsc_frontend/src/typeck/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 => {
Expand Down
116 changes: 116 additions & 0 deletions compiler/qsc_frontend/src/typeck/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 }))))
"##]],
);
}

0 comments on commit 1c42df3

Please sign in to comment.