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 right_is_string = self.is_string_expr(right);
|
||||
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
|
||||
if let Expr::Var(ident) = func.as_ref() {
|
||||
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])?;
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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)
|
||||
fn unregister_rc_var(&mut self, name: &str) {
|
||||
for scope in self.rc_scopes.iter_mut() {
|
||||
|
||||
Reference in New Issue
Block a user