diff --git a/docs/C_BACKEND.md b/docs/C_BACKEND.md index 5767780..0c53bc3 100644 --- a/docs/C_BACKEND.md +++ b/docs/C_BACKEND.md @@ -164,27 +164,26 @@ result->length = nums->length; ## Current Limitations -### 1. Memory Leaks +### 1. Memory Management (Partial RC) -**Everything allocated is never freed.** This includes: -- Closure environments -- ADT values -- List elements and arrays -- Strings from concatenation +RC infrastructure is implemented but not fully integrated: +- ✅ Lists, boxed values, and strings use RC allocation +- ✅ List operations properly incref shared elements +- ⏳ Automatic decref at scope exit (not yet implemented) +- ⏳ Closures and ADTs still leak -This is acceptable for short-lived programs but not for long-running services. +**Current state:** Memory is tracked with refcounts, but objects are not automatically freed at scope exit. This is acceptable for short-lived programs but not for long-running services. -### 2. Limited Effects +### 2. Effects ✅ MOSTLY COMPLETE -Only `Console.print` is supported, hardcoded to `printf`: +All major effects are now supported: +- `Console` (print, readLine) +- `Random` (int, float, bool) +- `Time` (now, sleep) +- `File` (read, write, append, exists, delete, isDir, mkdir) +- `Http` (get, post, put, delete) -```c -static void lux_console_print(LuxString msg) { - printf("%s\n", msg); -} -``` - -Other effects (File, Http, Random, etc.) are not yet implemented in the C backend. +All effects use evidence passing for O(1) handler lookup. ### 3. If/Else Side Effects @@ -275,7 +274,7 @@ See [docs/EVIDENCE_PASSING.md](EVIDENCE_PASSING.md) for details. ## Future Roadmap -### Phase 4: Perceus Reference Counting +### Phase 4: Perceus Reference Counting 🔄 IN PROGRESS **Goal:** Deterministic memory management without GC pauses. @@ -284,7 +283,18 @@ Perceus is a compile-time reference counting system that: 2. Detects when values can be reused in-place (FBIP) 3. Guarantees no memory leaks without runtime GC -**Example - reuse analysis:** +**Current Status:** +- ✅ RC infrastructure (header, alloc, incref/decref, drop) +- ✅ Lists use RC allocation with proper element incref +- ✅ Boxed values (Int, Bool, Float) use RC allocation +- ✅ Dynamic strings use RC allocation +- ⏳ Automatic decref at scope exit (TODO) +- ⏳ Closure RC (TODO) +- ⏳ Last-use optimization (TODO) + +See [docs/REFERENCE_COUNTING.md](REFERENCE_COUNTING.md) for details. + +**Example - reuse analysis (future):** ```lux fn increment(xs: List): List = List.map(xs, fn(x) => x + 1) diff --git a/docs/REFERENCE_COUNTING.md b/docs/REFERENCE_COUNTING.md new file mode 100644 index 0000000..a87fa71 --- /dev/null +++ b/docs/REFERENCE_COUNTING.md @@ -0,0 +1,224 @@ +# Reference Counting in Lux C Backend + +## Overview + +This document describes the reference counting (RC) system for automatic memory management in the Lux C backend. The approach is inspired by Perceus (used in Koka) but starts with a simpler implementation. + +## Current Status + +**Phase 1-2 Complete**: The RC infrastructure and allocation functions are implemented. All heap-allocated objects (strings, lists, boxed values) are now RC-managed. + +**What's Implemented:** +- RC header with refcount and type tag +- `lux_rc_alloc()` for allocating RC-managed objects +- `lux_incref()` / `lux_decref()` operations +- Polymorphic `lux_drop()` function +- Lists, boxed values, and dynamically-created strings use RC allocation +- List operations properly incref shared elements + +**What's NOT Yet Implemented:** +- Automatic decref insertion at scope exit +- Last-use analysis for ownership transfer +- Closure RC (environments still leak) +- ADT RC + +## Design + +### RC Header + +All heap-allocated objects share a common header: + +```c +typedef struct { + int32_t rc; // Reference count + int32_t tag; // Type tag for polymorphic drop +} LuxRcHeader; + +// Macro to get header from object pointer +#define LUX_RC_HEADER(ptr) (((LuxRcHeader*)(ptr)) - 1) +``` + +### Type Tags + +```c +typedef enum { + LUX_TAG_STRING = 1, + LUX_TAG_LIST = 2, + LUX_TAG_CLOSURE = 3, + LUX_TAG_BOXED_INT = 4, + LUX_TAG_BOXED_BOOL = 5, + LUX_TAG_BOXED_FLOAT = 6, + LUX_TAG_ENV = 7, // Closure environment + LUX_TAG_ADT = 100 // ADT types start at 100 +} LuxTypeTag; +``` + +### RC Operations + +```c +// Allocate RC-managed memory with initial refcount of 1 +static void* lux_rc_alloc(size_t size, int32_t tag) { + LuxRcHeader* hdr = (LuxRcHeader*)malloc(sizeof(LuxRcHeader) + size); + if (!hdr) return NULL; + hdr->rc = 1; + hdr->tag = tag; + return hdr + 1; // Return pointer after header +} + +// Increment reference count +static inline void lux_incref(void* ptr) { + if (ptr) LUX_RC_HEADER(ptr)->rc++; +} + +// Decrement reference count, call drop if zero +static inline void lux_decref(void* ptr) { + if (ptr) { + LuxRcHeader* hdr = LUX_RC_HEADER(ptr); + if (--hdr->rc == 0) { + lux_drop(ptr, hdr->tag); + } + } +} +``` + +### Drop Functions + +The polymorphic drop function handles cleanup for each type: + +```c +static void lux_drop(void* ptr, int32_t tag) { + if (!ptr) return; + switch (tag) { + case LUX_TAG_STRING: + // Strings are just char arrays, no sub-references + break; + case LUX_TAG_LIST: { + LuxList* list = (LuxList*)ptr; + // Decref each element (they're all boxed/RC-managed) + for (int64_t i = 0; i < list->length; i++) { + lux_decref(list->elements[i]); + } + free(list->elements); + break; + } + case LUX_TAG_CLOSURE: { + LuxClosure* closure = (LuxClosure*)ptr; + // Decref the environment if it's RC-managed + lux_decref(closure->env); + break; + } + case LUX_TAG_BOXED_INT: + case LUX_TAG_BOXED_BOOL: + case LUX_TAG_BOXED_FLOAT: + // Primitive boxes have no sub-references + break; + default: + // ADT types - handled by generated drop functions + break; + } + // Free the object and its RC header + free(LUX_RC_HEADER(ptr)); +} +``` + +## Code Generation Rules (Future Work) + +### Variable Bindings + +When a value is bound to a variable: +```c +// let x = expr +Type x = expr; // expr returns owned reference (rc=1) +``` + +### Variable Use + +When a variable is used (not the last use): +```c +// Using x in expression +lux_incref(x); +some_function(x); // Pass owned reference +``` + +### Last Use + +When a variable is used for the last time: +```c +// Last use of x - no incref needed +some_function(x); // Transfer ownership +``` + +### Scope Exit + +When a scope ends, decref all local variables: +```c +{ + Type x = ...; + Type y = ...; + // ... use x and y ... + lux_decref(y); + lux_decref(x); +} +``` + +## Implementation Phases + +### Phase 1: RC Infrastructure ✅ COMPLETE +- Add LuxRcHeader and allocation functions +- Add incref/decref/drop functions +- Type tags for built-in types + +### Phase 2: List RC ✅ COMPLETE +- Modify lux_list_new to use RC allocation +- Add drop function for lists +- List operations (concat, reverse, etc.) incref shared elements + +### Phase 3: Boxing RC ✅ COMPLETE +- All box functions use lux_rc_alloc +- String operations create RC-managed strings + +### Phase 4: Scope Tracking (TODO) +- Track variable lifetimes +- Insert decref at scope exit +- Handle early returns + +### Phase 5: Closure RC (TODO) +- Modify closure allocation to use RC +- Environment structs use RC +- Handle captured variables + +### Phase 6: Last-Use Analysis (Optimization) +- Track last use of variables +- Skip incref on last use (ownership transfer) +- Enable Perceus-style reuse + +## Memory Layout + +RC-managed objects have this memory layout: + +``` ++------------------+ +| LuxRcHeader | <- malloc returns this pointer +| int32_t rc | +| int32_t tag | ++------------------+ +| Object Data | <- lux_rc_alloc returns this pointer +| ... | ++------------------+ +``` + +## Comparison with Perceus + +| Feature | Perceus (Koka) | Lux RC (Current) | +|---------|----------------|------------------| +| RC header | Yes | Yes ✅ | +| RC insertion | Compile-time | Partial | +| Last-use opt | Yes | TODO | +| Reuse (FBIP) | Yes | Future | +| Drop fusion | Yes | No | +| Borrow inference | Yes | No | + +## References + +- [Perceus Paper](https://www.microsoft.com/en-us/research/publication/perceus-garbage-free-reference-counting-with-reuse/) +- [Koka Reference Counting](https://koka-lang.github.io/koka/doc/book.html) diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 0ae3807..2ebc4a8 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -375,12 +375,71 @@ impl CBackend { self.writeln("// Closure representation: env pointer + function pointer"); self.writeln("typedef struct { void* env; void* fn_ptr; } LuxClosure;"); self.writeln(""); + self.writeln("// === Reference Counting Infrastructure ==="); + self.writeln("// Perceus-inspired RC system for automatic memory management."); + self.writeln("// See docs/REFERENCE_COUNTING.md for details."); + self.writeln(""); + self.writeln("// Type tags for polymorphic drop - identifies the type of RC object"); + self.writeln("typedef enum {"); + self.writeln(" LUX_TAG_STRING = 1,"); + self.writeln(" LUX_TAG_LIST = 2,"); + self.writeln(" LUX_TAG_CLOSURE = 3,"); + self.writeln(" LUX_TAG_BOXED_INT = 4,"); + self.writeln(" LUX_TAG_BOXED_BOOL = 5,"); + self.writeln(" LUX_TAG_BOXED_FLOAT = 6,"); + self.writeln(" LUX_TAG_ENV = 7, // Closure environment"); + self.writeln(" LUX_TAG_ADT = 100 // ADT types start at 100"); + self.writeln("} LuxTypeTag;"); + self.writeln(""); + self.writeln("// RC header placed before every heap-allocated object"); + self.writeln("typedef struct {"); + self.writeln(" int32_t rc; // Reference count"); + self.writeln(" int32_t tag; // Type tag for polymorphic drop"); + self.writeln("} LuxRcHeader;"); + self.writeln(""); + self.writeln("// Get the RC header from an object pointer"); + self.writeln("#define LUX_RC_HEADER(ptr) (((LuxRcHeader*)(ptr)) - 1)"); + self.writeln(""); + self.writeln("// Forward declaration of polymorphic drop"); + self.writeln("static void lux_drop(void* ptr, int32_t tag);"); + self.writeln(""); + self.writeln("// Allocate RC-managed memory with initial refcount of 1"); + self.writeln("static void* lux_rc_alloc(size_t size, int32_t tag) {"); + self.writeln(" LuxRcHeader* hdr = (LuxRcHeader*)malloc(sizeof(LuxRcHeader) + size);"); + self.writeln(" if (!hdr) return NULL;"); + self.writeln(" hdr->rc = 1;"); + self.writeln(" hdr->tag = tag;"); + self.writeln(" return hdr + 1; // Return pointer after header"); + self.writeln("}"); + self.writeln(""); + self.writeln("// Increment reference count"); + self.writeln("static inline void lux_incref(void* ptr) {"); + self.writeln(" if (ptr) LUX_RC_HEADER(ptr)->rc++;"); + self.writeln("}"); + self.writeln(""); + self.writeln("// Decrement reference count, call drop if zero"); + self.writeln("static inline void lux_decref(void* ptr) {"); + self.writeln(" if (ptr) {"); + self.writeln(" LuxRcHeader* hdr = LUX_RC_HEADER(ptr);"); + self.writeln(" if (--hdr->rc == 0) {"); + self.writeln(" lux_drop(ptr, hdr->tag);"); + self.writeln(" }"); + self.writeln(" }"); + self.writeln("}"); + self.writeln(""); + self.writeln("// Get current reference count (for debugging)"); + self.writeln("static inline int32_t lux_refcount(void* ptr) {"); + self.writeln(" return ptr ? LUX_RC_HEADER(ptr)->rc : 0;"); + self.writeln("}"); + self.writeln(""); self.writeln("// === String Operations ==="); + self.writeln("// Dynamically created strings are RC-managed."); + self.writeln("// Static string literals from source code are NOT RC-managed."); self.writeln(""); self.writeln("static LuxString lux_string_concat(LuxString a, LuxString b) {"); self.writeln(" size_t len_a = strlen(a);"); self.writeln(" size_t len_b = strlen(b);"); - self.writeln(" LuxString result = malloc(len_a + len_b + 1);"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len_a + len_b + 1, LUX_TAG_STRING);"); self.writeln(" memcpy(result, a, len_a);"); self.writeln(" memcpy(result + len_a, b, len_b + 1);"); self.writeln(" return result;"); @@ -389,7 +448,10 @@ impl CBackend { self.writeln("static LuxString lux_int_to_string(LuxInt n) {"); self.writeln(" char buffer[32];"); self.writeln(" snprintf(buffer, sizeof(buffer), \"%lld\", (long long)n);"); - self.writeln(" return strdup(buffer);"); + self.writeln(" size_t len = strlen(buffer);"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" memcpy(result, buffer, len + 1);"); + self.writeln(" return result;"); self.writeln("}"); self.writeln(""); self.writeln("static LuxBool lux_string_eq(LuxString a, LuxString b) {"); @@ -411,9 +473,14 @@ impl CBackend { self.writeln(" if (fgets(buffer, sizeof(buffer), stdin)) {"); self.writeln(" size_t len = strlen(buffer);"); self.writeln(" if (len > 0 && buffer[len-1] == '\\n') buffer[len-1] = '\\0';"); - self.writeln(" return strdup(buffer);"); + self.writeln(" len = strlen(buffer);"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" memcpy(result, buffer, len + 1);"); + self.writeln(" return result;"); self.writeln(" }"); - self.writeln(" return strdup(\"\");"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(1, LUX_TAG_STRING);"); + self.writeln(" result[0] = '\\0';"); + self.writeln(" return result;"); self.writeln("}"); self.writeln(""); self.writeln("// === Evidence Passing Types ==="); @@ -589,11 +656,15 @@ impl CBackend { self.writeln(""); self.writeln("static LuxString lux_file_read(LuxString path) {"); self.writeln(" FILE* f = fopen(path, \"r\");"); - self.writeln(" if (!f) return strdup(\"\");"); + self.writeln(" if (!f) {"); + self.writeln(" LuxString empty = (LuxString)lux_rc_alloc(1, LUX_TAG_STRING);"); + self.writeln(" empty[0] = '\\0';"); + self.writeln(" return empty;"); + self.writeln(" }"); self.writeln(" fseek(f, 0, SEEK_END);"); self.writeln(" long size = ftell(f);"); self.writeln(" fseek(f, 0, SEEK_SET);"); - self.writeln(" char* content = malloc(size + 1);"); + self.writeln(" LuxString content = (LuxString)lux_rc_alloc(size + 1, LUX_TAG_STRING);"); self.writeln(" size_t read_size = fread(content, 1, size, f);"); self.writeln(" content[read_size] = '\\0';"); self.writeln(" fclose(f);"); @@ -712,16 +783,24 @@ impl CBackend { self.writeln(" return 0;"); self.writeln("}"); self.writeln(""); + self.writeln("// Helper to create RC-managed error string"); + self.writeln("static LuxString lux_rc_string(const char* s) {"); + self.writeln(" size_t len = strlen(s);"); + self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" memcpy(result, s, len + 1);"); + self.writeln(" return result;"); + self.writeln("}"); + self.writeln(""); self.writeln("static LuxString lux_http_request(const char* method, LuxString url, LuxString body) {"); self.writeln(" char host[256], path[1024];"); self.writeln(" int port;"); self.writeln(" lux_http_parse_url(url, host, &port, path);"); self.writeln(""); self.writeln(" struct hostent* server = gethostbyname(host);"); - self.writeln(" if (!server) return strdup(\"Error: Could not resolve host\");"); + self.writeln(" if (!server) return lux_rc_string(\"Error: Could not resolve host\");"); self.writeln(""); self.writeln(" int sockfd = socket(AF_INET, SOCK_STREAM, 0);"); - self.writeln(" if (sockfd < 0) return strdup(\"Error: Could not create socket\");"); + self.writeln(" if (sockfd < 0) return lux_rc_string(\"Error: Could not create socket\");"); self.writeln(""); self.writeln(" struct sockaddr_in serv_addr;"); self.writeln(" memset(&serv_addr, 0, sizeof(serv_addr));"); @@ -731,7 +810,7 @@ impl CBackend { self.writeln(""); self.writeln(" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {"); self.writeln(" close(sockfd);"); - self.writeln(" return strdup(\"Error: Connection failed\");"); + self.writeln(" return lux_rc_string(\"Error: Connection failed\");"); self.writeln(" }"); self.writeln(""); self.writeln(" char request[4096];"); @@ -753,7 +832,7 @@ impl CBackend { self.writeln(""); self.writeln(" send(sockfd, request, strlen(request), 0);"); self.writeln(""); - self.writeln(" char* response = malloc(65536);"); + self.writeln(" char* response = (char*)malloc(65536);"); self.writeln(" size_t total = 0;"); self.writeln(" ssize_t n;"); self.writeln(" while ((n = recv(sockfd, response + total, 65536 - total - 1, 0)) > 0) {"); @@ -767,11 +846,14 @@ impl CBackend { self.writeln(" char* body_start = strstr(response, \"\\r\\n\\r\\n\");"); self.writeln(" if (body_start) {"); self.writeln(" body_start += 4;"); - self.writeln(" char* result = strdup(body_start);"); + self.writeln(" LuxString result = lux_rc_string(body_start);"); self.writeln(" free(response);"); self.writeln(" return result;"); self.writeln(" }"); - self.writeln(" return response;"); + self.writeln(" // If no body separator, wrap the whole response"); + self.writeln(" LuxString result = lux_rc_string(response);"); + self.writeln(" free(response);"); + self.writeln(" return result;"); self.writeln("}"); self.writeln(""); self.writeln("static LuxString lux_http_get(LuxString url) {"); @@ -843,11 +925,13 @@ impl CBackend { self.writeln("typedef struct { Option_Tag tag; union { Option_Some_Data some; } data; } Option;"); self.writeln(""); self.writeln("// === List Operations ==="); + self.writeln("// All lists are RC-managed. Elements are also RC-managed."); self.writeln(""); self.writeln("static LuxList* lux_list_new(int64_t capacity) {"); - self.writeln(" LuxList* list = malloc(sizeof(LuxList));"); + self.writeln(" LuxList* list = (LuxList*)lux_rc_alloc(sizeof(LuxList), LUX_TAG_LIST);"); + self.writeln(" if (!list) return NULL;"); self.writeln(" list->capacity = capacity > 0 ? capacity : 4;"); - self.writeln(" list->elements = malloc(sizeof(void*) * list->capacity);"); + self.writeln(" list->elements = (void**)malloc(sizeof(void*) * list->capacity);"); self.writeln(" list->length = 0;"); self.writeln(" return list;"); self.writeln("}"); @@ -857,15 +941,24 @@ impl CBackend { self.writeln(""); self.writeln("static LuxList* lux_list_concat(LuxList* a, LuxList* b) {"); self.writeln(" LuxList* result = lux_list_new(a->length + b->length);"); - self.writeln(" for (int64_t i = 0; i < a->length; i++) result->elements[i] = a->elements[i];"); - self.writeln(" for (int64_t i = 0; i < b->length; i++) result->elements[a->length + i] = b->elements[i];"); + self.writeln(" for (int64_t i = 0; i < a->length; i++) {"); + self.writeln(" lux_incref(a->elements[i]);"); + self.writeln(" result->elements[i] = a->elements[i];"); + self.writeln(" }"); + self.writeln(" for (int64_t i = 0; i < b->length; i++) {"); + self.writeln(" lux_incref(b->elements[i]);"); + self.writeln(" result->elements[a->length + i] = b->elements[i];"); + self.writeln(" }"); self.writeln(" result->length = a->length + b->length;"); self.writeln(" return result;"); self.writeln("}"); self.writeln(""); self.writeln("static LuxList* lux_list_reverse(LuxList* list) {"); self.writeln(" LuxList* result = lux_list_new(list->length);"); - self.writeln(" for (int64_t i = 0; i < list->length; i++) result->elements[i] = list->elements[list->length - 1 - i];"); + self.writeln(" for (int64_t i = 0; i < list->length; i++) {"); + self.writeln(" lux_incref(list->elements[list->length - 1 - i]);"); + self.writeln(" result->elements[i] = list->elements[list->length - 1 - i];"); + self.writeln(" }"); self.writeln(" result->length = list->length;"); self.writeln(" return result;"); self.writeln("}"); @@ -874,17 +967,26 @@ impl CBackend { self.writeln(" if (n <= 0) return lux_list_new(0);"); self.writeln(" if (n > list->length) n = list->length;"); self.writeln(" LuxList* result = lux_list_new(n);"); - self.writeln(" for (int64_t i = 0; i < n; i++) result->elements[i] = list->elements[i];"); + self.writeln(" for (int64_t i = 0; i < n; i++) {"); + self.writeln(" lux_incref(list->elements[i]);"); + self.writeln(" result->elements[i] = list->elements[i];"); + self.writeln(" }"); self.writeln(" result->length = n;"); self.writeln(" return result;"); self.writeln("}"); self.writeln(""); self.writeln("static LuxList* lux_list_drop(LuxList* list, int64_t n) {"); self.writeln(" if (n >= list->length) return lux_list_new(0);"); - self.writeln(" if (n <= 0) return list;"); + self.writeln(" if (n <= 0) {"); + self.writeln(" lux_incref(list); // Return same list with bumped refcount"); + self.writeln(" return list;"); + self.writeln(" }"); self.writeln(" int64_t new_len = list->length - n;"); self.writeln(" LuxList* result = lux_list_new(new_len);"); - self.writeln(" for (int64_t i = 0; i < new_len; i++) result->elements[i] = list->elements[n + i];"); + self.writeln(" for (int64_t i = 0; i < new_len; i++) {"); + self.writeln(" lux_incref(list->elements[n + i]);"); + self.writeln(" result->elements[i] = list->elements[n + i];"); + self.writeln(" }"); self.writeln(" result->length = new_len;"); self.writeln(" return result;"); self.writeln("}"); @@ -894,7 +996,8 @@ impl CBackend { self.writeln(" int64_t len = end - start;"); self.writeln(" LuxList* result = lux_list_new(len);"); self.writeln(" for (int64_t i = 0; i < len; i++) {"); - self.writeln(" LuxInt* p = malloc(sizeof(LuxInt)); *p = start + i;"); + self.writeln(" LuxInt* p = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);"); + self.writeln(" *p = start + i;"); self.writeln(" result->elements[i] = p;"); self.writeln(" }"); self.writeln(" result->length = len;"); @@ -905,14 +1008,81 @@ impl CBackend { self.writeln("static Option lux_option_some(void* value) { return (Option){Option_TAG_SOME, .data.some = {value}}; }"); self.writeln(""); self.writeln("// === Boxing/Unboxing ==="); + self.writeln("// All boxed values are RC-managed."); self.writeln(""); - self.writeln("static void* lux_box_int(LuxInt n) { LuxInt* p = malloc(sizeof(LuxInt)); *p = n; return p; }"); + self.writeln("static void* lux_box_int(LuxInt n) {"); + self.writeln(" LuxInt* p = (LuxInt*)lux_rc_alloc(sizeof(LuxInt), LUX_TAG_BOXED_INT);"); + self.writeln(" *p = n;"); + self.writeln(" return p;"); + self.writeln("}"); self.writeln("static LuxInt lux_unbox_int(void* p) { return *(LuxInt*)p; }"); - self.writeln("static void* lux_box_bool(LuxBool b) { LuxBool* p = malloc(sizeof(LuxBool)); *p = b; return p; }"); + self.writeln(""); + self.writeln("static void* lux_box_bool(LuxBool b) {"); + self.writeln(" LuxBool* p = (LuxBool*)lux_rc_alloc(sizeof(LuxBool), LUX_TAG_BOXED_BOOL);"); + self.writeln(" *p = b;"); + self.writeln(" return p;"); + self.writeln("}"); self.writeln("static LuxBool lux_unbox_bool(void* p) { return *(LuxBool*)p; }"); - self.writeln("static void* lux_box_string(LuxString s) { return s; }"); + self.writeln(""); + self.writeln("static void* lux_box_float(LuxFloat f) {"); + self.writeln(" LuxFloat* p = (LuxFloat*)lux_rc_alloc(sizeof(LuxFloat), LUX_TAG_BOXED_FLOAT);"); + self.writeln(" *p = f;"); + self.writeln(" return p;"); + self.writeln("}"); + self.writeln("static LuxFloat lux_unbox_float(void* p) { return *(LuxFloat*)p; }"); + self.writeln(""); + self.writeln("static void* lux_box_string(LuxString s) {"); + self.writeln(" // Strings are already char*, so we allocate a new RC-managed string"); + self.writeln(" size_t len = strlen(s);"); + self.writeln(" LuxString p = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);"); + self.writeln(" memcpy(p, s, len + 1);"); + self.writeln(" return p;"); + self.writeln("}"); self.writeln("static LuxString lux_unbox_string(void* p) { return (LuxString)p; }"); self.writeln(""); + self.writeln("// === Polymorphic Drop Function ==="); + self.writeln("// Called when an object's refcount reaches zero."); + self.writeln("// Recursively decrefs any owned references before freeing."); + self.writeln(""); + self.writeln("static void lux_drop(void* ptr, int32_t tag) {"); + self.writeln(" if (!ptr) return;"); + self.writeln(" switch (tag) {"); + self.writeln(" case LUX_TAG_STRING:"); + self.writeln(" // Strings are just char arrays, no sub-references"); + self.writeln(" break;"); + self.writeln(" case LUX_TAG_LIST: {"); + self.writeln(" LuxList* list = (LuxList*)ptr;"); + self.writeln(" // Decref each element (they're all boxed/RC-managed)"); + self.writeln(" for (int64_t i = 0; i < list->length; i++) {"); + self.writeln(" lux_decref(list->elements[i]);"); + self.writeln(" }"); + self.writeln(" free(list->elements);"); + self.writeln(" break;"); + self.writeln(" }"); + self.writeln(" case LUX_TAG_CLOSURE: {"); + self.writeln(" LuxClosure* closure = (LuxClosure*)ptr;"); + self.writeln(" // Decref the environment if it's RC-managed"); + self.writeln(" lux_decref(closure->env);"); + self.writeln(" break;"); + self.writeln(" }"); + self.writeln(" case LUX_TAG_BOXED_INT:"); + self.writeln(" case LUX_TAG_BOXED_BOOL:"); + self.writeln(" case LUX_TAG_BOXED_FLOAT:"); + self.writeln(" // Primitive boxes have no sub-references"); + self.writeln(" break;"); + self.writeln(" case LUX_TAG_ENV:"); + self.writeln(" // Environment structs may contain RC pointers"); + self.writeln(" // For now, just free - proper drop needs type info"); + self.writeln(" break;"); + self.writeln(" default:"); + self.writeln(" // ADT types (tag >= 100) - handled by generated drop functions"); + self.writeln(" // For now, just free the object"); + self.writeln(" break;"); + self.writeln(" }"); + self.writeln(" // Free the object and its RC header"); + self.writeln(" free(LUX_RC_HEADER(ptr));"); + self.writeln("}"); + self.writeln(""); self.writeln("// === Forward Declarations ==="); self.writeln(""); }