Skip to content

Commit 72570cf

Browse files
committed
Track moves through short-circuit expressions
1 parent 2eb3687 commit 72570cf

10 files changed

Lines changed: 176 additions & 6 deletions

File tree

src/checks/body.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ fn apply_expr_effects(expr: &HirExpr, state: &mut BodyState) {
198198
state.apply_move_events(events);
199199
apply_expr_effects(value, state);
200200
}
201+
HirExpr::Binary { left, right, .. } => {
202+
apply_expr_effects(left, state);
203+
apply_expr_effects(right, state);
204+
}
201205
HirExpr::Field { base, .. } => apply_expr_effects(base, state),
202206
HirExpr::Closure { .. }
203207
| HirExpr::Ident { .. }
@@ -444,6 +448,10 @@ fn check_resource_pool_lease_expr(
444448
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
445449
check_resource_pool_lease_expr(analyzer, value, within_with_resource);
446450
}
451+
HirExpr::Binary { left, right, .. } => {
452+
check_resource_pool_lease_expr(analyzer, left, within_with_resource);
453+
check_resource_pool_lease_expr(analyzer, right, within_with_resource);
454+
}
447455
HirExpr::Field { base, .. } => {
448456
check_resource_pool_lease_expr(analyzer, base, within_with_resource);
449457
}

src/checks/calls.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ fn check_expr(analyzer: &mut Analyzer<'_>, expr: &HirExpr) {
7979
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
8080
check_expr(analyzer, value);
8181
}
82+
HirExpr::Binary { left, right, .. } => {
83+
check_expr(analyzer, left);
84+
check_expr(analyzer, right);
85+
}
8286
HirExpr::Field { base, .. } => check_expr(analyzer, base),
8387
HirExpr::Closure { body, .. } => check_block(analyzer, body),
8488
HirExpr::Ident { .. }
@@ -287,6 +291,7 @@ fn hir_expr_span(expr: &HirExpr) -> &Span {
287291
HirExpr::Ident { span, .. }
288292
| HirExpr::Number { span, .. }
289293
| HirExpr::String { span, .. }
294+
| HirExpr::Binary { span, .. }
290295
| HirExpr::Field { span, .. }
291296
| HirExpr::Call { span, .. }
292297
| HirExpr::Effect { span, .. }

src/checks/local.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ fn collect_expr_take_handle_fields(expr: &HirExpr, fields: &mut Vec<TakeHandleFi
358358
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
359359
collect_expr_take_handle_fields(value, fields);
360360
}
361+
HirExpr::Binary { left, right, .. } => {
362+
collect_expr_take_handle_fields(left, fields);
363+
collect_expr_take_handle_fields(right, fields);
364+
}
361365
HirExpr::Field { base, .. } => collect_expr_take_handle_fields(base, fields),
362366
HirExpr::Call { args, .. } => {
363367
for arg in args {
@@ -585,6 +589,10 @@ fn collect_ordered_moved_uses_from_expr(
585589
collect_ordered_moved_uses_from_expr(value, state, moved_uses);
586590
state.apply_move_events(events);
587591
}
592+
HirExpr::Binary { left, right, .. } => {
593+
collect_ordered_moved_uses_from_expr(left, state, moved_uses);
594+
collect_ordered_moved_uses_from_expr(right, state, moved_uses);
595+
}
588596
HirExpr::Field { base, .. } => {
589597
collect_ordered_moved_uses_from_expr(base, state, moved_uses);
590598
}
@@ -714,6 +722,10 @@ fn collect_retained_closure_captures_from_expr(
714722
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
715723
collect_retained_closure_captures_from_expr(value, state, captures);
716724
}
725+
HirExpr::Binary { left, right, .. } => {
726+
collect_retained_closure_captures_from_expr(left, state, captures);
727+
collect_retained_closure_captures_from_expr(right, state, captures);
728+
}
717729
HirExpr::Field { base, .. } => {
718730
collect_retained_closure_captures_from_expr(base, state, captures);
719731
}
@@ -737,6 +749,7 @@ fn retained_closure_arg(expr: &HirExpr) -> Option<(&HirBlock, &Span)> {
737749
} => retained_closure_arg(value),
738750
HirExpr::Effect { .. }
739751
| HirExpr::Manage { .. }
752+
| HirExpr::Binary { .. }
740753
| HirExpr::Ident { .. }
741754
| HirExpr::Number { .. }
742755
| HirExpr::String { .. }
@@ -767,6 +780,7 @@ fn hir_expr_span(expr: &HirExpr) -> &Span {
767780
HirExpr::Ident { span, .. }
768781
| HirExpr::Number { span, .. }
769782
| HirExpr::String { span, .. }
783+
| HirExpr::Binary { span, .. }
770784
| HirExpr::Field { span, .. }
771785
| HirExpr::Call { span, .. }
772786
| HirExpr::Effect { span, .. }
@@ -854,6 +868,10 @@ fn collect_expr_resource_escapes(
854868
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
855869
collect_expr_resource_escapes(value, escapes_by_with_span);
856870
}
871+
HirExpr::Binary { left, right, .. } => {
872+
collect_expr_resource_escapes(left, escapes_by_with_span);
873+
collect_expr_resource_escapes(right, escapes_by_with_span);
874+
}
857875
HirExpr::Field { base, .. } => collect_expr_resource_escapes(base, escapes_by_with_span),
858876
HirExpr::Closure { body, .. } => collect_block_resource_escapes(body, escapes_by_with_span),
859877
HirExpr::Ident { .. }
@@ -953,6 +971,10 @@ fn collect_resource_escapes_in_expr(
953971
}
954972
}
955973
HirExpr::Effect { value, .. } => collect_resource_escapes_in_expr(binding, value, escapes),
974+
HirExpr::Binary { left, right, .. } => {
975+
collect_resource_escapes_in_expr(binding, left, escapes);
976+
collect_resource_escapes_in_expr(binding, right, escapes);
977+
}
956978
HirExpr::Field { base, .. } => collect_resource_escapes_in_expr(binding, base, escapes),
957979
HirExpr::Closure { body, .. } => collect_resource_escapes_in_block(binding, body, escapes),
958980
HirExpr::Ident { .. }
@@ -1065,6 +1087,10 @@ fn collect_expr_managed_closure_uses(
10651087
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
10661088
collect_expr_managed_closure_uses(value, closures);
10671089
}
1090+
HirExpr::Binary { left, right, .. } => {
1091+
collect_expr_managed_closure_uses(left, closures);
1092+
collect_expr_managed_closure_uses(right, closures);
1093+
}
10681094
HirExpr::Field { base, .. } => collect_expr_managed_closure_uses(base, closures),
10691095
HirExpr::Closure { body, .. } => collect_block_managed_closure_uses(body, closures),
10701096
HirExpr::Ident { .. }
@@ -1177,6 +1203,10 @@ fn collect_hir_expr_effect_events(expr: &HirExpr, events: &mut Vec<HirEffectEven
11771203
events.extend(expr_events.iter().cloned());
11781204
collect_hir_expr_effect_events(value, events);
11791205
}
1206+
HirExpr::Binary { left, right, .. } => {
1207+
collect_hir_expr_effect_events(left, events);
1208+
collect_hir_expr_effect_events(right, events);
1209+
}
11801210
HirExpr::Field { base, .. } => collect_hir_expr_effect_events(base, events),
11811211
HirExpr::Closure { .. }
11821212
| HirExpr::Ident { .. }
@@ -1198,6 +1228,10 @@ fn collect_hir_expr_idents(expr: &HirExpr, uses: &mut Vec<(String, Span)>) {
11981228
HirExpr::Effect { value, .. } | HirExpr::Manage { value, .. } => {
11991229
collect_hir_expr_idents(value, uses);
12001230
}
1231+
HirExpr::Binary { left, right, .. } => {
1232+
collect_hir_expr_idents(left, uses);
1233+
collect_hir_expr_idents(right, uses);
1234+
}
12011235
HirExpr::Closure { body, .. } => collect_hir_block_idents(body, uses),
12021236
HirExpr::Number { .. } | HirExpr::String { .. } | HirExpr::Unknown(_) => {}
12031237
}
@@ -1596,6 +1630,7 @@ fn local_flow_step_binding(statement: &HirStmt) -> Option<LocalFlowBinding> {
15961630
HirExpr::Ident { name, span, .. } => Some((name.clone(), span.clone())),
15971631
HirExpr::Number { .. }
15981632
| HirExpr::String { .. }
1633+
| HirExpr::Binary { .. }
15991634
| HirExpr::Field { .. }
16001635
| HirExpr::Call { .. }
16011636
| HirExpr::Effect { .. }
@@ -1641,6 +1676,7 @@ fn hir_expr_type_name(expr: &HirExpr) -> Option<&str> {
16411676
| HirExpr::Effect { type_name, .. }
16421677
| HirExpr::Manage { type_name, .. } => type_name.as_deref(),
16431678
HirExpr::Field { access, .. } => access.type_name.as_deref(),
1679+
HirExpr::Binary { .. } => None,
16441680
HirExpr::Number { .. }
16451681
| HirExpr::String { .. }
16461682
| HirExpr::Closure { .. }

src/hir.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use std::collections::{HashMap, HashSet};
22

33
use crate::diagnostic::Span;
44
use crate::syntax::ast::{
5-
Block, CallArg, Callee, DataEffect, EffectDecl, Expr, FieldDecl, FunctionDecl, Item, LetKind,
6-
Param, Program as SyntaxProgram, Stmt, TypeDecl, TypeKind, TypeRef,
5+
BinaryOp, Block, CallArg, Callee, DataEffect, EffectDecl, Expr, FieldDecl, FunctionDecl, Item,
6+
LetKind, Param, Program as SyntaxProgram, Stmt, TypeDecl, TypeKind, TypeRef,
77
};
88

99
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -234,6 +234,12 @@ pub enum HirExpr {
234234
value: String,
235235
span: Span,
236236
},
237+
Binary {
238+
op: BinaryOp,
239+
left: Box<HirExpr>,
240+
right: Box<HirExpr>,
241+
span: Span,
242+
},
237243
Field {
238244
base: Box<HirExpr>,
239245
name: String,
@@ -712,6 +718,17 @@ fn lower_hir_expr(
712718
value: value.clone(),
713719
span: span.clone(),
714720
},
721+
Expr::Binary {
722+
op,
723+
left,
724+
right,
725+
span,
726+
} => HirExpr::Binary {
727+
op: *op,
728+
left: Box::new(lower_hir_expr(hir, function_name, left, value_types)),
729+
right: Box::new(lower_hir_expr(hir, function_name, right, value_types)),
730+
span: span.clone(),
731+
},
715732
Expr::Field { base, name, span } => {
716733
let base_type = infer_hir_expr_type(hir, base, value_types);
717734
let field = base_type
@@ -950,6 +967,10 @@ fn collect_body_facts_in_expr(
950967
facts: &mut BodyFacts,
951968
) {
952969
match expr {
970+
Expr::Binary { left, right, .. } => {
971+
collect_body_facts_in_expr(hir, function_name, left, value_types, facts);
972+
collect_body_facts_in_expr(hir, function_name, right, value_types, facts);
973+
}
953974
Expr::Call { callee, args, span } => {
954975
let resolution = hir.resolve_call(callee);
955976
if is_resource_pool_callee(callee) {
@@ -1108,6 +1129,7 @@ fn infer_hir_expr_type(
11081129
) -> Option<String> {
11091130
match expr {
11101131
Expr::Ident(name, _) => value_types.get(name).cloned(),
1132+
Expr::Binary { .. } => None,
11111133
Expr::Effect { value, .. } | Expr::Manage { value, .. } => {
11121134
infer_hir_expr_type(hir, value, value_types)
11131135
}
@@ -1165,6 +1187,7 @@ fn resource_pool_arg_type(expr: &Expr, value_types: &HashMap<String, String>) ->
11651187
return resource_pool_namespace_arg(callee).map(|resource| resource.to_string());
11661188
}
11671189
Expr::Field { .. }
1190+
| Expr::Binary { .. }
11681191
| Expr::Closure { .. }
11691192
| Expr::Number(_, _)
11701193
| Expr::String(_, _)
@@ -1221,6 +1244,7 @@ fn classify_return_expr(hir: &Hir, expr: &Expr) -> HirReturnProof {
12211244
},
12221245
Expr::Effect { value, .. } | Expr::Manage { value, .. } => classify_return_expr(hir, value),
12231246
Expr::Field { .. }
1247+
| Expr::Binary { .. }
12241248
| Expr::Closure { .. }
12251249
| Expr::Number(_, _)
12261250
| Expr::String(_, _)

src/lexer.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl Lexer<'_> {
7171
'-' if self.peek_next() == Some('>') => self.push_two("->"),
7272
'=' if self.peek_next() == Some('>') => self.push_two("=>"),
7373
':' | ',' | '.' | '(' | ')' | '{' | '}' | '<' | '>' | '[' | ']' | '?' | '|'
74-
| '+' | '-' | '*' | '/' | '=' | ';' => self.push_one(),
74+
| '&' | '+' | '-' | '*' | '/' | '=' | ';' => self.push_one(),
7575
_ => self.push_one(),
7676
}
7777
}
@@ -187,6 +187,7 @@ impl Lexer<'_> {
187187
']' => "]",
188188
'?' => "?",
189189
'|' => "|",
190+
'&' => "&",
190191
'+' => "+",
191192
'-' => "-",
192193
'*' => "*",

src/review.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,10 @@ fn collect_boundary_expr(expr: &Expr, path: &str, boundary: &mut BoundarySig) {
655655
collect_boundary_expr(value, &arg_path, boundary);
656656
}
657657
}
658+
Expr::Binary { left, right, .. } => {
659+
collect_boundary_expr(left, &format!("{path}.left"), boundary);
660+
collect_boundary_expr(right, &format!("{path}.right"), boundary);
661+
}
658662
Expr::Field { base, .. } => collect_boundary_expr(base, path, boundary),
659663
Expr::Closure { body, .. } => {
660664
collect_boundary_block(body, &format!("{path}.closure"), boundary)
@@ -682,6 +686,7 @@ fn boundary_expr_subject(expr: &Expr) -> Option<String> {
682686
Expr::Effect { value, .. } | Expr::Manage { value, .. } => boundary_expr_subject(value),
683687
Expr::Field { name, .. } => Some(format!(".{name}")),
684688
Expr::Call { .. }
689+
| Expr::Binary { .. }
685690
| Expr::Closure { .. }
686691
| Expr::Number(_, _)
687692
| Expr::String(_, _)

src/syntax/ast.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ pub enum Expr {
147147
Ident(String, Span),
148148
Number(String, Span),
149149
String(String, Span),
150+
Binary {
151+
op: BinaryOp,
152+
left: Box<Expr>,
153+
right: Box<Expr>,
154+
span: Span,
155+
},
150156
Field {
151157
base: Box<Expr>,
152158
name: String,
@@ -173,6 +179,12 @@ pub enum Expr {
173179
Unknown(Span),
174180
}
175181

182+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183+
pub enum BinaryOp {
184+
LogicalAnd,
185+
LogicalOr,
186+
}
187+
176188
#[derive(Debug, Clone, PartialEq, Eq)]
177189
pub enum Callee {
178190
Name(String),
@@ -192,6 +204,7 @@ impl Expr {
192204
Self::Ident(_, span)
193205
| Self::Number(_, span)
194206
| Self::String(_, span)
207+
| Self::Binary { span, .. }
195208
| Self::Field { span, .. }
196209
| Self::Call { span, .. }
197210
| Self::Effect { span, .. }

src/syntax/parser.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::lexer::{Token, TokenKind, lex};
22
use crate::syntax::ast::{
3-
Block, CallArg, Callee, DataEffect, EffectDecl, Expr, FieldDecl, FileMode, FunctionDecl,
4-
IfStmt, Item, LetKind, LetStmt, LoopStmt, Param, Program, ReturnStmt, Stmt, TypeDecl, TypeKind,
5-
TypeRef, WithStmt,
3+
BinaryOp, Block, CallArg, Callee, DataEffect, EffectDecl, Expr, FieldDecl, FileMode,
4+
FunctionDecl, IfStmt, Item, LetKind, LetStmt, LoopStmt, Param, Program, ReturnStmt, Stmt,
5+
TypeDecl, TypeKind, TypeRef, WithStmt,
66
};
77

88
pub fn parse_source(file: &str, source: &str) -> Program {
@@ -526,6 +526,10 @@ fn parse_expr(tokens: &[Token], start: usize, end: usize) -> Option<Expr> {
526526
});
527527
}
528528

529+
if let Some(binary) = parse_binary_expr(tokens, start, end) {
530+
return Some(binary);
531+
}
532+
529533
if let Some(effect) = parse_data_effect(tokens.get(start)) {
530534
let value_start = start + 1;
531535
let value = if tokens
@@ -583,6 +587,59 @@ fn parse_expr(tokens: &[Token], start: usize, end: usize) -> Option<Expr> {
583587
}
584588
}
585589

590+
fn parse_binary_expr(tokens: &[Token], start: usize, end: usize) -> Option<Expr> {
591+
find_top_level_binary_operator(tokens, start, end, "|", "|")
592+
.or_else(|| find_top_level_binary_operator(tokens, start, end, "&", "&"))
593+
.and_then(|(operator, op)| {
594+
let left = parse_expr(tokens, start, operator)?;
595+
let right = parse_expr(tokens, operator + 2, end)?;
596+
Some(Expr::Binary {
597+
op,
598+
left: Box::new(left),
599+
right: Box::new(right),
600+
span: tokens[operator].span.clone(),
601+
})
602+
})
603+
}
604+
605+
fn find_top_level_binary_operator(
606+
tokens: &[Token],
607+
start: usize,
608+
end: usize,
609+
first: &str,
610+
second: &str,
611+
) -> Option<(usize, BinaryOp)> {
612+
let mut depth = 0usize;
613+
for index in (start..end.saturating_sub(1)).rev() {
614+
let token = &tokens[index];
615+
if token.symbol(")") || token.symbol("}") || token.symbol("]") || token.symbol(">") {
616+
depth += 1;
617+
continue;
618+
}
619+
if token.symbol("(") || token.symbol("{") || token.symbol("[") || token.symbol("<") {
620+
depth = depth.saturating_sub(1);
621+
continue;
622+
}
623+
if depth == 0
624+
&& token.symbol(first)
625+
&& tokens
626+
.get(index + 1)
627+
.is_some_and(|token| token.symbol(second))
628+
{
629+
if first == "|" && tokens.get(index + 2).is_some_and(|token| token.symbol("{")) {
630+
continue;
631+
}
632+
let op = if first == "|" {
633+
BinaryOp::LogicalOr
634+
} else {
635+
BinaryOp::LogicalAnd
636+
};
637+
return Some((index, op));
638+
}
639+
}
640+
None
641+
}
642+
586643
fn parse_call_expr(tokens: &[Token], start: usize, end: usize) -> Option<Expr> {
587644
let open = find_call_open(tokens, start, end)?;
588645
let callee = parse_callee(tokens, start, open)?;

0 commit comments

Comments
 (0)