use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use crate::analysis::registry::TypeRegistry;
use crate::analysis::types::RustNames;
use crate::ast::stmt::{BinaryOpKind, Expr, Literal, Stmt, TypeExpr};
use crate::intern::{Interner, Symbol};
use super::context::{RefinementContext, VariableCapabilities};
use super::detection::symbol_appears_in_stmts;
use super::types::codegen_type_expr;
enum CollInfo { Vec(String), Map(String, String) }
pub(crate) fn try_emit_for_range_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<(String, usize)> {
if idx + 1 >= stmts.len() {
return None;
}
let (counter_sym, counter_start_expr, is_new_binding) = match stmts[idx] {
Stmt::Let { var, value, .. } => {
if is_simple_expr(value) {
(*var, *value, true)
} else {
return None;
}
}
Stmt::Set { target, value } => {
if is_simple_expr(value) {
(*target, *value, false)
} else {
return None;
}
}
_ => return None,
};
let counter_start_literal = match counter_start_expr {
Expr::Literal(Literal::Number(n)) => Some(*n),
_ => None,
};
let (body, limit_expr, is_exclusive) = match stmts[idx + 1] {
Stmt::While { cond, body, .. } => {
match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(sym) = left {
if *sym == counter_sym {
(body, *right, false)
} else {
return None;
}
} else {
return None;
}
}
Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
if let Expr::Identifier(sym) = left {
if *sym == counter_sym {
(body, *right, true)
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
}
}
_ => return None,
};
if body.is_empty() {
return None;
}
let last = &body[body.len() - 1];
match last {
Stmt::Set { target, value, .. } => {
if *target != counter_sym {
return None;
}
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let is_counter_plus_1 = match (left, right) {
(Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
(Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
_ => false,
};
if !is_counter_plus_1 {
return None;
}
}
_ => return None,
}
}
_ => return None,
}
let body_without_increment = &body[..body.len() - 1];
if body_modifies_var(body_without_increment, counter_sym) {
return None;
}
if !is_simple_expr(limit_expr) {
return None;
}
let mut limit_syms = Vec::new();
collect_expr_symbols(limit_expr, &mut limit_syms);
for sym in &limit_syms {
if body_modifies_var(body_without_increment, *sym)
|| body_mutates_collection(body_without_increment, *sym)
{
return None;
}
}
let buffer_reuse = detect_buffer_reuse_in_body(body_without_increment, interner, ctx);
let double_buffer = if buffer_reuse.is_none() {
detect_double_buffer_swap(body_without_increment, interner, ctx)
} else {
None
};
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let counter_name = names.ident(counter_sym);
let limit_str = codegen_expr_simple(limit_expr, interner);
let start_str = codegen_expr_simple(counter_start_expr, interner);
let use_zero_based = counter_start_literal == Some(1)
&& !is_exclusive
&& counter_has_index_uses(body_without_increment, counter_sym)
&& counter_only_used_for_indexing(body_without_increment, counter_sym)
&& counter_indexes_only_vec_types(body_without_increment, counter_sym, ctx);
let range_str = if use_zero_based {
if let Expr::Literal(Literal::Number(n)) = limit_expr {
format!("0..{}", n)
} else {
format!("0..{}", limit_str)
}
} else if is_exclusive {
format!("{}..{}", start_str, limit_str)
} else {
if let Expr::Literal(Literal::Number(n)) = limit_expr {
format!("{}..{}", start_str, n + 1)
} else {
format!("{}..({} + 1)", start_str, limit_str)
}
};
let mut output = String::new();
{
let indexed_arrays = collect_indexed_arrays(body_without_increment, counter_sym);
let indexed_arrays: Vec<Symbol> = indexed_arrays.into_iter().filter(|arr_sym| {
match ctx.get_variable_types().get(arr_sym) {
Some(t) if t.contains("HashMap") => false,
_ => true,
}
}).collect();
for arr_sym in &indexed_arrays {
let arr_name = interner.resolve(*arr_sym);
writeln!(output, "{}unsafe {{ std::hint::assert_unchecked(({} as usize) <= {}.len()); }}",
indent_str, limit_str, arr_name).unwrap();
}
}
if let Some(ref reuse) = buffer_reuse {
let reuse_inner = names.ident(reuse.inner_sym);
writeln!(output, "{}let mut {}: Vec<{}> = Vec::new();", indent_str, reuse_inner, reuse.inner_elem_type).unwrap();
}
writeln!(output, "{}for {} in {} {{", indent_str, counter_name, range_str).unwrap();
ctx.push_scope();
if use_zero_based {
ctx.register_variable_type(counter_sym, "__zero_based_i64".to_string());
}
let body_refs: Vec<&Stmt> = body_without_increment.iter().collect();
let mut bi = 0;
while bi < body_refs.len() {
if let Some(ref reuse) = buffer_reuse {
if bi == reuse.inner_let_idx {
let reuse_inner = names.ident(reuse.inner_sym);
writeln!(output, "{}{}.clear();", " ".repeat(indent + 1), reuse_inner).unwrap();
ctx.register_variable_type(reuse.inner_sym, format!("Vec<{}>", reuse.inner_elem_type));
bi += 1;
continue;
}
if bi == reuse.set_idx {
let reuse_inner = names.ident(reuse.inner_sym);
let reuse_outer = names.ident(reuse.outer_sym);
writeln!(output, "{}std::mem::swap(&mut {}, &mut {});", " ".repeat(indent + 1), reuse_outer, reuse_inner).unwrap();
bi += 1;
continue;
}
}
if let Some((x_sym, y_sym, db_set_idx)) = double_buffer {
if bi == db_set_idx {
let x_name = names.ident(x_sym);
let y_name = names.ident(y_sym);
writeln!(output, "{}std::mem::swap(&mut {}, &mut {});", " ".repeat(indent + 1), x_name, y_name).unwrap();
bi += 1;
continue;
}
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1, ctx) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&body_refs, bi, interner, indent + 1, ctx) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
output.push_str(&super::codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env));
bi += 1;
}
ctx.pop_scope();
if use_zero_based {
ctx.register_variable_type(counter_sym, "i64".to_string());
}
writeln!(output, "{}}}", indent_str).unwrap();
let remaining_stmts = &stmts[idx + 2..];
if symbol_appears_in_stmts(counter_sym, remaining_stmts) {
let next_stmt_overwrites_counter = remaining_stmts.first().map_or(false, |s| {
matches!(s, Stmt::Set { target, .. } if *target == counter_sym)
});
if next_stmt_overwrites_counter {
if is_new_binding {
writeln!(output, "{}let mut {} = 0;", indent_str, counter_name).unwrap();
}
} else {
let post_value = if is_exclusive {
match (counter_start_literal, limit_expr) {
(Some(s), Expr::Literal(Literal::Number(n))) => {
format!("{}", std::cmp::max(s, *n))
}
(Some(s), _) => {
format!("({}_i64).max({})", s, limit_str)
}
(None, _) => {
format!("({}).max({})", start_str, limit_str)
}
}
} else {
match (counter_start_literal, limit_expr) {
(Some(s), Expr::Literal(Literal::Number(n))) => {
format!("{}", std::cmp::max(s, n + 1))
}
(Some(s), _) => {
format!("({}_i64).max({} + 1)", s, limit_str)
}
(None, _) => {
format!("({}).max({} + 1)", start_str, limit_str)
}
}
};
if is_new_binding {
writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_value).unwrap();
} else {
writeln!(output, "{}{} = {};", indent_str, counter_name, post_value).unwrap();
}
}
}
Some((output, 1)) }
fn collect_expr_symbols(expr: &Expr, out: &mut Vec<Symbol>) {
match expr {
Expr::Identifier(sym) => out.push(*sym),
Expr::Length { collection } => collect_expr_symbols(collection, out),
Expr::BinaryOp { left, right, .. } => {
collect_expr_symbols(left, out);
collect_expr_symbols(right, out);
}
_ => {}
}
}
pub(crate) fn body_modifies_var(stmts: &[Stmt], sym: Symbol) -> bool {
for stmt in stmts {
match stmt {
Stmt::Set { target, .. } if *target == sym => return true,
Stmt::If { then_block, else_block, .. } => {
if body_modifies_var(then_block, sym) {
return true;
}
if let Some(else_stmts) = else_block {
if body_modifies_var(else_stmts, sym) {
return true;
}
}
}
Stmt::While { body, .. } => {
if body_modifies_var(body, sym) {
return true;
}
}
Stmt::Repeat { body, .. } => {
if body_modifies_var(body, sym) {
return true;
}
}
Stmt::Zone { body, .. } => {
if body_modifies_var(body, sym) {
return true;
}
}
_ => {}
}
}
false
}
pub(crate) fn body_mutates_collection(stmts: &[Stmt], coll_sym: Symbol) -> bool {
for stmt in stmts {
match stmt {
Stmt::Push { collection, .. } | Stmt::Pop { collection, .. }
| Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
if let Expr::Identifier(sym) = collection {
if *sym == coll_sym {
return true;
}
}
}
Stmt::SetIndex { collection, .. } => {
if let Expr::Identifier(sym) = collection {
if *sym == coll_sym {
return true;
}
}
}
Stmt::Set { target, .. } if *target == coll_sym => return true,
Stmt::If { then_block, else_block, .. } => {
if body_mutates_collection(then_block, coll_sym) {
return true;
}
if let Some(else_stmts) = else_block {
if body_mutates_collection(else_stmts, coll_sym) {
return true;
}
}
}
Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
if body_mutates_collection(body, coll_sym) {
return true;
}
}
Stmt::Zone { body, .. } => {
if body_mutates_collection(body, coll_sym) {
return true;
}
}
_ => {}
}
}
false
}
pub(crate) fn body_resizes_collection(stmts: &[Stmt], coll_sym: Symbol) -> bool {
for stmt in stmts {
match stmt {
Stmt::Push { collection, .. } | Stmt::Pop { collection, .. }
| Stmt::Add { collection, .. } | Stmt::Remove { collection, .. } => {
if let Expr::Identifier(sym) = collection {
if *sym == coll_sym {
return true;
}
}
}
Stmt::Set { target, .. } if *target == coll_sym => return true,
Stmt::If { then_block, else_block, .. } => {
if body_resizes_collection(then_block, coll_sym) {
return true;
}
if let Some(else_stmts) = else_block {
if body_resizes_collection(else_stmts, coll_sym) {
return true;
}
}
}
Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
if body_resizes_collection(body, coll_sym) {
return true;
}
}
Stmt::Zone { body, .. } => {
if body_resizes_collection(body, coll_sym) {
return true;
}
}
_ => {}
}
}
false
}
fn all_paths_push_to(stmts: &[Stmt], coll_sym: Symbol) -> bool {
stmts.iter().any(|s| match s {
Stmt::Push { collection, .. } => {
matches!(collection, Expr::Identifier(sym) if *sym == coll_sym)
}
Stmt::If { then_block, else_block, .. } => {
if let Some(else_stmts) = else_block {
all_paths_push_to(then_block, coll_sym)
&& all_paths_push_to(else_stmts, coll_sym)
} else {
false
}
}
_ => false,
})
}
fn all_paths_set_index_to(stmts: &[Stmt], coll_sym: Symbol) -> bool {
stmts.iter().any(|s| match s {
Stmt::SetIndex { collection, .. } => {
matches!(collection, Expr::Identifier(sym) if *sym == coll_sym)
}
Stmt::If { then_block, else_block, .. } => {
if let Some(else_stmts) = else_block {
all_paths_set_index_to(then_block, coll_sym)
&& all_paths_set_index_to(else_stmts, coll_sym)
} else {
false
}
}
_ => false,
})
}
fn counter_has_index_uses(stmts: &[Stmt], counter_sym: Symbol) -> bool {
stmts.iter().any(|s| stmt_has_counter_index_use(s, counter_sym))
}
fn stmt_has_counter_index_use(stmt: &Stmt, counter_sym: Symbol) -> bool {
match stmt {
Stmt::Let { value, .. } => expr_has_counter_index_use(value, counter_sym),
Stmt::Set { value, .. } => expr_has_counter_index_use(value, counter_sym),
Stmt::Show { object, .. } => expr_has_counter_index_use(object, counter_sym),
Stmt::Push { value, .. } => expr_has_counter_index_use(value, counter_sym),
Stmt::SetIndex { index, value, .. } => {
matches!(index, Expr::Identifier(sym) if *sym == counter_sym)
|| expr_has_counter_index_use(value, counter_sym)
}
Stmt::If { cond, then_block, else_block } => {
expr_has_counter_index_use(cond, counter_sym)
|| counter_has_index_uses(then_block, counter_sym)
|| else_block.as_ref().map_or(false, |eb| counter_has_index_uses(eb, counter_sym))
}
Stmt::While { body, .. } => counter_has_index_uses(body, counter_sym),
Stmt::Return { value } => value.map_or(false, |v| expr_has_counter_index_use(v, counter_sym)),
Stmt::Call { args, .. } => args.iter().any(|a| expr_has_counter_index_use(a, counter_sym)),
Stmt::Repeat { body, .. } => counter_has_index_uses(body, counter_sym),
_ => false,
}
}
fn expr_has_counter_index_use(expr: &Expr, counter_sym: Symbol) -> bool {
match expr {
Expr::Index { collection, index } => {
matches!(index, Expr::Identifier(sym) if *sym == counter_sym)
|| expr_has_counter_index_use(collection, counter_sym)
|| expr_has_counter_index_use(index, counter_sym)
}
Expr::BinaryOp { left, right, .. } => {
expr_has_counter_index_use(left, counter_sym) || expr_has_counter_index_use(right, counter_sym)
}
Expr::Call { args, .. } => args.iter().any(|a| expr_has_counter_index_use(a, counter_sym)),
Expr::Length { collection } => expr_has_counter_index_use(collection, counter_sym),
Expr::List(items) => items.iter().any(|e| expr_has_counter_index_use(e, counter_sym)),
Expr::Not { operand } => expr_has_counter_index_use(operand, counter_sym),
Expr::Copy { expr: inner } => expr_has_counter_index_use(inner, counter_sym),
Expr::Slice { collection, start, end } => {
expr_has_counter_index_use(collection, counter_sym)
|| expr_has_counter_index_use(start, counter_sym)
|| expr_has_counter_index_use(end, counter_sym)
}
_ => false,
}
}
fn counter_indexes_only_vec_types(stmts: &[Stmt], counter_sym: Symbol, ctx: &RefinementContext) -> bool {
for stmt in stmts {
if !stmt_counter_indexes_vec_types(stmt, counter_sym, ctx) {
return false;
}
}
true
}
fn stmt_counter_indexes_vec_types(stmt: &Stmt, counter_sym: Symbol, ctx: &RefinementContext) -> bool {
match stmt {
Stmt::Let { value, .. } => expr_counter_indexes_vec_types(value, counter_sym, ctx),
Stmt::Set { value, .. } => expr_counter_indexes_vec_types(value, counter_sym, ctx),
Stmt::Show { object, .. } => expr_counter_indexes_vec_types(object, counter_sym, ctx),
Stmt::Push { value, .. } => expr_counter_indexes_vec_types(value, counter_sym, ctx),
Stmt::SetIndex { collection, index, value } => {
let idx_uses_counter = matches!(index, Expr::Identifier(sym) if *sym == counter_sym);
if idx_uses_counter {
if let Expr::Identifier(coll_sym) = collection {
let is_vec = ctx.get_variable_types().get(coll_sym)
.map_or(false, |t| t.starts_with("Vec") || t.starts_with("&[") || t.starts_with("&mut ["));
if !is_vec { return false; }
} else {
return false;
}
}
expr_counter_indexes_vec_types(value, counter_sym, ctx)
}
Stmt::If { cond, then_block, else_block } => {
expr_counter_indexes_vec_types(cond, counter_sym, ctx)
&& counter_indexes_only_vec_types(then_block, counter_sym, ctx)
&& else_block.as_ref().map_or(true, |eb| counter_indexes_only_vec_types(eb, counter_sym, ctx))
}
Stmt::While { body, .. } => counter_indexes_only_vec_types(body, counter_sym, ctx),
Stmt::Repeat { body, .. } => counter_indexes_only_vec_types(body, counter_sym, ctx),
Stmt::Return { value } => value.map_or(true, |v| expr_counter_indexes_vec_types(v, counter_sym, ctx)),
Stmt::Call { args, .. } => args.iter().all(|a| expr_counter_indexes_vec_types(a, counter_sym, ctx)),
_ => true,
}
}
fn expr_counter_indexes_vec_types(expr: &Expr, counter_sym: Symbol, ctx: &RefinementContext) -> bool {
match expr {
Expr::Index { collection, index } => {
let idx_uses_counter = matches!(index, Expr::Identifier(sym) if *sym == counter_sym);
if idx_uses_counter {
if let Expr::Identifier(coll_sym) = collection {
let is_vec = ctx.get_variable_types().get(coll_sym)
.map_or(false, |t| t.starts_with("Vec") || t.starts_with("&[") || t.starts_with("&mut ["));
if !is_vec { return false; }
} else {
return false;
}
}
expr_counter_indexes_vec_types(collection, counter_sym, ctx)
&& expr_counter_indexes_vec_types(index, counter_sym, ctx)
}
Expr::BinaryOp { left, right, .. } => {
expr_counter_indexes_vec_types(left, counter_sym, ctx)
&& expr_counter_indexes_vec_types(right, counter_sym, ctx)
}
Expr::Call { args, .. } => args.iter().all(|a| expr_counter_indexes_vec_types(a, counter_sym, ctx)),
Expr::Not { operand } => expr_counter_indexes_vec_types(operand, counter_sym, ctx),
Expr::Copy { expr: inner } => expr_counter_indexes_vec_types(inner, counter_sym, ctx),
Expr::Length { collection } => expr_counter_indexes_vec_types(collection, counter_sym, ctx),
Expr::List(items) => items.iter().all(|e| expr_counter_indexes_vec_types(e, counter_sym, ctx)),
Expr::Slice { collection, start, end } => {
expr_counter_indexes_vec_types(collection, counter_sym, ctx)
&& expr_counter_indexes_vec_types(start, counter_sym, ctx)
&& expr_counter_indexes_vec_types(end, counter_sym, ctx)
}
_ => true,
}
}
fn counter_only_used_for_indexing(stmts: &[Stmt], counter_sym: Symbol) -> bool {
for stmt in stmts {
if !check_counter_stmt_indexing_only(stmt, counter_sym) {
return false;
}
}
true
}
fn check_counter_stmt_indexing_only(stmt: &Stmt, counter_sym: Symbol) -> bool {
match stmt {
Stmt::Let { value, .. } => expr_uses_counter_only_in_index(value, counter_sym),
Stmt::Set { value, .. } => expr_uses_counter_only_in_index(value, counter_sym),
Stmt::Show { object, .. } => expr_uses_counter_only_in_index(object, counter_sym),
Stmt::Push { value, .. } => expr_uses_counter_only_in_index(value, counter_sym),
Stmt::SetIndex { collection: _, index, value } => {
let index_ok = match index {
Expr::Identifier(sym) if *sym == counter_sym => true,
_ => !expr_contains_symbol(index, counter_sym),
};
let value_ok = expr_uses_counter_only_in_index(value, counter_sym);
index_ok && value_ok
}
Stmt::If { cond, then_block, else_block } => {
expr_uses_counter_only_in_index(cond, counter_sym)
&& counter_only_used_for_indexing(then_block, counter_sym)
&& else_block.as_ref().map_or(true, |eb| counter_only_used_for_indexing(eb, counter_sym))
}
Stmt::While { cond, body, .. } => {
expr_uses_counter_only_in_index(cond, counter_sym)
&& counter_only_used_for_indexing(body, counter_sym)
}
Stmt::Repeat { body, .. } => counter_only_used_for_indexing(body, counter_sym),
Stmt::Call { args, .. } => {
args.iter().all(|a| expr_uses_counter_only_in_index(a, counter_sym))
}
Stmt::Return { value } => {
value.map_or(true, |v| expr_uses_counter_only_in_index(v, counter_sym))
}
_ => {
true
}
}
}
fn expr_uses_counter_only_in_index(expr: &Expr, counter_sym: Symbol) -> bool {
match expr {
Expr::Identifier(sym) => {
*sym != counter_sym
}
Expr::Index { collection, index } => {
let collection_ok = expr_uses_counter_only_in_index(collection, counter_sym);
let index_ok = match index {
Expr::Identifier(sym) if *sym == counter_sym => true,
_ => expr_uses_counter_only_in_index(index, counter_sym),
};
collection_ok && index_ok
}
Expr::BinaryOp { op, left, right } => {
match op {
BinaryOpKind::Lt | BinaryOpKind::LtEq | BinaryOpKind::Gt
| BinaryOpKind::GtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq => {
let left_is_counter = matches!(left, Expr::Identifier(s) if *s == counter_sym);
let right_is_counter = matches!(right, Expr::Identifier(s) if *s == counter_sym);
if left_is_counter && !expr_contains_symbol(right, counter_sym) {
return true;
}
if right_is_counter && !expr_contains_symbol(left, counter_sym) {
return true;
}
expr_uses_counter_only_in_index(left, counter_sym)
&& expr_uses_counter_only_in_index(right, counter_sym)
}
_ => {
expr_uses_counter_only_in_index(left, counter_sym)
&& expr_uses_counter_only_in_index(right, counter_sym)
}
}
}
Expr::Not { operand } => expr_uses_counter_only_in_index(operand, counter_sym),
Expr::Call { args, .. } => {
args.iter().all(|a| expr_uses_counter_only_in_index(a, counter_sym))
}
Expr::Length { collection } => expr_uses_counter_only_in_index(collection, counter_sym),
Expr::Literal(_) => true,
Expr::List(items) => items.iter().all(|e| expr_uses_counter_only_in_index(e, counter_sym)),
Expr::Slice { collection, start, end } => {
expr_uses_counter_only_in_index(collection, counter_sym)
&& expr_uses_counter_only_in_index(start, counter_sym)
&& expr_uses_counter_only_in_index(end, counter_sym)
}
Expr::Copy { expr: inner } => expr_uses_counter_only_in_index(inner, counter_sym),
_ => !expr_contains_symbol(expr, counter_sym),
}
}
fn expr_contains_symbol(expr: &Expr, sym: Symbol) -> bool {
match expr {
Expr::Identifier(s) => *s == sym,
Expr::BinaryOp { left, right, .. } => {
expr_contains_symbol(left, sym) || expr_contains_symbol(right, sym)
}
Expr::Not { operand } => expr_contains_symbol(operand, sym),
Expr::Call { args, .. } => args.iter().any(|a| expr_contains_symbol(a, sym)),
Expr::Index { collection, index } => {
expr_contains_symbol(collection, sym) || expr_contains_symbol(index, sym)
}
Expr::Length { collection } => expr_contains_symbol(collection, sym),
Expr::List(items) => items.iter().any(|e| expr_contains_symbol(e, sym)),
Expr::Slice { collection, start, end } => {
expr_contains_symbol(collection, sym)
|| expr_contains_symbol(start, sym)
|| expr_contains_symbol(end, sym)
}
Expr::Copy { expr: inner } => expr_contains_symbol(inner, sym),
Expr::Literal(_) => false,
_ => false,
}
}
pub(crate) fn try_emit_vec_fill_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
ctx: &mut RefinementContext<'a>,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let (vec_sym, elem_type) = match stmts[idx] {
Stmt::Let { var, value, ty, .. } => {
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(codegen_type_expr(¶ms[0], interner))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
if !type_args.is_empty() {
Some(codegen_type_expr(&type_args[0], interner))
} else {
None
}
} else {
None
}
} else {
None
};
match type_from_annotation.or(type_from_new) {
Some(t) => (*var, t),
None => return None,
}
}
_ => return None,
};
let mut prefix_values: Vec<String> = Vec::new();
let mut cursor = idx + 1;
while cursor < stmts.len() {
if let Stmt::Push { value, collection } = stmts[cursor] {
if let Expr::Identifier(sym) = collection {
if *sym == vec_sym {
let val_str = match value {
Expr::Literal(Literal::Number(n)) => Some(format!("{}", n)),
Expr::Literal(Literal::Float(f)) => Some(format!("{:.1}", f)),
Expr::Literal(Literal::Boolean(b)) => Some(format!("{}", b)),
Expr::Literal(Literal::Char(c)) => Some(format!("'{}'", c)),
Expr::Literal(Literal::Text(s)) => {
Some(format!("String::from(\"{}\")", interner.resolve(*s)))
}
_ => None,
};
if let Some(vs) = val_str {
prefix_values.push(vs);
cursor += 1;
continue;
}
}
}
}
break;
}
if cursor + 1 >= stmts.len() {
return None;
}
let counter_is_new_binding = matches!(stmts[cursor], Stmt::Let { .. });
let (counter_sym, counter_start) = match stmts[cursor] {
Stmt::Let { var, value: Expr::Literal(Literal::Number(n)), .. } => {
(*var, *n)
}
Stmt::Set { target, value: Expr::Literal(Literal::Number(n)) } => {
(*target, *n)
}
_ => return None,
};
match stmts[cursor + 1] {
Stmt::While { cond, body, .. } => {
let (limit_expr, is_exclusive) = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(sym) = left {
if *sym == counter_sym {
(Some(*right), false)
} else {
(None, false)
}
} else {
(None, false)
}
}
Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
if let Expr::Identifier(sym) = left {
if *sym == counter_sym {
(Some(*right), true)
} else {
(None, false)
}
} else {
(None, false)
}
}
_ => (None, false),
};
let limit_expr = limit_expr?;
if body.len() != 2 {
return None;
}
let push_val = match &body[0] {
Stmt::Push { value, collection } => {
if let Expr::Identifier(sym) = collection {
if *sym == vec_sym {
Some(*value)
} else {
None
}
} else {
None
}
}
_ => None,
}?;
let fill_val_str = match push_val {
Expr::Literal(Literal::Number(n)) => format!("{}", n),
Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
Expr::Literal(Literal::Char(c)) => format!("'{}'", c),
Expr::Literal(Literal::Text(s)) => {
format!("String::from(\"{}\")", interner.resolve(*s))
}
_ => return None,
};
match &body[1] {
Stmt::Set { target, value, .. } => {
if *target != counter_sym {
return None;
}
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let is_counter_plus_1 = match (left, right) {
(Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym => true,
(Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym => true,
_ => false,
};
if !is_counter_plus_1 {
return None;
}
}
_ => return None,
}
}
_ => return None,
}
let indent_str = " ".repeat(indent);
let vec_name = interner.resolve(vec_sym);
let limit_str = codegen_expr_simple(limit_expr, interner);
let prefix_count = prefix_values.len();
let raw_loop_count = if is_exclusive {
if counter_start == 0 {
limit_str.clone()
} else {
format!("({} - {})", limit_str, counter_start)
}
} else {
if counter_start == 0 {
format!("({} + 1)", limit_str)
} else if counter_start == 1 {
limit_str.clone()
} else {
format!("({} - {} + 1)", limit_str, counter_start)
}
};
let count_expr = if prefix_count == 0 {
format!("{} as usize", raw_loop_count)
} else {
format!("({} + {}) as usize", prefix_count, raw_loop_count)
};
let mut output = String::new();
writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
indent_str, vec_name, elem_type, fill_val_str, count_expr).unwrap();
ctx.register_variable_type(vec_sym, format!("Vec<{}>", elem_type));
for (i, prefix_val) in prefix_values.iter().enumerate() {
if *prefix_val != fill_val_str {
writeln!(output, "{}{}[{}] = {};",
indent_str, vec_name, i, prefix_val).unwrap();
}
}
let names = RustNames::new(interner);
let counter_name = names.ident(counter_sym);
if counter_is_new_binding {
writeln!(output, "{}let mut {} = {};",
indent_str, counter_name, counter_start).unwrap();
} else {
writeln!(output, "{}{} = {};",
indent_str, counter_name, counter_start).unwrap();
}
let extra_consumed = (cursor - idx) + 1;
Some((output, extra_consumed))
}
_ => None,
}
}
fn is_simple_expr(expr: &Expr) -> bool {
match expr {
Expr::Literal(Literal::Number(_))
| Expr::Literal(Literal::Float(_))
| Expr::Literal(Literal::Boolean(_))
| Expr::Identifier(_) => true,
Expr::BinaryOp { op, left, right } => {
matches!(op,
BinaryOpKind::Add | BinaryOpKind::Subtract |
BinaryOpKind::Multiply | BinaryOpKind::Divide | BinaryOpKind::Modulo
) && is_simple_expr(left) && is_simple_expr(right)
}
Expr::Length { collection } => {
matches!(collection, Expr::Identifier(_))
}
_ => false,
}
}
fn codegen_expr_simple(expr: &Expr, interner: &Interner) -> String {
match expr {
Expr::Literal(Literal::Number(n)) => format!("{}", n),
Expr::Literal(Literal::Float(f)) => format!("{:.1}", f),
Expr::Literal(Literal::Boolean(b)) => format!("{}", b),
Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
Expr::BinaryOp { op, left, right } => {
let l = codegen_expr_simple(left, interner);
let r = codegen_expr_simple(right, interner);
let op_str = match op {
BinaryOpKind::Add => "+",
BinaryOpKind::Subtract => "-",
BinaryOpKind::Multiply => "*",
BinaryOpKind::Divide => "/",
BinaryOpKind::Modulo => "%",
_ => return format!("({})", l),
};
format!("({} {} {})", l, op_str, r)
}
Expr::Length { collection } => {
if let Expr::Identifier(sym) = collection {
format!("({}.len() as i64)", interner.resolve(*sym))
} else {
"_".to_string()
}
}
_ => "_".to_string(),
}
}
pub(crate) fn try_emit_string_with_capacity_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let str_sym = match stmts[idx] {
Stmt::Let { var, value, mutable, .. } => {
if !*mutable && !mutable_vars.contains(var) {
return None;
}
if let Expr::Literal(Literal::Text(sym)) = value {
if interner.resolve(*sym).is_empty() {
*var
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
};
for scan in (idx + 1)..stmts.len() {
let stmt = stmts[scan];
let is_counter_init = match stmt {
Stmt::Let { value, .. } if is_simple_expr(value) => true,
Stmt::Set { value, .. } if is_simple_expr(value) => true,
_ => false,
};
if is_counter_init && scan + 1 < stmts.len() {
let (counter_sym, start_expr) = match stmt {
Stmt::Let { var, value, .. } => (*var, *value),
Stmt::Set { target, value } => (*target, *value),
_ => unreachable!(),
};
if let Stmt::While { cond, body, .. } = stmts[scan + 1] {
let (limit_expr, is_exclusive) = match cond {
Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
if matches!(left, Expr::Identifier(sym) if *sym == counter_sym) {
(Some(*right), true)
} else {
(None, false)
}
}
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if matches!(left, Expr::Identifier(sym) if *sym == counter_sym) {
(Some(*right), false)
} else {
(None, false)
}
}
_ => (None, false),
};
if let Some(limit_expr) = limit_expr {
if !is_simple_expr(limit_expr) {
continue;
}
if body.len() >= 2 {
let last_is_increment = match &body[body.len() - 1] {
Stmt::Set { target, value } if *target == counter_sym => {
matches!(value, Expr::BinaryOp { op: BinaryOpKind::Add, left, right }
if (matches!(left, Expr::Identifier(s) if *s == counter_sym) && matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1))) && matches!(right, Expr::Identifier(s) if *s == counter_sym))
)
}
_ => false,
};
if last_is_increment {
let body_appends = body_has_string_self_append(body, str_sym);
if body_appends {
let indent_str = " ".repeat(indent);
let var_name = interner.resolve(str_sym);
let limit_str = codegen_expr_simple(limit_expr, interner);
let start_str = codegen_expr_simple(start_expr, interner);
let capacity_expr = if is_exclusive {
match start_expr {
Expr::Literal(Literal::Number(0)) => limit_str.clone(),
Expr::Literal(Literal::Number(s)) => {
if let Expr::Literal(Literal::Number(n)) = limit_expr {
format!("{}", n - s)
} else {
format!("({} - {})", limit_str, s)
}
}
_ => format!("({} - {})", limit_str, start_str),
}
} else {
match start_expr {
Expr::Literal(Literal::Number(0)) => {
if let Expr::Literal(Literal::Number(n)) = limit_expr {
format!("{}", n + 1)
} else {
format!("({} + 1)", limit_str)
}
}
Expr::Literal(Literal::Number(1)) => limit_str.clone(),
Expr::Literal(Literal::Number(s)) => {
if let Expr::Literal(Literal::Number(n)) = limit_expr {
format!("{}", n - s + 1)
} else {
format!("({} - {} + 1)", limit_str, s)
}
}
_ => format!("({} - {} + 1)", limit_str, start_str),
}
};
let mut output = String::new();
writeln!(output, "{}let mut {} = String::with_capacity({} as usize);",
indent_str, var_name, capacity_expr).unwrap();
ctx.register_string_var(str_sym);
return Some((output, 0));
}
}
}
}
}
}
if symbol_appears_in_stmts(str_sym, &[stmt]) {
return None;
}
}
None
}
fn body_has_string_self_append(stmts: &[Stmt], str_sym: Symbol) -> bool {
for stmt in stmts {
match stmt {
Stmt::Set { target, value } if *target == str_sym => {
if let Expr::BinaryOp { op: BinaryOpKind::Add, left, .. } = value {
if matches!(left, Expr::Identifier(sym) if *sym == str_sym) {
return true;
}
}
}
Stmt::If { then_block, else_block, .. } => {
if body_has_string_self_append(then_block, str_sym) {
return true;
}
if let Some(eb) = else_block {
if body_has_string_self_append(eb, str_sym) {
return true;
}
}
}
Stmt::While { body, .. } | Stmt::Repeat { body, .. } => {
if body_has_string_self_append(body, str_sym) {
return true;
}
}
_ => {}
}
}
false
}
pub(crate) fn simplify_1based_index(expr: &Expr, interner: &Interner, include_as_usize: bool) -> String {
let cast = if include_as_usize { " as usize" } else { "" };
match expr {
Expr::Literal(Literal::Number(1)) => "0".to_string(),
Expr::Literal(Literal::Number(n)) => format!("{}", n - 1),
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
match (left, right) {
(_, Expr::Literal(Literal::Number(1))) => {
let inner = codegen_expr_simple(left, interner);
if include_as_usize {
format!("({}){}", inner, cast)
} else {
inner
}
}
(Expr::Literal(Literal::Number(1)), _) => {
let inner = codegen_expr_simple(right, interner);
if include_as_usize {
format!("({}){}", inner, cast)
} else {
inner
}
}
(_, Expr::Literal(Literal::Number(k))) if *k > 1 => {
let inner = codegen_expr_simple(left, interner);
format!("({} + {}){}", inner, k - 1, cast)
}
(Expr::Literal(Literal::Number(k)), _) if *k > 1 => {
let inner = codegen_expr_simple(right, interner);
format!("({} + {}){}", inner, k - 1, cast)
}
_ => {
let full = codegen_expr_simple(expr, interner);
format!("({} - 1){}", full, cast)
}
}
}
_ => {
let full = codegen_expr_simple(expr, interner);
format!("({} - 1){}", full, cast)
}
}
}
pub(crate) fn collect_indexed_arrays(stmts: &[Stmt], counter_sym: Symbol) -> Vec<Symbol> {
let mut arrays = Vec::new();
let mut seen = HashSet::new();
for stmt in stmts {
collect_indexed_arrays_from_stmt(stmt, counter_sym, &mut arrays, &mut seen);
}
arrays
}
fn collect_indexed_arrays_from_stmt(stmt: &Stmt, counter_sym: Symbol, arrays: &mut Vec<Symbol>, seen: &mut HashSet<Symbol>) {
match stmt {
Stmt::Set { value, .. } => collect_indexed_arrays_from_expr(value, counter_sym, arrays, seen),
Stmt::Let { value, .. } => collect_indexed_arrays_from_expr(value, counter_sym, arrays, seen),
Stmt::Show { object, .. } => collect_indexed_arrays_from_expr(object, counter_sym, arrays, seen),
Stmt::Push { value, .. } => collect_indexed_arrays_from_expr(value, counter_sym, arrays, seen),
Stmt::SetIndex { value, index, .. } => {
collect_indexed_arrays_from_expr(value, counter_sym, arrays, seen);
collect_indexed_arrays_from_expr(index, counter_sym, arrays, seen);
}
Stmt::If { cond, then_block, else_block } => {
collect_indexed_arrays_from_expr(cond, counter_sym, arrays, seen);
for s in then_block.iter() { collect_indexed_arrays_from_stmt(s, counter_sym, arrays, seen); }
if let Some(el) = else_block {
for s in el.iter() { collect_indexed_arrays_from_stmt(s, counter_sym, arrays, seen); }
}
}
_ => {}
}
}
fn collect_indexed_arrays_from_expr(expr: &Expr, counter_sym: Symbol, arrays: &mut Vec<Symbol>, seen: &mut HashSet<Symbol>) {
match expr {
Expr::Index { collection, index } => {
if expr_involves_symbol(index, counter_sym) {
if let Expr::Identifier(sym) = collection {
if seen.insert(*sym) {
arrays.push(*sym);
}
}
}
collect_indexed_arrays_from_expr(collection, counter_sym, arrays, seen);
collect_indexed_arrays_from_expr(index, counter_sym, arrays, seen);
}
Expr::BinaryOp { left, right, .. } => {
collect_indexed_arrays_from_expr(left, counter_sym, arrays, seen);
collect_indexed_arrays_from_expr(right, counter_sym, arrays, seen);
}
Expr::Not { operand } => collect_indexed_arrays_from_expr(operand, counter_sym, arrays, seen),
Expr::Length { collection } => collect_indexed_arrays_from_expr(collection, counter_sym, arrays, seen),
_ => {}
}
}
fn expr_involves_symbol(expr: &Expr, sym: Symbol) -> bool {
match expr {
Expr::Identifier(s) => *s == sym,
Expr::BinaryOp { left, right, .. } => {
expr_involves_symbol(left, sym) || expr_involves_symbol(right, sym)
}
_ => false,
}
}
pub(crate) fn exprs_equal(a: &Expr, b: &Expr) -> bool {
match (a, b) {
(Expr::Identifier(s1), Expr::Identifier(s2)) => s1 == s2,
(Expr::Literal(Literal::Number(n1)), Expr::Literal(Literal::Number(n2))) => n1 == n2,
(Expr::BinaryOp { op: op1, left: l1, right: r1 }, Expr::BinaryOp { op: op2, left: l2, right: r2 }) => {
op1 == op2 && exprs_equal(l1, l2) && exprs_equal(r1, r2)
}
_ => false,
}
}
pub(crate) fn try_emit_swap_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
variable_types: &HashMap<Symbol, String>,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let (a_sym, arr_sym_1, idx_expr_1) = match stmts[idx] {
Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
if let Expr::Identifier(coll_sym) = collection {
(*var, *coll_sym, *index)
} else {
return None;
}
}
_ => return None,
};
if let Some(t) = variable_types.get(&arr_sym_1) {
if !t.starts_with("Vec") && !t.starts_with("&mut [") {
return None;
}
} else {
return None;
}
if let Some(result) = try_emit_unconditional_swap(stmts, idx, a_sym, arr_sym_1, idx_expr_1, interner, indent) {
return Some(result);
}
let (b_sym, arr_sym_2, idx_expr_2) = match stmts[idx + 1] {
Stmt::Let { var, value: Expr::Index { collection, index }, mutable: false, .. } => {
if let Expr::Identifier(coll_sym) = collection {
(*var, *coll_sym, *index)
} else {
return None;
}
}
_ => return None,
};
if arr_sym_1 != arr_sym_2 {
return None;
}
if !is_simple_expr(idx_expr_1) || !is_simple_expr(idx_expr_2) {
return None;
}
match stmts[idx + 2] {
Stmt::If { cond, then_block, else_block } => {
let compares_a_b = match cond {
Expr::BinaryOp { op, left, right } => {
matches!(op, BinaryOpKind::Gt | BinaryOpKind::Lt | BinaryOpKind::GtEq | BinaryOpKind::LtEq | BinaryOpKind::Eq | BinaryOpKind::NotEq) &&
((matches!(left, Expr::Identifier(s) if *s == a_sym) && matches!(right, Expr::Identifier(s) if *s == b_sym)) ||
(matches!(left, Expr::Identifier(s) if *s == b_sym) && matches!(right, Expr::Identifier(s) if *s == a_sym)))
}
_ => false,
};
if !compares_a_b {
return None;
}
if else_block.is_some() {
return None;
}
if then_block.len() != 2 {
return None;
}
let swap_ok = match (&then_block[0], &then_block[1]) {
(
Stmt::SetIndex { collection: c1, index: i1, value: v1 },
Stmt::SetIndex { collection: c2, index: i2, value: v2 },
) => {
let same_arr = matches!((c1, c2), (Expr::Identifier(s1), Expr::Identifier(s2)) if *s1 == arr_sym_1 && *s2 == arr_sym_1);
let cross = exprs_equal(i1, idx_expr_1) && exprs_equal(i2, idx_expr_2) &&
matches!(v1, Expr::Identifier(s) if *s == b_sym) &&
matches!(v2, Expr::Identifier(s) if *s == a_sym);
let cross_rev = exprs_equal(i1, idx_expr_2) && exprs_equal(i2, idx_expr_1) &&
matches!(v1, Expr::Identifier(s) if *s == a_sym) &&
matches!(v2, Expr::Identifier(s) if *s == b_sym);
same_arr && (cross || cross_rev)
}
_ => false,
};
if !swap_ok {
return None;
}
let indent_str = " ".repeat(indent);
let arr_name = interner.resolve(arr_sym_1);
let idx1_simplified = simplify_1based_index(idx_expr_1, interner, true);
let idx2_simplified = simplify_1based_index(idx_expr_2, interner, true);
let op_str = match cond {
Expr::BinaryOp { op, .. } => match op {
BinaryOpKind::Gt => ">", BinaryOpKind::Lt => "<",
BinaryOpKind::GtEq => ">=", BinaryOpKind::LtEq => "<=",
BinaryOpKind::Eq => "==", BinaryOpKind::NotEq => "!=",
_ => unreachable!(),
},
_ => unreachable!(),
};
let mut output = String::new();
writeln!(output, "{}if {}[{}] {} {}[{}] {{",
indent_str, arr_name, idx1_simplified, op_str, arr_name, idx2_simplified,
).unwrap();
writeln!(output, "{} let __swap_tmp = {}[{}];",
indent_str, arr_name, idx1_simplified).unwrap();
writeln!(output, "{} {}[{}] = {}[{}];",
indent_str, arr_name, idx1_simplified, arr_name, idx2_simplified).unwrap();
writeln!(output, "{} {}[{}] = __swap_tmp;",
indent_str, arr_name, idx2_simplified).unwrap();
writeln!(output, "{}}}", indent_str).unwrap();
Some((output, 2)) }
_ => None,
}
}
pub(crate) fn try_emit_seq_copy_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
ctx: &mut RefinementContext<'a>,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let (dst_sym, elem_type) = match stmts[idx] {
Stmt::Let { var, value, mutable: true, .. } => {
if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() && !type_args.is_empty() {
(*var, codegen_type_expr(&type_args[0], interner))
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
};
let counter_sym = match stmts[idx + 1] {
Stmt::Set { target, value: Expr::Literal(Literal::Number(1)) } => *target,
_ => return None,
};
match stmts[idx + 2] {
Stmt::While { cond, body, .. } => {
let src_sym = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(c) = left {
if *c == counter_sym {
if let Expr::Length { collection } = right {
if let Expr::Identifier(s) = collection {
*s
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
};
if body.len() != 2 {
return None;
}
match &body[0] {
Stmt::Push { value, collection } => {
if !matches!(collection, Expr::Identifier(s) if *s == dst_sym) {
return None;
}
if let Expr::Index { collection: idx_coll, index: idx_expr } = value {
if !matches!(idx_coll, Expr::Identifier(s) if *s == src_sym) {
return None;
}
if !matches!(idx_expr, Expr::Identifier(s) if *s == counter_sym) {
return None;
}
} else {
return None;
}
}
_ => return None,
}
match &body[1] {
Stmt::Set { target, value } => {
if *target != counter_sym {
return None;
}
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = match (left, right) {
(Expr::Identifier(s), Expr::Literal(Literal::Number(1))) => *s == counter_sym,
(Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) => *s == counter_sym,
_ => false,
};
if !ok {
return None;
}
}
_ => return None,
}
}
_ => return None,
}
let indent_str = " ".repeat(indent);
let dst_name = interner.resolve(dst_sym);
let src_name = interner.resolve(src_sym);
let names = RustNames::new(interner);
let counter_name = names.ident(counter_sym);
let mut output = String::new();
writeln!(output, "{}let mut {}: Vec<{}> = {}.to_vec();",
indent_str, dst_name, elem_type, src_name).unwrap();
ctx.register_variable_type(dst_sym, format!("Vec<{}>", elem_type));
let remaining = &stmts[idx + 3..];
if symbol_appears_in_stmts(counter_sym, remaining) {
writeln!(output, "{}{} = {}.len() as i64 + 1;",
indent_str, counter_name, src_name).unwrap();
}
Some((output, 2)) }
_ => None,
}
}
pub(crate) fn try_emit_seq_from_slice_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let (dst_sym, elem_type) = match stmts[idx] {
Stmt::Let { var, value, ty, mutable: true, .. } => {
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(codegen_type_expr(¶ms[0], interner))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() {
if !type_args.is_empty() {
Some(codegen_type_expr(&type_args[0], interner))
} else {
None
}
} else {
None
}
} else {
None
};
match type_from_annotation.or(type_from_new) {
Some(t) => (*var, t),
None => return None,
}
}
_ => return None,
};
let mut counter_init_idx: Option<usize> = None;
for scan in (idx + 1)..stmts.len() {
let stmt = stmts[scan];
let is_counter_init = match stmt {
Stmt::Let { value, .. } if is_simple_expr(value) => true,
Stmt::Set { value, .. } if is_simple_expr(value) => true,
_ => false,
};
if is_counter_init && scan + 1 < stmts.len() {
let c_sym = match stmt {
Stmt::Let { var, .. } => *var,
Stmt::Set { target, .. } => *target,
_ => unreachable!(),
};
if let Stmt::While { cond, body, .. } = stmts[scan + 1] {
let cond_ok = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq | BinaryOpKind::Lt, left, .. } => {
matches!(left, Expr::Identifier(sym) if *sym == c_sym)
}
_ => false,
};
if cond_ok && body.len() == 2 {
let push_to_dst = match &body[0] {
Stmt::Push { collection, value } => {
if !matches!(collection, Expr::Identifier(s) if *s == dst_sym) {
false
} else if let Expr::Index { index, .. } = value {
matches!(index, Expr::Identifier(s) if *s == c_sym)
} else {
false
}
}
_ => false,
};
let inc_ok = match &body[1] {
Stmt::Set { target, value } if *target == c_sym => {
matches!(value, Expr::BinaryOp { op: BinaryOpKind::Add, left, right }
if (matches!(left, Expr::Identifier(s) if *s == c_sym) && matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1))) && matches!(right, Expr::Identifier(s) if *s == c_sym))
)
}
_ => false,
};
if push_to_dst && inc_ok {
counter_init_idx = Some(scan);
break;
}
}
}
}
if let Stmt::While { cond, body, .. } = stmt {
if body.len() == 2 {
if let Some((c_sym, c_end_expr, c_is_exclusive)) = extract_while_cond(cond) {
if is_simple_expr(c_end_expr) {
if let Some((c_src_sym, c_dst_check)) = extract_push_index_body(body, c_sym) {
if c_dst_check == dst_sym {
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let dst_name = interner.resolve(dst_sym);
let src_name = interner.resolve(c_src_sym);
let counter_name = names.ident(c_sym);
let end_str = codegen_expr_simple(c_end_expr, interner);
let mut cont_output = String::new();
if c_is_exclusive {
writeln!(cont_output, "{}let mut {}: Vec<{}> = {}[({} - 1) as usize..({} - 1) as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, counter_name, end_str).unwrap();
} else {
writeln!(cont_output, "{}let mut {}: Vec<{}> = {}[({} - 1) as usize..{} as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, counter_name, end_str).unwrap();
}
ctx.register_variable_type(dst_sym, format!("Vec<{}>", elem_type));
for si in (idx + 1)..scan {
use super::codegen_stmt;
cont_output.push_str(&codegen_stmt(stmts[si], interner, indent, mutable_vars, ctx,
lww_fields, mv_fields, synced_vars, var_caps, async_functions,
pipe_vars, boxed_fields, registry, type_env));
}
let remaining = &stmts[scan + 1..];
if symbol_appears_in_stmts(c_sym, remaining) {
let post_val = if c_is_exclusive {
end_str.to_string()
} else {
if let Expr::Literal(Literal::Number(n)) = c_end_expr {
format!("{}", n + 1)
} else {
format!("{} + 1", end_str)
}
};
writeln!(cont_output, "{}{} = {};", indent_str, counter_name, post_val).unwrap();
}
let extra_consumed = scan - idx;
return Some((cont_output, extra_consumed));
}
}
}
}
}
}
if symbol_appears_in_stmts(dst_sym, &[stmt]) {
return None;
}
}
let counter_idx = counter_init_idx?;
let while_idx = counter_idx + 1;
let (counter_sym, start_expr, counter_is_new_binding) = match stmts[counter_idx] {
Stmt::Let { var, value, .. } => {
if is_simple_expr(value) {
(*var, *value, true)
} else {
return None;
}
}
Stmt::Set { target, value } => {
if is_simple_expr(value) {
(*target, *value, false)
} else {
return None;
}
}
_ => return None,
};
match stmts[while_idx] {
Stmt::While { cond, body, .. } => {
let (end_expr, is_exclusive) = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(c) = left {
if *c == counter_sym { (Some(*right), false) } else { (None, false) }
} else { (None, false) }
}
Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
if let Expr::Identifier(c) = left {
if *c == counter_sym { (Some(*right), true) } else { (None, false) }
} else { (None, false) }
}
_ => (None, false),
};
let end_expr = end_expr?;
if body.len() != 2 {
return None;
}
let src_sym = match &body[0] {
Stmt::Push { value, collection } => {
if !matches!(collection, Expr::Identifier(s) if *s == dst_sym) {
return None;
}
if let Expr::Index { collection: idx_coll, index: idx_expr } = value {
if !matches!(idx_expr, Expr::Identifier(s) if *s == counter_sym) {
return None;
}
if let Expr::Identifier(s) = idx_coll {
*s
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
};
match &body[1] {
Stmt::Set { target, value } => {
if *target != counter_sym { return None; }
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = match (left, right) {
(Expr::Identifier(s), Expr::Literal(Literal::Number(1))) => *s == counter_sym,
(Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) => *s == counter_sym,
_ => false,
};
if !ok { return None; }
}
_ => return None,
}
}
_ => return None,
}
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let dst_name = interner.resolve(dst_sym);
let src_name = interner.resolve(src_sym);
let counter_name = names.ident(counter_sym);
let is_start_one = matches!(start_expr, Expr::Literal(Literal::Number(1)));
let is_end_length_of_src = if !is_exclusive {
matches!(end_expr, Expr::Length { collection } if matches!(collection, Expr::Identifier(s) if *s == src_sym))
} else {
false
};
let mut output = String::new();
if is_start_one && is_end_length_of_src {
writeln!(output, "{}let mut {}: Vec<{}> = {}.to_vec();",
indent_str, dst_name, elem_type, src_name).unwrap();
} else {
let start_str = codegen_expr_simple(start_expr, interner);
let end_str = codegen_expr_simple(end_expr, interner);
if is_exclusive {
if matches!(start_expr, Expr::Literal(Literal::Number(1))) {
writeln!(output, "{}let mut {}: Vec<{}> = {}[..({} - 1) as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, end_str).unwrap();
} else {
writeln!(output, "{}let mut {}: Vec<{}> = {}[({} - 1) as usize..({} - 1) as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, start_str, end_str).unwrap();
}
} else {
if matches!(start_expr, Expr::Literal(Literal::Number(1))) {
writeln!(output, "{}let mut {}: Vec<{}> = {}[..{} as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, end_str).unwrap();
} else {
writeln!(output, "{}let mut {}: Vec<{}> = {}[({} - 1) as usize..{} as usize].to_vec();",
indent_str, dst_name, elem_type, src_name, start_str, end_str).unwrap();
}
}
}
ctx.register_variable_type(dst_sym, format!("Vec<{}>", elem_type));
for si in (idx + 1)..counter_idx {
use super::codegen_stmt;
output.push_str(&codegen_stmt(stmts[si], interner, indent, mutable_vars, ctx,
lww_fields, mv_fields, synced_vars, var_caps, async_functions,
pipe_vars, boxed_fields, registry, type_env));
}
let remaining = &stmts[while_idx + 1..];
if symbol_appears_in_stmts(counter_sym, remaining) {
let end_str = codegen_expr_simple(end_expr, interner);
let post_val = if is_exclusive {
end_str.to_string()
} else {
format!("{} + 1", end_str)
};
if counter_is_new_binding {
writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_val).unwrap();
} else {
writeln!(output, "{}{} = {};", indent_str, counter_name, post_val).unwrap();
}
}
let extra_consumed = while_idx - idx;
Some((output, extra_consumed))
}
_ => None,
}
}
pub(crate) fn try_emit_vec_with_capacity_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let (vec_sym, collection_info) = match stmts[idx] {
Stmt::Let { var, value, ty, .. } => {
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(CollInfo::Vec(codegen_type_expr(¶ms[0], interner)))
} else if matches!(base_name, "Map" | "HashMap") && params.len() >= 2 {
Some(CollInfo::Map(
codegen_type_expr(¶ms[0], interner),
codegen_type_expr(¶ms[1], interner),
))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() && !type_args.is_empty() {
Some(CollInfo::Vec(codegen_type_expr(&type_args[0], interner)))
} else if matches!(tn, "Map" | "HashMap") && init_fields.is_empty() && type_args.len() >= 2 {
Some(CollInfo::Map(
codegen_type_expr(&type_args[0], interner),
codegen_type_expr(&type_args[1], interner),
))
} else {
None
}
} else {
None
};
match type_from_annotation.or(type_from_new) {
Some(info) => (*var, info),
None => return None,
}
}
_ => return None,
};
let mut counter_init_idx: Option<usize> = None;
for scan in (idx + 1)..stmts.len() {
let stmt = stmts[scan];
let is_counter_init = match stmt {
Stmt::Let { value, .. } if is_simple_expr(value) => true,
Stmt::Set { value, .. } if is_simple_expr(value) => true,
_ => false,
};
if is_counter_init && scan + 1 < stmts.len() {
let counter_sym = match stmt {
Stmt::Let { var, .. } => *var,
Stmt::Set { target, .. } => *target,
_ => unreachable!(),
};
if let Stmt::While { cond, body, .. } = stmts[scan + 1] {
let loop_matches = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq | BinaryOpKind::Lt, left, .. } => {
matches!(left, Expr::Identifier(sym) if *sym == counter_sym)
}
_ => false,
};
if loop_matches && body.len() >= 2 {
let last_is_increment = match &body[body.len() - 1] {
Stmt::Set { target, value } if *target == counter_sym => {
matches!(value, Expr::BinaryOp { op: BinaryOpKind::Add, left, right }
if (matches!(left, Expr::Identifier(s) if *s == counter_sym) && matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1))) && matches!(right, Expr::Identifier(s) if *s == counter_sym))
)
}
_ => false,
};
if last_is_increment {
let body_without_increment = &body[..body.len() - 1];
let has_push = match &collection_info {
CollInfo::Vec(_) => {
body_without_increment.iter().any(|s| {
matches!(s, Stmt::Push { collection, .. } if matches!(collection, Expr::Identifier(sym) if *sym == vec_sym))
}) || all_paths_push_to(body_without_increment, vec_sym)
}
CollInfo::Map(_, _) => {
body_without_increment.iter().any(|s| {
matches!(s, Stmt::SetIndex { collection, .. } if matches!(collection, Expr::Identifier(sym) if *sym == vec_sym))
}) || all_paths_set_index_to(body_without_increment, vec_sym)
}
};
if has_push {
counter_init_idx = Some(scan);
break;
}
}
}
}
}
if symbol_appears_in_stmts(vec_sym, &[stmt]) {
return None;
}
}
let counter_idx = counter_init_idx?;
let while_idx = counter_idx + 1;
let body = match stmts[while_idx] {
Stmt::While { body, .. } => body,
_ => return None,
};
let remaining = &stmts[counter_idx..];
let remaining_refs: Vec<&Stmt> = remaining.iter().copied().collect();
let loop_result = try_emit_for_range_pattern(
&remaining_refs, 0, interner, indent, mutable_vars, ctx,
lww_fields, mv_fields, synced_vars, var_caps, async_functions,
pipe_vars, boxed_fields, registry, type_env,
);
let (loop_code, _) = loop_result?;
let start_str = codegen_expr_simple(match stmts[counter_idx] {
Stmt::Let { value, .. } | Stmt::Set { value, .. } => value,
_ => return None,
}, interner);
let limit_expr = match stmts[while_idx] {
Stmt::While { cond, .. } => match cond {
Expr::BinaryOp { right, .. } => *right,
_ => return None,
},
_ => return None,
};
let is_exclusive = match stmts[while_idx] {
Stmt::While { cond, .. } => matches!(cond, Expr::BinaryOp { op: BinaryOpKind::Lt, .. }),
_ => false,
};
let limit_str = codegen_expr_simple(limit_expr, interner);
let start_lit = match stmts[counter_idx] {
Stmt::Let { value: Expr::Literal(Literal::Number(n)), .. }
| Stmt::Set { value: Expr::Literal(Literal::Number(n)), .. } => Some(*n),
_ => None,
};
let limit_lit = match limit_expr {
Expr::Literal(Literal::Number(n)) => Some(*n),
_ => None,
};
let capacity_expr = match (start_lit, limit_lit) {
(Some(s), Some(l)) => {
let count = if is_exclusive { l - s } else { l - s + 1 };
format!("{}", std::cmp::max(0, count))
}
_ => {
if is_exclusive {
if start_str == "0" {
format!("{} as usize", limit_str)
} else {
format!("({} - {}) as usize", limit_str, start_str)
}
} else {
if start_str == "1" {
format!("{} as usize", limit_str)
} else {
format!("({} - {} + 1) as usize", limit_str, start_str)
}
}
}
};
let indent_str = " ".repeat(indent);
let vec_name = interner.resolve(vec_sym);
let vec_fill_literal = if let CollInfo::Vec(ref elem_type) = collection_info {
let is_copy = matches!(elem_type.as_str(), "i64" | "f64" | "bool");
if is_copy {
let body_without_increment = &body[..body.len() - 1];
if body_without_increment.len() == 1 {
match &body_without_increment[0] {
Stmt::Push { collection, value } => {
let is_target = matches!(collection, Expr::Identifier(sym) if *sym == vec_sym);
if is_target {
match value {
Expr::Literal(Literal::Number(n)) => Some(format!("{}", n)),
Expr::Literal(Literal::Float(f)) => Some(format!("{:.1}", f)),
Expr::Literal(Literal::Boolean(b)) => Some(format!("{}", b)),
_ => None,
}
} else {
None
}
}
_ => None,
}
} else {
None
}
} else {
None
}
} else {
None
};
let mut output = String::new();
match &collection_info {
CollInfo::Vec(elem_type) => {
if let Some(fill_value) = &vec_fill_literal {
writeln!(output, "{}let mut {}: Vec<{}> = vec![{}; {}];",
indent_str, vec_name, elem_type, fill_value, capacity_expr).unwrap();
} else {
writeln!(output, "{}let mut {}: Vec<{}> = Vec::with_capacity({});",
indent_str, vec_name, elem_type, capacity_expr).unwrap();
}
ctx.register_variable_type(vec_sym, format!("Vec<{}>", elem_type));
}
CollInfo::Map(key_type, val_type) => {
writeln!(output, "{}let mut {}: FxHashMap<{}, {}> = FxHashMap::with_capacity_and_hasher({}, Default::default());",
indent_str, vec_name, key_type, val_type, capacity_expr).unwrap();
ctx.register_variable_type(vec_sym, format!("FxHashMap<{}, {}>", key_type, val_type));
}
}
let intervening = &stmts[(idx + 1)..counter_idx];
let body_without_increment = &body[..body.len() - 1];
for stmt in intervening {
let sibling_cap = detect_sibling_collection(stmt, body_without_increment, interner);
if let Some((sib_sym, sib_info)) = sibling_cap {
let sib_name = interner.resolve(sib_sym);
match &sib_info {
CollInfo::Vec(elem_type) => {
writeln!(output, "{}let mut {}: Vec<{}> = Vec::with_capacity({});",
indent_str, sib_name, elem_type, capacity_expr).unwrap();
ctx.register_variable_type(sib_sym, format!("Vec<{}>", elem_type));
}
CollInfo::Map(key_type, val_type) => {
writeln!(output, "{}let mut {}: FxHashMap<{}, {}> = FxHashMap::with_capacity_and_hasher({}, Default::default());",
indent_str, sib_name, key_type, val_type, capacity_expr).unwrap();
ctx.register_variable_type(sib_sym, format!("FxHashMap<{}, {}>", key_type, val_type));
}
}
} else {
use super::codegen_stmt;
output.push_str(&codegen_stmt(stmt, interner, indent, mutable_vars, ctx,
lww_fields, mv_fields, synced_vars, var_caps, async_functions,
pipe_vars, boxed_fields, registry, type_env));
}
}
if vec_fill_literal.is_some() {
if let Some(closing_pos) = loop_code.rfind("\n }") {
let after_loop = &loop_code[closing_pos + 6..]; let trimmed = after_loop.trim_start_matches('\n');
if !trimmed.trim().is_empty() {
output.push_str(trimmed);
}
}
} else {
output.push_str(&loop_code);
}
let extra_consumed = while_idx - idx;
Some((output, extra_consumed))
}
fn detect_sibling_collection<'a>(
stmt: &Stmt<'a>,
body_without_increment: &[Stmt<'a>],
interner: &Interner,
) -> Option<(Symbol, CollInfo)> {
let (var, value, ty) = match stmt {
Stmt::Let { var, value, ty, .. } => (*var, *value, ty.as_ref()),
_ => return None,
};
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(CollInfo::Vec(codegen_type_expr(¶ms[0], interner)))
} else if matches!(base_name, "Map" | "HashMap") && params.len() >= 2 {
Some(CollInfo::Map(
codegen_type_expr(¶ms[0], interner),
codegen_type_expr(¶ms[1], interner),
))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() && !type_args.is_empty() {
Some(CollInfo::Vec(codegen_type_expr(&type_args[0], interner)))
} else if matches!(tn, "Map" | "HashMap") && init_fields.is_empty() && type_args.len() >= 2 {
Some(CollInfo::Map(
codegen_type_expr(&type_args[0], interner),
codegen_type_expr(&type_args[1], interner),
))
} else {
None
}
} else {
None
};
let info = type_from_annotation.or(type_from_new)?;
let has_push = match &info {
CollInfo::Vec(_) => {
body_without_increment.iter().any(|s| {
matches!(s, Stmt::Push { collection, .. } if matches!(collection, Expr::Identifier(sym) if *sym == var))
}) || all_paths_push_to(body_without_increment, var)
}
CollInfo::Map(_, _) => {
body_without_increment.iter().any(|s| {
matches!(s, Stmt::SetIndex { collection, .. } if matches!(collection, Expr::Identifier(sym) if *sym == var))
}) || all_paths_set_index_to(body_without_increment, var)
}
};
if has_push { Some((var, info)) } else { None }
}
pub(crate) fn try_emit_merge_capacity_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
ctx: &mut RefinementContext<'a>,
) -> Option<(String, usize)> {
if idx + 1 >= stmts.len() {
return None;
}
let (vec_sym, elem_type) = match stmts[idx] {
Stmt::Let { var, value, ty, mutable: true, .. } => {
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(codegen_type_expr(¶ms[0], interner))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() && !type_args.is_empty() {
Some(codegen_type_expr(&type_args[0], interner))
} else {
None
}
} else {
None
};
match type_from_annotation.or(type_from_new) {
Some(elem) => (*var, elem),
None => return None,
}
}
_ => return None,
};
let mut source_syms: HashSet<Symbol> = HashSet::new();
let mut last_while_idx = idx;
let mut found_any_while = false;
for scan in (idx + 1)..stmts.len() {
let stmt = stmts[scan];
match stmt {
Stmt::While { body, .. } => {
if all_paths_push_to(body, vec_sym) {
if let Some(sources) = collect_push_sources(body, vec_sym) {
source_syms.extend(sources);
last_while_idx = scan;
found_any_while = true;
} else {
break;
}
} else {
break;
}
}
Stmt::Let { .. } | Stmt::Set { .. } => {
if symbol_appears_in_stmts(vec_sym, &[stmt]) {
break;
}
}
Stmt::Return { .. } => {
break;
}
_ => {
break;
}
}
}
if !found_any_while || source_syms.is_empty() {
return None;
}
let names = RustNames::new(interner);
let vec_name = names.ident(vec_sym);
let indent_str = " ".repeat(indent);
let capacity_parts: Vec<String> = source_syms.iter().map(|sym| {
format!("{}.len()", names.ident(*sym))
}).collect();
let capacity_expr = capacity_parts.join(" + ");
let mut output = String::new();
writeln!(output, "{}let mut {}: Vec<{}> = Vec::with_capacity(({}) as usize);",
indent_str, vec_name, elem_type, capacity_expr).unwrap();
ctx.register_variable_type(vec_sym, format!("Vec<{}>", elem_type));
Some((output, 0))
}
fn collect_push_sources(stmts: &[Stmt], coll_sym: Symbol) -> Option<HashSet<Symbol>> {
let mut sources = HashSet::new();
for stmt in stmts {
match stmt {
Stmt::Push { value, collection } => {
if matches!(collection, Expr::Identifier(sym) if *sym == coll_sym) {
collect_index_sources_from_expr(value, &mut sources);
}
}
Stmt::If { then_block, else_block, .. } => {
if let Some(then_sources) = collect_push_sources(then_block, coll_sym) {
sources.extend(then_sources);
}
if let Some(else_stmts) = else_block {
if let Some(else_sources) = collect_push_sources(else_stmts, coll_sym) {
sources.extend(else_sources);
}
}
}
Stmt::While { body, .. } => {
if let Some(body_sources) = collect_push_sources(body, coll_sym) {
sources.extend(body_sources);
}
}
_ => {}
}
}
if sources.is_empty() {
None
} else {
Some(sources)
}
}
fn collect_index_sources_from_expr(expr: &Expr, sources: &mut HashSet<Symbol>) {
match expr {
Expr::Index { collection, .. } => {
if let Expr::Identifier(sym) = collection {
sources.insert(*sym);
}
}
Expr::Identifier(sym) => {
sources.insert(*sym);
}
_ => {}
}
}
pub(crate) fn try_emit_rotate_left_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
variable_types: &HashMap<Symbol, String>,
) -> Option<(String, usize)> {
if idx + 3 >= stmts.len() {
return None;
}
let (tmp_sym, arr_sym) = match stmts[idx] {
Stmt::Let { var, mutable: false, value, .. } => {
if let Expr::Index { collection, index } = value {
if let Expr::Identifier(a) = collection {
if matches!(index, Expr::Literal(Literal::Number(1))) {
(*var, *a)
} else {
return None;
}
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
};
match variable_types.get(&arr_sym) {
Some(t) if t.starts_with("Vec") => {}
_ => return None,
}
let counter_sym = match stmts[idx + 1] {
Stmt::Set { target, value: Expr::Literal(Literal::Number(1)) } => *target,
_ => return None,
};
let limit_expr = match stmts[idx + 2] {
Stmt::While { cond, body, .. } => {
let limit = match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(c) = left {
if *c == counter_sym { Some(*right) } else { None }
} else {
None
}
}
_ => None,
}?;
if body.len() != 2 {
return None;
}
match &body[0] {
Stmt::SetIndex { collection, index: idx_expr, value } => {
if !matches!(collection, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
if !matches!(idx_expr, Expr::Identifier(s) if *s == counter_sym) {
return None;
}
if let Expr::Index { collection: v_coll, index: v_idx } = value {
if !matches!(v_coll, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
match v_idx {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = (matches!(left, Expr::Identifier(s) if *s == counter_sym)
&& matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1)))
&& matches!(right, Expr::Identifier(s) if *s == counter_sym));
if !ok {
return None;
}
}
_ => return None,
}
} else {
return None;
}
}
_ => return None,
}
match &body[1] {
Stmt::Set { target, value } => {
if *target != counter_sym {
return None;
}
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = (matches!(left, Expr::Identifier(s) if *s == counter_sym)
&& matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1)))
&& matches!(right, Expr::Identifier(s) if *s == counter_sym));
if !ok {
return None;
}
}
_ => return None,
}
}
_ => return None,
}
limit
}
_ => return None,
};
if !is_simple_expr(limit_expr) {
return None;
}
match stmts[idx + 3] {
Stmt::SetIndex { collection, index, value } => {
if !matches!(collection, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
if !matches!(value, Expr::Identifier(s) if *s == tmp_sym) {
return None;
}
match index {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = (exprs_equal(left, limit_expr)
&& matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1)))
&& exprs_equal(right, limit_expr));
if !ok {
return None;
}
}
_ => return None,
}
}
_ => return None,
}
let indent_str = " ".repeat(indent);
let arr_name = interner.resolve(arr_sym);
let tmp_name = interner.resolve(tmp_sym);
let limit_str = codegen_expr_simple(limit_expr, interner);
let mut output = String::new();
let remaining = &stmts[idx + 4..];
if symbol_appears_in_stmts(tmp_sym, remaining) {
writeln!(output, "{}let {} = {}[0];", indent_str, tmp_name, arr_name).unwrap();
}
writeln!(output, "{}{}[0..=({} as usize)].rotate_left(1);",
indent_str, arr_name, limit_str).unwrap();
Some((output, 3)) }
fn try_emit_unconditional_swap<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
tmp_sym: Symbol,
arr_sym: Symbol,
idx_expr_1: &Expr,
interner: &Interner,
indent: usize,
) -> Option<(String, usize)> {
if idx + 2 >= stmts.len() {
return None;
}
let idx_expr_2 = match stmts[idx + 1] {
Stmt::SetIndex { collection, index, value } => {
if !matches!(collection, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
if !exprs_equal(index, idx_expr_1) {
return None;
}
if let Expr::Index { collection: v_coll, index: v_idx } = value {
if !matches!(v_coll, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
*v_idx
} else {
return None;
}
}
_ => return None,
};
match stmts[idx + 2] {
Stmt::SetIndex { collection, index, value } => {
if !matches!(collection, Expr::Identifier(s) if *s == arr_sym) {
return None;
}
if !exprs_equal(index, idx_expr_2) {
return None;
}
if !matches!(value, Expr::Identifier(s) if *s == tmp_sym) {
return None;
}
}
_ => return None,
}
if !is_simple_expr(idx_expr_1) || !is_simple_expr(idx_expr_2) {
return None;
}
let indent_str = " ".repeat(indent);
let arr_name = interner.resolve(arr_sym);
let idx1_simplified = simplify_1based_index(idx_expr_1, interner, true);
let idx2_simplified = simplify_1based_index(idx_expr_2, interner, true);
let mut output = String::new();
writeln!(output, "{}let __swap_tmp = {}[{}];",
indent_str, arr_name, idx1_simplified).unwrap();
writeln!(output, "{}{}[{}] = {}[{}];",
indent_str, arr_name, idx1_simplified, arr_name, idx2_simplified).unwrap();
writeln!(output, "{}{}[{}] = __swap_tmp;",
indent_str, arr_name, idx2_simplified).unwrap();
Some((output, 2)) }
pub(crate) fn try_emit_bare_slice_push_pattern<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
variable_types: &HashMap<Symbol, String>,
) -> Option<(String, usize)> {
if let Some(result) = try_bare_slice_push_with_init(stmts, idx, interner, indent, variable_types) {
return Some(result);
}
try_bare_slice_push_bare_while(stmts, idx, interner, indent, variable_types)
}
fn try_bare_slice_push_with_init<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
variable_types: &HashMap<Symbol, String>,
) -> Option<(String, usize)> {
if idx + 1 >= stmts.len() {
return None;
}
let (counter_sym, start_expr, is_new_binding) = match stmts[idx] {
Stmt::Let { var, value, .. } => {
if is_simple_expr(value) {
(*var, *value, true)
} else {
return None;
}
}
Stmt::Set { target, value } => {
if is_simple_expr(value) {
(*target, *value, false)
} else {
return None;
}
}
_ => return None,
};
let while_info = extract_push_copy_while(stmts[idx + 1], counter_sym)?;
validate_slice_push_types(while_info.src_sym, while_info.dst_sym, variable_types)?;
let remaining = &stmts[idx + 2..];
let output = emit_extend_from_slice(
interner, indent, while_info.dst_sym, while_info.src_sym, counter_sym,
start_expr, while_info.end_expr, while_info.is_exclusive,
remaining, Some(is_new_binding),
);
Some((output, 1)) }
fn try_bare_slice_push_bare_while<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
variable_types: &HashMap<Symbol, String>,
) -> Option<(String, usize)> {
let while_stmt = stmts[idx];
let (counter_sym, end_expr, is_exclusive, src_sym, dst_sym) = match while_stmt {
Stmt::While { cond, body, .. } => {
let (counter_sym, end_expr, is_exclusive) = extract_while_cond(cond)?;
if !is_simple_expr(end_expr) { return None; }
if body.len() != 2 { return None; }
let (src, dst) = extract_push_index_body(body, counter_sym)?;
(counter_sym, end_expr, is_exclusive, src, dst)
}
_ => return None,
};
validate_slice_push_types(src_sym, dst_sym, variable_types)?;
let start_expr = Expr::Identifier(counter_sym);
let remaining = &stmts[idx + 1..];
let output = emit_extend_from_slice(
interner, indent, dst_sym, src_sym, counter_sym,
&start_expr, end_expr, is_exclusive,
remaining, None, );
Some((output, 0)) }
struct WhileInfo<'a> {
end_expr: &'a Expr<'a>,
is_exclusive: bool,
src_sym: Symbol,
dst_sym: Symbol,
}
fn extract_push_copy_while<'a>(
stmt: &'a Stmt<'a>,
counter_sym: Symbol,
) -> Option<WhileInfo<'a>> {
let (cond, body) = match stmt {
Stmt::While { cond, body, .. } => (cond, body),
_ => return None,
};
let (cond_counter, end_expr, is_exclusive) = extract_while_cond(cond)?;
if cond_counter != counter_sym { return None; }
if !is_simple_expr(end_expr) { return None; }
if body.len() != 2 { return None; }
let (src_sym, dst_sym) = extract_push_index_body(body, counter_sym)?;
validate_increment(&body[1], counter_sym)?;
Some(WhileInfo { end_expr, is_exclusive, src_sym, dst_sym })
}
fn extract_while_cond<'a>(cond: &'a Expr<'a>) -> Option<(Symbol, &'a Expr<'a>, bool)> {
match cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, right } => {
if let Expr::Identifier(sym) = left {
Some((*sym, *right, false))
} else {
None
}
}
Expr::BinaryOp { op: BinaryOpKind::Lt, left, right } => {
if let Expr::Identifier(sym) = left {
Some((*sym, *right, true))
} else {
None
}
}
_ => None,
}
}
fn extract_push_index_body<'a>(body: &[Stmt<'a>], counter_sym: Symbol) -> Option<(Symbol, Symbol)> {
let (src, dst) = match &body[0] {
Stmt::Push { value, collection } => {
let dst = if let Expr::Identifier(s) = collection { *s } else { return None; };
if let Expr::Index { collection: src_coll, index } = value {
if !matches!(index, Expr::Identifier(s) if *s == counter_sym) {
return None;
}
if let Expr::Identifier(s) = src_coll { (*s, dst) } else { return None; }
} else {
return None;
}
}
_ => return None,
};
validate_increment(&body[1], counter_sym)?;
Some((src, dst))
}
fn validate_increment(stmt: &Stmt, counter_sym: Symbol) -> Option<()> {
match stmt {
Stmt::Set { target, value } if *target == counter_sym => {
match value {
Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
let ok = matches!((left, right),
(Expr::Identifier(s), Expr::Literal(Literal::Number(1))) if *s == counter_sym
) || matches!((left, right),
(Expr::Literal(Literal::Number(1)), Expr::Identifier(s)) if *s == counter_sym
);
if ok { Some(()) } else { None }
}
_ => None,
}
}
_ => None,
}
}
fn validate_slice_push_types(
src_sym: Symbol,
dst_sym: Symbol,
variable_types: &HashMap<Symbol, String>,
) -> Option<()> {
if src_sym == dst_sym { return None; }
let dst_type = variable_types.get(&dst_sym)?;
if !dst_type.starts_with("Vec<") { return None; }
let src_type = variable_types.get(&src_sym)?;
if !src_type.starts_with("Vec<") && !src_type.starts_with("&[") && !src_type.starts_with("&mut [") {
return None;
}
Some(())
}
fn emit_extend_from_slice(
interner: &Interner,
indent: usize,
dst_sym: Symbol,
src_sym: Symbol,
counter_sym: Symbol,
start_expr: &Expr,
end_expr: &Expr,
is_exclusive: bool,
remaining: &[&Stmt],
binding_info: Option<bool>, ) -> String {
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let dst_name = names.ident(dst_sym);
let src_name = names.ident(src_sym);
let counter_name = names.ident(counter_sym);
let start_str = codegen_expr_simple(start_expr, interner);
let end_str = codegen_expr_simple(end_expr, interner);
let mut output = String::new();
if is_exclusive {
writeln!(output, "{}if {} < {} {{", indent_str, start_str, end_str).unwrap();
writeln!(output, "{} {}.extend_from_slice(&{}[({} - 1) as usize..({} - 1) as usize]);",
indent_str, dst_name, src_name, start_str, end_str).unwrap();
writeln!(output, "{}}}", indent_str).unwrap();
} else {
writeln!(output, "{}if {} <= {} {{", indent_str, start_str, end_str).unwrap();
writeln!(output, "{} {}.extend_from_slice(&{}[({} - 1) as usize..{} as usize]);",
indent_str, dst_name, src_name, start_str, end_str).unwrap();
writeln!(output, "{}}}", indent_str).unwrap();
}
if symbol_appears_in_stmts(counter_sym, remaining) {
let post_val = if is_exclusive {
end_str.to_string()
} else {
if let Expr::Literal(Literal::Number(n)) = end_expr {
format!("{}", n + 1)
} else {
format!("{} + 1", end_str)
}
};
match binding_info {
Some(true) => {
writeln!(output, "{}let mut {} = {};", indent_str, counter_name, post_val).unwrap();
}
Some(false) | None => {
writeln!(output, "{}{} = {};", indent_str, counter_name, post_val).unwrap();
}
}
}
output
}
pub(crate) fn try_emit_drain_tail_in_while<'a>(
stmt: &Stmt<'a>,
while_cond: &Expr<'a>,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<String> {
let counter_sym = match while_cond {
Expr::BinaryOp { op: BinaryOpKind::LtEq, left, .. } => {
if let Expr::Identifier(sym) = left {
*sym
} else {
return None;
}
}
_ => return None,
};
let (if_cond, then_block, else_block) = match stmt {
Stmt::If { cond, then_block, else_block: Some(else_block) } => {
(*cond, then_block, else_block)
}
_ => return None,
};
if then_block.len() != 2 {
return None;
}
let (target_sym, array_sym, push_counter_sym) = match &then_block[0] {
Stmt::Push { value, collection } => {
let tgt = if let Expr::Identifier(s) = collection { *s } else { return None; };
if let Expr::Index { collection: arr_expr, index: idx_expr } = value {
let arr_sym = if let Expr::Identifier(s) = arr_expr { *s } else { return None; };
let idx_sym = if let Expr::Identifier(s) = idx_expr { *s } else { return None; };
(tgt, arr_sym, idx_sym)
} else {
return None;
}
}
_ => return None,
};
validate_increment(&then_block[1], push_counter_sym)?;
if push_counter_sym != counter_sym {
return None;
}
validate_slice_push_types(array_sym, target_sym, ctx.get_variable_types())?;
let mut cond_syms = Vec::new();
collect_expr_symbols(if_cond, &mut cond_syms);
for sym in &cond_syms {
if body_modifies_var(then_block, *sym) || body_mutates_collection(then_block, *sym) {
return None;
}
}
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let target_name = names.ident(target_sym);
let array_name = names.ident(array_sym);
let counter_name = names.ident(push_counter_sym);
use super::expr::codegen_expr_with_async;
let cond_str = codegen_expr_with_async(if_cond, interner, synced_vars, async_functions, ctx.get_variable_types());
let mut output = String::new();
writeln!(output, "{}if {} {{", indent_str, cond_str).unwrap();
writeln!(output, "{} {}.extend_from_slice(&{}[({} - 1) as usize..]);",
indent_str, target_name, array_name, counter_name).unwrap();
writeln!(output, "{} break;", indent_str).unwrap();
writeln!(output, "{}}} else {{", indent_str).unwrap();
for else_stmt in else_block.iter() {
output.push_str(&super::codegen_stmt(else_stmt, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env));
}
writeln!(output, "{}}}", indent_str).unwrap();
Some(output)
}
pub(crate) fn detect_double_buffer_swap<'a>(
body: &[Stmt<'a>],
interner: &Interner,
ctx: &RefinementContext<'a>,
) -> Option<(Symbol, Symbol, usize)> {
if body.len() < 2 {
return None;
}
let (x_sym, y_sym, set_idx) = {
let last_idx = body.len() - 1;
match &body[last_idx] {
Stmt::Set { target, value } => {
if let Expr::Identifier(src) = value {
if *src != *target {
(*target, *src, last_idx)
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
}
};
let x_type = ctx.get_variable_types().get(&x_sym)?;
let y_type = ctx.get_variable_types().get(&y_sym)?;
if !x_type.starts_with("Vec<") || x_type != y_type {
return None;
}
for stmt in body.iter() {
if let Stmt::Let { var, .. } = stmt {
if *var == y_sym {
return None;
}
}
}
if body_resizes_collection(&body[..set_idx], y_sym) {
return None;
}
let mut has_overwrite_loop = false;
for stmt in body[..set_idx].iter() {
if let Stmt::While { body: inner_body, .. } = stmt {
if !inner_body.is_empty() {
if let Stmt::SetIndex { collection, .. } = &inner_body[0] {
if let Expr::Identifier(coll_sym) = collection {
if *coll_sym == y_sym {
if let Stmt::SetIndex { value, .. } = &inner_body[0] {
let mut syms = Vec::new();
collect_expr_symbols(value, &mut syms);
if !syms.contains(&y_sym) {
has_overwrite_loop = true;
break;
}
}
}
}
}
}
}
}
if !has_overwrite_loop {
return None;
}
Some((x_sym, y_sym, set_idx))
}
pub(crate) struct BufferReuseInfo {
pub inner_sym: Symbol,
pub outer_sym: Symbol,
pub inner_elem_type: String,
pub inner_let_idx: usize,
pub set_idx: usize,
}
pub(crate) fn detect_buffer_reuse_in_body<'a>(
body: &[Stmt<'a>],
interner: &Interner,
ctx: &RefinementContext<'a>,
) -> Option<BufferReuseInfo> {
if body.len() < 2 {
return None;
}
let (inner_sym, inner_elem_type, inner_let_idx) = {
let mut found = None;
for (bi, stmt) in body.iter().enumerate() {
if bi > 1 { break; }
if let Stmt::Let { var, value, ty, mutable: true } = stmt {
let type_from_annotation = if let Some(TypeExpr::Generic { base, params }) = ty {
let base_name = interner.resolve(*base);
if matches!(base_name, "Seq" | "List" | "Vec") && !params.is_empty() {
Some(codegen_type_expr(¶ms[0], interner))
} else {
None
}
} else {
None
};
let type_from_new = if let Expr::New { type_name, type_args, init_fields } = value {
let tn = interner.resolve(*type_name);
if matches!(tn, "Seq" | "List" | "Vec") && init_fields.is_empty() && !type_args.is_empty() {
Some(codegen_type_expr(&type_args[0], interner))
} else {
None
}
} else {
None
};
if let Some(t) = type_from_annotation.or(type_from_new) {
found = Some((*var, t, bi));
break;
}
}
}
found?
};
let (outer_sym, set_idx) = {
let mut found = None;
for (bi, stmt) in body.iter().enumerate().rev() {
if let Stmt::Set { target, value } = stmt {
if let Expr::Identifier(src) = value {
if *src == inner_sym && *target != inner_sym {
found = Some((*target, bi));
break;
}
}
}
}
found?
};
if set_idx == body.len().wrapping_sub(2) && body.len() >= 2 {
match &body[body.len() - 1] {
Stmt::Set { target, value } => {
let is_increment = matches!(value,
Expr::BinaryOp { op: BinaryOpKind::Add, left, right }
if (matches!(left, Expr::Identifier(s) if *s == *target) && matches!(right, Expr::Literal(Literal::Number(1))))
|| (matches!(left, Expr::Literal(Literal::Number(1))) && matches!(right, Expr::Identifier(s) if *s == *target))
);
if !is_increment {
return None;
}
}
_ => return None,
}
} else if set_idx != body.len() - 1 {
return None;
}
let outer_type = ctx.get_variable_types().get(&outer_sym)?;
let expected_prefix = format!("Vec<{}>", inner_elem_type);
if !outer_type.starts_with(&expected_prefix) {
return None;
}
for bi in (set_idx + 1)..body.len() {
let stmt_ref: &Stmt = &body[bi];
if symbol_appears_in_stmts(inner_sym, &[stmt_ref]) {
return None;
}
}
Some(BufferReuseInfo {
inner_sym,
outer_sym,
inner_elem_type,
inner_let_idx,
set_idx,
})
}
pub(crate) fn try_emit_buffer_reuse_while<'a>(
stmts: &[&Stmt<'a>],
idx: usize,
interner: &Interner,
indent: usize,
mutable_vars: &HashSet<Symbol>,
ctx: &mut RefinementContext<'a>,
lww_fields: &HashSet<(String, String)>,
mv_fields: &HashSet<(String, String)>,
synced_vars: &mut HashSet<Symbol>,
var_caps: &HashMap<Symbol, VariableCapabilities>,
async_functions: &HashSet<Symbol>,
pipe_vars: &HashSet<Symbol>,
boxed_fields: &HashSet<(String, String, String)>,
registry: &TypeRegistry,
type_env: &crate::analysis::types::TypeEnv,
) -> Option<(String, usize)> {
let (cond, body) = match stmts[idx] {
Stmt::While { cond, body, .. } => (cond, body),
_ => return None,
};
if body.len() < 3 {
return None;
}
let reuse = detect_buffer_reuse_in_body(body, interner, ctx)?;
let indent_str = " ".repeat(indent);
let names = RustNames::new(interner);
let inner_name = names.ident(reuse.inner_sym);
let outer_name = names.ident(reuse.outer_sym);
let mut output = String::new();
writeln!(output, "{}let mut {}: Vec<{}> = Vec::new();", indent_str, inner_name, reuse.inner_elem_type).unwrap();
use super::stmt::{extract_length_expr_syms, collect_length_syms_from_stmts};
let mut all_length_syms_raw = extract_length_expr_syms(cond);
collect_length_syms_from_stmts(body, &mut all_length_syms_raw);
let mut seen = HashSet::new();
let all_length_syms: Vec<Symbol> = all_length_syms_raw
.into_iter()
.filter(|s| seen.insert(*s))
.collect();
let mut hoisted_syms: Vec<(Symbol, Option<String>)> = Vec::new();
for len_sym in &all_length_syms {
if !body_mutates_collection(body, *len_sym) && !body_modifies_var(body, *len_sym) {
let name = interner.resolve(*len_sym);
let hoisted_name = format!("{}_len", name);
writeln!(output, "{}let {} = ({}.len() as i64);", indent_str, hoisted_name, name).unwrap();
let old_type = ctx.get_variable_types().get(len_sym).cloned();
let new_type = match &old_type {
Some(existing) => format!("{}|__hl:{}", existing, hoisted_name),
None => format!("|__hl:{}", hoisted_name),
};
ctx.register_variable_type(*len_sym, new_type);
hoisted_syms.push((*len_sym, old_type));
}
}
use super::expr::codegen_expr_with_async;
let cond_str = codegen_expr_with_async(cond, interner, synced_vars, async_functions, ctx.get_variable_types());
writeln!(output, "{}while {} {{", indent_str, cond_str).unwrap();
ctx.push_scope();
let body_refs: Vec<&Stmt> = body.iter().collect();
let mut bi = 0;
while bi < body_refs.len() {
if bi == reuse.inner_let_idx {
writeln!(output, "{} {}.clear();", indent_str, inner_name).unwrap();
ctx.register_variable_type(reuse.inner_sym, format!("Vec<{}>", reuse.inner_elem_type));
bi += 1;
continue;
}
if bi == reuse.set_idx {
writeln!(output, "{} std::mem::swap(&mut {}, &mut {});", indent_str, outer_name, inner_name).unwrap();
bi += 1;
continue;
}
if let Some((code, skip)) = try_emit_seq_from_slice_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_fill_pattern(&body_refs, bi, interner, indent + 1, ctx) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_vec_with_capacity_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_merge_capacity_pattern(&body_refs, bi, interner, indent + 1, ctx) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_for_range_pattern(&body_refs, bi, interner, indent + 1, mutable_vars, ctx, lww_fields, mv_fields, synced_vars, var_caps, async_functions, pipe_vars, boxed_fields, registry, type_env) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_swap_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_seq_copy_pattern(&body_refs, bi, interner, indent + 1, ctx) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
if let Some((code, skip)) = try_emit_rotate_left_pattern(&body_refs, bi, interner, indent + 1, ctx.get_variable_types()) {
output.push_str(&code);
bi += 1 + skip;
continue;
}
use super::codegen_stmt;
output.push_str(&codegen_stmt(body_refs[bi], interner, indent + 1, mutable_vars, ctx,
lww_fields, mv_fields, synced_vars, var_caps, async_functions,
pipe_vars, boxed_fields, registry, type_env));
bi += 1;
}
ctx.pop_scope();
for (sym, old_type) in hoisted_syms {
if let Some(old) = old_type {
ctx.register_variable_type(sym, old);
} else {
ctx.get_variable_types_mut().remove(&sym);
}
}
writeln!(output, "{}}}", indent_str).unwrap();
Some((output, 0))
}