fix: track temporary strings from toString and concat for RC cleanup
- toString now stores result in temp variable and registers for RC - String concatenation stores result and registers for RC - Immediately decref temporary input strings after concat to avoid leaks - Add is_rc_temp() helper to identify RC temporary variables This fixes the memory leak where dynamically created strings from toString() and string concatenation were not being freed. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1748,7 +1748,21 @@ impl CBackend {
|
|||||||
let left_is_string = self.is_string_expr(left);
|
let left_is_string = self.is_string_expr(left);
|
||||||
let right_is_string = self.is_string_expr(right);
|
let right_is_string = self.is_string_expr(right);
|
||||||
if left_is_string || right_is_string {
|
if left_is_string || right_is_string {
|
||||||
return Ok(format!("lux_string_concat({}, {})", l, r));
|
// String concat returns RC-managed string - track it
|
||||||
|
let temp = format!("_str_concat_{}", self.fresh_name());
|
||||||
|
self.writeln(&format!("LuxString {} = lux_string_concat({}, {});", temp, l, r));
|
||||||
|
self.register_rc_var(&temp, "LuxString");
|
||||||
|
// Also need to decref any temporary strings used as inputs
|
||||||
|
// (toString results, nested concats)
|
||||||
|
if self.is_rc_temp(&l) {
|
||||||
|
self.writeln(&format!("lux_decref_string({});", l));
|
||||||
|
self.unregister_rc_var(&l);
|
||||||
|
}
|
||||||
|
if self.is_rc_temp(&r) {
|
||||||
|
self.writeln(&format!("lux_decref_string({});", r));
|
||||||
|
self.unregister_rc_var(&r);
|
||||||
|
}
|
||||||
|
return Ok(temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1856,9 +1870,12 @@ impl CBackend {
|
|||||||
// Check for built-in functions like toString
|
// Check for built-in functions like toString
|
||||||
if let Expr::Var(ident) = func.as_ref() {
|
if let Expr::Var(ident) = func.as_ref() {
|
||||||
if ident.name == "toString" {
|
if ident.name == "toString" {
|
||||||
// toString converts Int to String
|
// toString converts Int to String - returns RC-managed string
|
||||||
let arg = self.emit_expr(&args[0])?;
|
let arg = self.emit_expr(&args[0])?;
|
||||||
return Ok(format!("lux_int_to_string({})", arg));
|
let temp = format!("_to_string_{}", self.fresh_name());
|
||||||
|
self.writeln(&format!("LuxString {} = lux_int_to_string({});", temp, arg));
|
||||||
|
self.register_rc_var(&temp, "LuxString");
|
||||||
|
return Ok(temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3293,6 +3310,11 @@ impl CBackend {
|
|||||||
self.rc_scopes.iter().any(|scope| scope.iter().any(|var| var.name == name))
|
self.rc_scopes.iter().any(|scope| scope.iter().any(|var| var.name == name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if a value is a temporary RC variable (starts with _ and is tracked)
|
||||||
|
fn is_rc_temp(&self, name: &str) -> bool {
|
||||||
|
name.starts_with("_") && self.is_rc_tracked(name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a variable from RC tracking (for ownership transfer)
|
/// Remove a variable from RC tracking (for ownership transfer)
|
||||||
fn unregister_rc_var(&mut self, name: &str) {
|
fn unregister_rc_var(&mut self, name: &str) {
|
||||||
for scope in self.rc_scopes.iter_mut() {
|
for scope in self.rc_scopes.iter_mut() {
|
||||||
|
|||||||
Reference in New Issue
Block a user