Skip to content

Commit 85f5572

Browse files
committed
Add type inference for conditionals
This supports the conditionals themselves being treated as expressions. So a conditional block now can be used in let expressions. let x = if conditional { return foo(); } else { return bar(); } It returns the type of its last returning expression (ending type). The type checker also checks if all ending types in each if/else if/else blocks is same. That is, in above snippet, foo := bar.
1 parent f719ee6 commit 85f5572

File tree

6 files changed

+49
-14
lines changed

6 files changed

+49
-14
lines changed

src/ast.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -635,11 +635,13 @@ impl Expr {
635635
}
636636
Self::FnCall(f, args) => *f.get_output_type(),
637637
Self::Let(var, val) => var.get_type(),
638-
Self::Conditional(cond, _, _) => {
639-
// TODO: Conditionals should be like functional languages, they
640-
// must always return something. And how to accomodate
641-
// conditionals with quantum variables in expressions?
642-
Type::Bottom
638+
Self::Conditional(conditional, truth_block, false_block) => {
639+
let last_expr = truth_block.last();
640+
if last_expr.is_none() {
641+
return Type::Bottom;
642+
}
643+
644+
return last_expr.unwrap().as_ref().borrow().get_type();
643645
}
644646
Self::Literal(lit) => match *lit.as_ref().borrow() {
645647
LiteralAST::Lit_Str(_) => Type::Bottom,

src/inference.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -404,16 +404,24 @@ fn infer_expr(expr: &QccCell<Expr>) -> Option<Type> {
404404
}
405405
}
406406

407-
Expr::Conditional(ref conditional, ref truth_block, ref false_block) =>
408-
{
407+
Expr::Conditional(ref conditional, ref truth_block, ref false_block) => {
408+
let mut truth_block_type = Some(Type::Bottom);
409409
for expr in truth_block {
410-
infer_expr(expr);
410+
truth_block_type = infer_expr(expr);
411411
}
412+
413+
let mut false_block_type = Some(Type::Bottom);
412414
for expr in false_block {
413-
infer_expr(expr);
415+
false_block_type = infer_expr(expr);
416+
}
417+
418+
// Ensure both last expressions in truth_block and false_block are
419+
// of same type.
420+
if truth_block_type == false_block_type {
421+
return truth_block_type;
422+
} else {
423+
return Some(Type::Bottom);
414424
}
415-
// TODO: How to infer type for entire if block?
416-
return Some(Type::Bottom);
417425
}
418426

419427
Expr::Literal(ref lit) => {
@@ -630,7 +638,7 @@ fn infer_from_table(
630638
// A literal is usually typed but if it isn't then it should follow
631639
// based on what the context says.
632640
match *l.as_ref().borrow() {
633-
LiteralAST::Lit_Qbit(ref q) => todo!("{q} perhaps a qubit"),
641+
LiteralAST::Lit_Qbit(ref q) => None,
634642
// digits are trivially typed
635643
LiteralAST::Lit_Digit(ref d) => None,
636644
LiteralAST::Lit_Str(ref s) => todo!("{:?} perhaps a string", s),

src/parser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,8 @@ impl Parser {
551551
} else {
552552
return Err(QccErrorKind::ExpectedExpr)?;
553553
}
554+
} else if self.lexer.is_token(Token::If) {
555+
return self.parse_if_block();
554556
} else {
555557
return Err(QccErrorKind::ExpectedExpr)?;
556558
}

tests/test_if_return_mismatch.ql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn mismatched_types(b: bit) : qbit {
2+
return if b {
3+
return 0q(0, 1);
4+
} else {
5+
return 32;
6+
};
7+
}
8+
9+
fn main() {
10+
let x = mismatched_types(1);
11+
}
12+

tests/test_if_return_type.ql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn unfair_toss(b: bit) : qbit {
2+
return if b {
3+
return 0q(0, 1);
4+
} else {
5+
return 0q(1, 0);
6+
};
7+
}
8+
9+
fn main() {
10+
let choice = unfair_toss(1);
11+
}

tests/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ fn test_ast_gen() -> Result<(), Box<dyn std::error::Error>> {
217217
|_ x: qubit = 0q0_1
218218
|_ x: qubit
219219
220-
|_ fn test_if$main () : <bottom> // @test_if.ql:6:4
220+
|_ fn test_if$main () : float64 // @test_if.ql:6:4
221221
|_ choice: qubit = test_if$foo: qubit ()
222222
|_ (choice == 0)
223223
|_ True
@@ -239,7 +239,7 @@ fn test_ast_gen() -> Result<(), Box<dyn std::error::Error>> {
239239
|_ fn test_else_if$pseudo_random () : float64 // @test_else_if.ql:1:4
240240
|_ 42
241241
242-
|_ fn test_else_if$main () : <bottom> // @test_else_if.ql:5:4
242+
|_ fn test_else_if$main () : float64 // @test_else_if.ql:5:4
243243
|_ choice: float64 = test_else_if$pseudo_random: float64 ()
244244
|_ (choice == 0)
245245
|_ True

0 commit comments

Comments
 (0)