Skip to content

Commit 04e9ab2

Browse files
authored
Fix panic in loop unification pass for short-circuiting expressions (#2723)
This avoids the panic in loop unification for the special case of short-circuiting expressions in the iterable of a for-loop by checking for the unit type and allowing transformation to progress using the iteration variable's type instead. Fixes #2695
1 parent cfeb5a7 commit 04e9ab2

File tree

2 files changed

+128
-3
lines changed

2 files changed

+128
-3
lines changed

source/compiler/qsc_passes/src/loop_unification.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ impl LoopUni<'_> {
140140
);
141141
let array_capture = array_id.gen_id_init(Mutability::Immutable, *iterable, self.assigner);
142142

143-
let Ty::Array(item_ty) = &array_id.ty else {
144-
panic!("iterator should have array type");
143+
let item_ty = match &array_id.ty {
144+
Ty::Array(inner) => (**inner).clone(),
145+
// If the type is not array, this is likely the special case where a short-circuiting expression is the iterable
146+
// and the type is thus unknown. In that case, we can just use the type of the iteration variable pattern.
147+
_ => iter.ty.clone(),
145148
};
146149
let ns = self
147150
.core
@@ -151,7 +154,7 @@ impl LoopUni<'_> {
151154
self.core,
152155
ns,
153156
"Length",
154-
vec![GenericArg::Ty((**item_ty).clone())],
157+
vec![GenericArg::Ty(item_ty)],
155158
array_id.span,
156159
);
157160
len_callee.id = self.assigner.next_node();
@@ -351,6 +354,12 @@ impl MutVisitor for LoopUni<'_> {
351354
Ty::Prim(Prim::Range) => {
352355
*expr = self.visit_for_range(iter, iterable, block, expr.span);
353356
}
357+
Ty::Tuple(ref inner) if inner.is_empty() => {
358+
// The type checking would only allow unit in here in the case where the iterable expression is
359+
// short-circuiting (an explicit `fail` or `return`), so treat this as if it were an array
360+
// of the type defined by the iteration variable.
361+
*expr = self.visit_for_array(iter, iterable, block, expr.span);
362+
}
354363
a => {
355364
// This scenario should have been caught by type-checking earlier
356365
panic!(

source/compiler/qsc_passes/src/loop_unification/tests.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,3 +594,119 @@ fn convert_repeat_nested() {
594594
ctl-adj: <none>"#]],
595595
);
596596
}
597+
598+
#[test]
599+
fn convert_treats_for_loop_with_short_circuit_expression_explicit_int() {
600+
check(
601+
indoc! {r#"
602+
function Main() : Unit {
603+
let x = for i : Int in fail "" {};
604+
}
605+
"#},
606+
&expect![[r#"
607+
Package:
608+
Item 0 [0-65] (Public):
609+
Namespace (Ident 14 [0-65] "test"): Item 1
610+
Item 1 [0-65] (Internal):
611+
Parent: 0
612+
Callable 0 [0-65] (function):
613+
name: Ident 1 [9-13] "Main"
614+
input: Pat 2 [13-15] [Type Unit]: Unit
615+
output: Unit
616+
functors: empty set
617+
body: SpecDecl 3 [0-65]: Impl:
618+
Block 4 [23-65] [Type Unit]:
619+
Stmt 5 [29-63]: Local (Immutable):
620+
Pat 6 [33-34] [Type Unit]: Bind: Ident 7 [33-34] "x"
621+
Expr 41 [37-62] [Type Unit]: Expr Block: Block 42 [37-62] [Type Unit]:
622+
Stmt 16 [0-0]: Local (Immutable):
623+
Pat 17 [52-59] [Type Unit]: Bind: Ident 15 [52-59] "@array_id_15"
624+
Expr 11 [52-59] [Type Unit]: Fail: Expr 12 [57-59] [Type String]: String:
625+
Lit: ""
626+
Stmt 22 [0-0]: Local (Immutable):
627+
Pat 23 [52-59] [Type Int]: Bind: Ident 19 [52-59] "@len_id_19"
628+
Expr 20 [52-59] [Type Int]: Call:
629+
Expr 18 [52-59] [Type (Int[] -> Int)]: Var:
630+
res: Item 1 (Package 0)
631+
generics:
632+
Int
633+
Expr 21 [52-59] [Type Unit]: Var: Local 15
634+
Stmt 26 [52-59]: Local (Mutable):
635+
Pat 27 [52-59] [Type Int]: Bind: Ident 24 [52-59] "@index_id_24"
636+
Expr 25 [52-59] [Type Int]: Lit: Int(0)
637+
Stmt 39 [0-0]: Expr: Expr 40 [37-62] [Type Unit]: While:
638+
Expr 36 [52-59] [Type Bool]: BinOp (Lt):
639+
Expr 37 [52-59] [Type Int]: Var: Local 24
640+
Expr 38 [52-59] [Type Int]: Var: Local 19
641+
Block 13 [60-62] [Type Unit]:
642+
Stmt 28 [41-48]: Local (Immutable):
643+
Pat 9 [41-48] [Type Int]: Bind: Ident 10 [41-42] "i"
644+
Expr 29 [52-59] [Type Int]: Index:
645+
Expr 30 [52-59] [Type Unit]: Var: Local 15
646+
Expr 31 [52-59] [Type Int]: Var: Local 24
647+
Stmt 33 [52-59]: Semi: Expr 34 [52-59] [Type Unit]: AssignOp (Add):
648+
Expr 35 [52-59] [Type Int]: Var: Local 24
649+
Expr 32 [52-59] [Type Int]: Lit: Int(1)
650+
adj: <none>
651+
ctl: <none>
652+
ctl-adj: <none>"#]],
653+
);
654+
}
655+
656+
#[test]
657+
fn convert_treats_for_loop_with_short_circuit_expression_explicit_non_int() {
658+
check(
659+
indoc! {r#"
660+
function Main() : Unit {
661+
let x = for i : String in fail "" {};
662+
}
663+
"#},
664+
&expect![[r#"
665+
Package:
666+
Item 0 [0-68] (Public):
667+
Namespace (Ident 14 [0-68] "test"): Item 1
668+
Item 1 [0-68] (Internal):
669+
Parent: 0
670+
Callable 0 [0-68] (function):
671+
name: Ident 1 [9-13] "Main"
672+
input: Pat 2 [13-15] [Type Unit]: Unit
673+
output: Unit
674+
functors: empty set
675+
body: SpecDecl 3 [0-68]: Impl:
676+
Block 4 [23-68] [Type Unit]:
677+
Stmt 5 [29-66]: Local (Immutable):
678+
Pat 6 [33-34] [Type Unit]: Bind: Ident 7 [33-34] "x"
679+
Expr 41 [37-65] [Type Unit]: Expr Block: Block 42 [37-65] [Type Unit]:
680+
Stmt 16 [0-0]: Local (Immutable):
681+
Pat 17 [55-62] [Type Unit]: Bind: Ident 15 [55-62] "@array_id_15"
682+
Expr 11 [55-62] [Type Unit]: Fail: Expr 12 [60-62] [Type String]: String:
683+
Lit: ""
684+
Stmt 22 [0-0]: Local (Immutable):
685+
Pat 23 [55-62] [Type Int]: Bind: Ident 19 [55-62] "@len_id_19"
686+
Expr 20 [55-62] [Type Int]: Call:
687+
Expr 18 [55-62] [Type (String[] -> Int)]: Var:
688+
res: Item 1 (Package 0)
689+
generics:
690+
String
691+
Expr 21 [55-62] [Type Unit]: Var: Local 15
692+
Stmt 26 [55-62]: Local (Mutable):
693+
Pat 27 [55-62] [Type Int]: Bind: Ident 24 [55-62] "@index_id_24"
694+
Expr 25 [55-62] [Type Int]: Lit: Int(0)
695+
Stmt 39 [0-0]: Expr: Expr 40 [37-65] [Type Unit]: While:
696+
Expr 36 [55-62] [Type Bool]: BinOp (Lt):
697+
Expr 37 [55-62] [Type Int]: Var: Local 24
698+
Expr 38 [55-62] [Type Int]: Var: Local 19
699+
Block 13 [63-65] [Type Unit]:
700+
Stmt 28 [41-51]: Local (Immutable):
701+
Pat 9 [41-51] [Type String]: Bind: Ident 10 [41-42] "i"
702+
Expr 29 [55-62] [Type String]: Index:
703+
Expr 30 [55-62] [Type Unit]: Var: Local 15
704+
Expr 31 [55-62] [Type Int]: Var: Local 24
705+
Stmt 33 [55-62]: Semi: Expr 34 [55-62] [Type Unit]: AssignOp (Add):
706+
Expr 35 [55-62] [Type Int]: Var: Local 24
707+
Expr 32 [55-62] [Type Int]: Lit: Int(1)
708+
adj: <none>
709+
ctl: <none>
710+
ctl-adj: <none>"#]],
711+
);
712+
}

0 commit comments

Comments
 (0)