diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 25a5399..d8ee8ec 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -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() {