Fixed stack overflow error caused by improper cleanup of block scoped variables

This commit is contained in:
2025-12-02 23:37:13 -07:00
parent baab8b9d0b
commit e05f130040
2 changed files with 35 additions and 20 deletions

View File

@@ -1252,7 +1252,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn expression_block<'v>( fn expression_block<'v>(
&mut self, &mut self,
mut expr: BlockExpression, mut expr: BlockExpression,
scope: &mut VariableScope<'v>, parent_scope: &mut VariableScope<'v>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// First, sort the expressions to ensure functions are hoisted // First, sort the expressions to ensure functions are hoisted
expr.0.sort_by(|a, b| { expr.0.sort_by(|a, b| {
@@ -1267,10 +1267,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
}); });
let mut scope = VariableScope::scoped(parent_scope);
for expr in expr.0 { for expr in expr.0 {
if !self.declared_main if !self.declared_main
&& !matches!(expr.node, Expression::Function(_)) && !matches!(expr.node, Expression::Function(_))
&& !scope.has_parent() && !parent_scope.has_parent()
{ {
self.write_output("main:")?; self.write_output("main:")?;
self.declared_main = true; self.declared_main = true;
@@ -1278,11 +1280,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
match expr.node { match expr.node {
Expression::Return(ret_expr) => { Expression::Return(ret_expr) => {
self.expression_return(*ret_expr, scope)?; self.expression_return(*ret_expr, &mut scope)?;
} }
_ => { _ => {
// Swallow errors within expressions so block can continue // Swallow errors within expressions so block can continue
if let Err(e) = self.expression(expr, scope).and_then(|result| { if let Err(e) = self.expression(expr, &mut scope).and_then(|result| {
// If the expression was a statement that returned a temp result (e.g. `1 + 2;` line), // If the expression was a statement that returned a temp result (e.g. `1 + 2;` line),
// we must free it to avoid leaking registers. // we must free it to avoid leaking registers.
if let Some(comp_res) = result if let Some(comp_res) = result
@@ -1298,6 +1300,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
} }
if scope.stack_offset() > 0 {
self.write_output(format!("sub sp sp {}", scope.stack_offset()))?;
}
Ok(()) Ok(())
} }
@@ -1700,4 +1706,3 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
} }

View File

@@ -91,6 +91,8 @@ impl<'a> VariableScope<'a> {
pub fn scoped(parent: &'a VariableScope<'a>) -> Self { pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
Self { Self {
parent: Option::Some(parent), parent: Option::Some(parent),
temporary_vars: parent.temporary_vars.clone(),
persistant_vars: parent.persistant_vars.clone(),
..Default::default() ..Default::default()
} }
} }
@@ -140,26 +142,34 @@ impl<'a> VariableScope<'a> {
Ok(var_location) Ok(var_location)
} }
pub fn get_location_of( pub fn get_location_of(&self, var_name: impl Into<String>) -> Result<VariableLocation, Error> {
&mut self,
var_name: impl Into<String>,
) -> Result<VariableLocation, Error> {
let var_name = var_name.into(); let var_name = var_name.into();
let var = self
.var_lookup_table
.get(var_name.as_str())
.cloned()
.ok_or(Error::UnknownVariable(var_name))?;
// 1. Check this scope
if let Some(var) = self.var_lookup_table.get(var_name.as_str()) {
if let VariableLocation::Stack(inserted_at_offset) = var { if let VariableLocation::Stack(inserted_at_offset) = var {
Ok(VariableLocation::Stack( // Return offset relative to CURRENT sp
return Ok(VariableLocation::Stack(
self.stack_offset - inserted_at_offset, self.stack_offset - inserted_at_offset,
)) ));
} else { } else {
Ok(var) return Ok(var.clone());
} }
} }
// 2. Recursively check parent
if let Some(parent) = self.parent {
let loc = parent.get_location_of(var_name)?;
if let VariableLocation::Stack(parent_offset) = loc {
return Ok(VariableLocation::Stack(parent_offset + self.stack_offset));
}
return Ok(loc);
}
Err(Error::UnknownVariable(var_name))
}
pub fn has_parent(&self) -> bool { pub fn has_parent(&self) -> bool {
self.parent.is_some() self.parent.is_some()
} }