feat: add Perceus-inspired reference counting infrastructure
Implements Phase 1-3 of the RC system for automatic memory management: - Add LuxRcHeader with refcount and type tag for all heap objects - Add lux_rc_alloc, lux_incref, lux_decref, and lux_drop functions - Update list allocation to use RC (lux_list_new uses lux_rc_alloc) - List operations (concat, reverse, take, drop) now incref shared elements - Update boxing functions (box_int, box_bool, box_float) to use RC - String operations (concat, int_to_string, readLine) return RC strings - File and HTTP operations return RC-managed strings The infrastructure is ready for automatic decref insertion at scope exit (Phase 4) and closure RC (Phase 5) in future work. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<Int>): List<Int> =
|
||||
List.map(xs, fn(x) => x + 1)
|
||||
|
||||
224
docs/REFERENCE_COUNTING.md
Normal file
224
docs/REFERENCE_COUNTING.md
Normal file
@@ -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)
|
||||
@@ -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("");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user