use crate::parser::*;
use super::{pattern_analysis, CodeGenerator};
impl<'ast> CodeGenerator<'ast> {
pub(in crate::codegen::rust) fn generate_for_statement(
&mut self,
pattern: &Pattern<'ast>,
iterable: &'ast Expression<'ast>,
body: &[&'ast Statement<'ast>],
location: &crate::parser::ast::SourceLocation,
) -> String {
if let Some(simd_block) =
super::simd_transform::try_emit_simd_for_loop(self, pattern, iterable, body)
{
return simd_block;
}
let mut output = self.indent();
output.push_str("for ");
let pattern_str = self.pattern_to_rust(pattern);
let loop_var = pattern_analysis::extract_pattern_identifier(pattern);
let mut all_pattern_bindings = std::collections::HashSet::new();
self.extract_pattern_bindings(pattern, &mut all_pattern_bindings);
let needs_mut = if let Some(var) = loop_var.as_ref() {
self.loop_body_modifies_variable(body, var)
|| self.loop_body_calls_mut_dispatch_method(iterable, body, var)
} else {
all_pattern_bindings.iter().any(|var| {
self.loop_body_modifies_variable(body, var)
|| self.loop_body_calls_mut_dispatch_method(iterable, body, var)
})
};
let needs_borrow = self.should_borrow_for_iteration(iterable)
|| self.self_field_iterable_needs_borrow(iterable, body);
let needs_mut_borrow = needs_mut && needs_borrow;
let iterable_already_mut_ref = matches!(
iterable,
Expression::Unary {
op: UnaryOp::MutRef,
..
}
);
if needs_mut && !needs_mut_borrow && !iterable_already_mut_ref {
output.push_str("mut ");
}
let is_unused_loop_var = location
.as_ref()
.is_some_and(|loc| self.unused_let_bindings.contains(&(loc.line, loc.column)));
let display_pattern = if is_unused_loop_var {
format!("_{}", pattern_str)
} else {
pattern_str
};
output.push_str(&display_pattern);
output.push_str(" in ");
let is_borrowed_iterator = needs_borrow || self.is_iterating_over_borrowed(iterable);
if needs_mut_borrow {
output.push_str("&mut ");
} else if needs_borrow {
output.push('&');
}
let iterable_to_generate = if let Expression::Unary {
op: crate::parser::UnaryOp::Ref,
operand,
..
} = iterable
{
if let Expression::Identifier { name, .. } = &**operand {
if self.inferred_borrowed_params.contains(name) {
operand
} else {
iterable
}
} else {
iterable
}
} else {
iterable
};
let prev_field_access = self.in_field_access_object;
self.in_field_access_object = true;
output.push_str(&self.generate_expression(iterable_to_generate));
self.in_field_access_object = prev_field_access;
output.push_str(" {\n");
self.indent_level += 1;
let mut borrowed_bindings_added: Vec<String> = Vec::new();
if is_borrowed_iterator {
let enumerate_index_var = Self::extract_enumerate_index_var(iterable, pattern);
let mut all_bindings = std::collections::HashSet::new();
self.extract_pattern_bindings(pattern, &mut all_bindings);
for var in all_bindings {
if Some(&var) == enumerate_index_var.as_ref() {
continue;
}
self.borrowed_iterator_vars.insert(var.clone());
borrowed_bindings_added.push(var.clone());
if needs_mut_borrow {
self.mut_borrowed_iterator_vars.insert(var);
}
}
}
let is_owned_string_iterator = !is_borrowed_iterator;
if is_owned_string_iterator {
if let Some(var) = &loop_var {
self.owned_string_iterator_vars.insert(var.clone());
}
}
if let Some(var) = &loop_var {
if let Expression::Range { end, .. } = iterable {
if self.expression_produces_usize(end) {
self.usize_variables.insert(var.clone());
}
}
}
if let Some(iterable_type) = self.infer_expression_type(iterable) {
if let Some(elem_type) = Self::extract_iterator_element_type(&iterable_type) {
match pattern {
Pattern::Identifier(var) => {
self.local_var_types.insert(var.clone(), elem_type);
}
Pattern::Tuple(patterns) => {
if let Type::Tuple(tuple_types) = &elem_type {
for (pat, ty) in patterns.iter().zip(tuple_types.iter()) {
if let Pattern::Identifier(var) = pat {
self.local_var_types.insert(var.clone(), ty.clone());
}
}
}
}
_ => {
if let Some(var) = &loop_var {
self.local_var_types.insert(var.clone(), elem_type);
}
}
}
}
}
let saved_body = self.current_function_body.clone();
let saved_idx = self.current_statement_idx;
let saved_local_idx = self.current_block_local_idx;
self.current_function_body = body.to_vec();
for (i, stmt) in body.iter().enumerate() {
self.current_statement_idx = self.auto_clone_counter;
self.current_block_local_idx = i;
self.auto_clone_counter += 1;
output.push_str(&self.generate_statement(stmt));
}
self.current_function_body = saved_body;
self.current_statement_idx = saved_idx;
self.current_block_local_idx = saved_local_idx;
if is_borrowed_iterator {
for var in &borrowed_bindings_added {
self.borrowed_iterator_vars.remove(var);
self.mut_borrowed_iterator_vars.remove(var);
}
}
if is_owned_string_iterator {
if let Some(var) = &loop_var {
self.owned_string_iterator_vars.remove(var);
}
}
for var in &borrowed_bindings_added {
self.local_var_types.remove(var);
}
if let Some(var) = &loop_var {
self.local_var_types.remove(var);
}
self.indent_level -= 1;
output.push_str(&self.indent());
output.push_str("}\n");
output
}
fn extract_enumerate_index_var(
iterable: &Expression<'ast>,
pattern: &Pattern<'ast>,
) -> Option<String> {
let is_enumerate = matches!(
iterable,
Expression::MethodCall { method, .. } if method == "enumerate"
);
if !is_enumerate {
return None;
}
if let Pattern::Tuple(elements) = pattern {
if let Some(Pattern::Identifier(name)) = elements.first() {
return Some(name.clone());
}
}
None
}
}