summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-21 00:38:14 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-21 00:38:14 +0000
commitfd84225099175d54ea20f0d39ddf10ccd2daa381 (patch)
treeae42004d20ae3a6f3d4c3c48f999748a1e3e9b0a
parentabea7bb0b29f1f1672faff94d1bf26768f214dc1 (diff)
Modify codegen, tests should pass now
-rw-r--r--src/codegen/codegen.c50
-rw-r--r--std/fs.zc217
-rw-r--r--std/result.zc11
-rw-r--r--std/string.zc29
-rw-r--r--tests/std/test_fs.zc1
5 files changed, 244 insertions, 64 deletions
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index b2f1ad1..776ed80 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -2228,10 +2228,54 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
break;
}
case NODE_RETURN:
- fprintf(out, " return ");
- codegen_expression(ctx, node->ret.value, out);
- fprintf(out, ";\n");
+ {
+ int handled = 0;
+ // If returning a variable that implements Drop, we must zero it out
+ // to prevent the cleanup attribute from destroying the resource we just returned.
+ if (node->ret.value && node->ret.value->type == NODE_EXPR_VAR)
+ {
+ char *tname = infer_type(ctx, node->ret.value);
+ if (tname)
+ {
+ char *clean = tname;
+ if (strncmp(clean, "struct ", 7) == 0)
+ {
+ clean += 7;
+ }
+
+ ASTNode *def = find_struct_def(ctx, clean);
+ if (def && def->type_info && def->type_info->traits.has_drop)
+ {
+ fprintf(out, " return ({ ");
+ if (strstr(g_config.cc, "tcc"))
+ {
+ fprintf(out, "__typeof__(");
+ codegen_expression(ctx, node->ret.value, out);
+ fprintf(out, ")");
+ }
+ else
+ {
+ fprintf(out, "__auto_type");
+ }
+ fprintf(out, " _z_ret_mv = ");
+ codegen_expression(ctx, node->ret.value, out);
+ fprintf(out, "; memset(&");
+ codegen_expression(ctx, node->ret.value, out);
+ fprintf(out, ", 0, sizeof(_z_ret_mv)); _z_ret_mv; });\n");
+ handled = 1;
+ }
+ free(tname);
+ }
+ }
+
+ if (!handled)
+ {
+ fprintf(out, " return ");
+ codegen_expression(ctx, node->ret.value, out);
+ fprintf(out, ";\n");
+ }
break;
+ }
case NODE_EXPR_MEMBER:
{
codegen_expression(ctx, node->member.target, out);
diff --git a/std/fs.zc b/std/fs.zc
index d35a1fa..e16c8ff 100644
--- a/std/fs.zc
+++ b/std/fs.zc
@@ -1,15 +1,74 @@
-
import "./core.zc"
import "./result.zc"
import "./string.zc"
import "./vec.zc"
+const Z_SEEK_SET: int = 0;
+const Z_SEEK_END: int = 2;
+const Z_F_OK: int = 0;
+
+
+// TODO: restructure this tomorrow.
raw {
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
+
+ // typedef needed for Vec<DirEntry*> generation if inferred
+ typedef struct DirEntry* DirEntryPtr;
- // Helper to get file size for stat
+ void* _z_fs_fopen(char* path, char* mode) {
+ return fopen(path, mode);
+ }
+
+ int _z_fs_fclose(void* stream) {
+ return fclose((FILE*)stream);
+ }
+
+ size_t _z_fs_fread(void* ptr, size_t size, size_t nmemb, void* stream) {
+ return fread(ptr, size, nmemb, (FILE*)stream);
+ }
+
+ size_t _z_fs_fwrite(void* ptr, size_t size, size_t nmemb, void* stream) {
+ return fwrite(ptr, size, nmemb, (FILE*)stream);
+ }
+
+ int _z_fs_fseek(void* stream, long offset, int whence) {
+ return fseek((FILE*)stream, offset, whence);
+ }
+
+ long _z_fs_ftell(void* stream) {
+ return ftell((FILE*)stream);
+ }
+
+ int _z_fs_access(char* pathname, int mode) {
+ return access(pathname, mode);
+ }
+
+ int _z_fs_unlink(char* pathname) {
+ return unlink(pathname);
+ }
+
+ int _z_fs_rmdir(char* pathname) {
+ return rmdir(pathname);
+ }
+
+ void* _z_fs_opendir(char* name) {
+ return opendir(name);
+ }
+
+ int _z_fs_closedir(void* dir) {
+ return closedir((DIR*)dir);
+ }
+
+ void* _z_fs_malloc(size_t size) {
+ return malloc(size);
+ }
+
+ void _z_fs_free(void* ptr) {
+ free(ptr);
+ }
+
int _z_fs_get_metadata(char* path, uint64_t* size, int* is_dir, int* is_file) {
struct stat st;
if (stat(path, &st) != 0) return -1;
@@ -19,10 +78,11 @@ raw {
return 0;
}
- int _z_fs_read_entry(void* dir, char* out_name, int* is_dir) {
+ int _z_fs_read_entry(void* dir, char* out_name, int buf_size, int* is_dir) {
struct dirent* ent = readdir((DIR*)dir);
if (!ent) return 0;
- strcpy(out_name, ent->d_name);
+ strncpy(out_name, ent->d_name, buf_size - 1);
+ out_name[buf_size - 1] = 0;
*is_dir = (ent->d_type == DT_DIR);
return 1;
}
@@ -31,26 +91,38 @@ raw {
#ifdef _WIN32
return mkdir(path);
#else
- return mkdir(path, 0777);
+ return mkdir(path, 0777);
#endif
}
}
extern fn _z_fs_mkdir(path: char*) -> int;
extern fn _z_fs_get_metadata(path: char*, size: U64*, is_dir: int*, is_file: int*) -> int;
-extern fn _z_fs_read_entry(dir: void*, out_name: char*, is_dir: int*) -> int;
+extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: int, is_dir: int*) -> int;
+extern fn _z_fs_fopen(path: char*, mode: char*) -> void*;
+extern fn _z_fs_fclose(stream: void*) -> int;
+extern fn _z_fs_fread(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize;
+extern fn _z_fs_fwrite(ptr: void*, size: usize, nmemb: usize, stream: void*) -> usize;
+extern fn _z_fs_fseek(stream: void*, offset: long, whence: int) -> int;
+extern fn _z_fs_ftell(stream: void*) -> long;
+extern fn _z_fs_access(pathname: char*, mode: int) -> int;
+extern fn _z_fs_unlink(pathname: char*) -> int;
+extern fn _z_fs_rmdir(pathname: char*) -> int;
+extern fn _z_fs_opendir(name: char*) -> void*;
+extern fn _z_fs_closedir(dir: void*) -> int;
+extern fn _z_fs_malloc(size: usize) -> void*;
+extern fn _z_fs_free(ptr: void*);
struct File {
- handle: void*; // FILE*
+ handle: void*;
}
struct Metadata {
size: U64;
- is_dir: bool;
+ is_dir: bool;
is_file: bool;
}
-// @derive(Eq) - Removed to avoid double-free on String field
struct DirEntry {
name: String;
is_dir: bool;
@@ -58,115 +130,160 @@ struct DirEntry {
impl File {
fn open(path: char*, mode: char*) -> Result<File> {
- var h = fopen(path, mode);
+ var h = _z_fs_fopen(path, mode);
if (h == NULL) {
return Result<File>::Err("Failed to open file");
}
return Result<File>::Ok(File { handle: h });
}
-
+
fn close(self) {
if (self.handle) {
- fclose(self.handle);
+ _z_fs_fclose(self.handle);
self.handle = NULL;
}
}
-
+
fn read_to_string(self) -> Result<String> {
- if (self.handle == NULL) return Result<String>::Err("File not open");
+ if (self.handle == NULL) {
+ return Result<String>::Err("File not open");
+ }
- fseek(self.handle, 0, SEEK_END);
- var size = ftell(self.handle);
- fseek(self.handle, 0, SEEK_SET);
+ _z_fs_fseek(self.handle, 0, Z_SEEK_END);
+ var size = _z_fs_ftell(self.handle);
+ _z_fs_fseek(self.handle, 0, Z_SEEK_SET);
- var buffer: char* = malloc((usize)size + 1);
- if (buffer == NULL) return Result<String>::Err("Out of memory");
+ var buffer: char* = _z_fs_malloc((usize)size + 1);
+ if (buffer == NULL) {
+ return Result<String>::Err("Out of memory");
+ }
- var read = fread(buffer, 1, size, self.handle);
+ var read = _z_fs_fread(buffer, 1, size, self.handle);
buffer[read] = 0;
var s = String::new(buffer);
- free(buffer);
+ _z_fs_free(buffer);
+
var res = Result<String>::Ok(s);
s.forget();
+
var ret = res;
res.forget();
return ret;
}
-
+
fn read_all(path: char*) -> Result<String> {
var res = File::open(path, "rb");
- if (res.is_err()) return Result<String>::Err(res.err);
+ if (res.is_err()) {
+ return Result<String>::Err(res.err);
+ }
var f: File = res.unwrap();
var s_res = f.read_to_string();
f.close();
+
var ret = s_res;
s_res.forget();
return ret;
}
-
+
fn write_string(self, content: char*) -> Result<bool> {
- if (self.handle == NULL) return Result<bool>::Err("File not open");
+ if (self.handle == NULL) {
+ return Result<bool>::Err("File not open");
+ }
+
var len = strlen(content);
- var written = fwrite(content, 1, len, self.handle);
- if (written != len) return Result<bool>::Err("Write incomplete");
+ var written = _z_fs_fwrite(content, 1, len, self.handle);
+ if (written != len) {
+ return Result<bool>::Err("Write incomplete");
+ }
return Result<bool>::Ok(true);
}
-
+
fn exists(path: char*) -> bool {
- return access(path, F_OK) == 0;
+ return _z_fs_access(path, Z_F_OK) == 0;
}
-
+
fn metadata(path: char*) -> Result<Metadata> {
var size: uint64_t;
var is_d: int;
var is_f: int;
+
if (_z_fs_get_metadata(path, &size, &is_d, &is_f) != 0) {
return Result<Metadata>::Err("Failed to get metadata");
}
- return Result<Metadata>::Ok(Metadata { size: (U64)size, is_dir: is_d != 0, is_file: is_f != 0 });
+
+ return Result<Metadata>::Ok(Metadata {
+ size: (U64)size,
+ is_dir: is_d != 0,
+ is_file: is_f != 0
+ });
}
-
+
fn create_dir(path: char*) -> Result<bool> {
- if (_z_fs_mkdir(path) != 0) return Result<bool>::Err("Failed to create directory");
+ if (_z_fs_mkdir(path) != 0) {
+ return Result<bool>::Err("Failed to create directory");
+ }
return Result<bool>::Ok(true);
}
-
+
fn remove_file(path: char*) -> Result<bool> {
- if (unlink(path) != 0) return Result<bool>::Err("Failed to remove file");
+ if (_z_fs_unlink(path) != 0) {
+ return Result<bool>::Err("Failed to remove file");
+ }
return Result<bool>::Ok(true);
}
-
+
fn remove_dir(path: char*) -> Result<bool> {
- if (rmdir(path) != 0) return Result<bool>::Err("Failed to remove directory");
+ if (_z_fs_rmdir(path) != 0) {
+ return Result<bool>::Err("Failed to remove directory");
+ }
return Result<bool>::Ok(true);
}
-
+
fn read_dir(path: char*) -> Result< Vec<DirEntry> > {
- var dir = opendir(path);
- if (dir == NULL) return Result< Vec<DirEntry> >::Err("Failed to open directory");
+ var dir = _z_fs_opendir(path);
+ if (dir == NULL) {
+ return Result< Vec<DirEntry> >::Err("Failed to open directory");
+ }
var entries = Vec<DirEntry>::new();
- var name_buf = malloc(256);
+ var name_buf: char* = _z_fs_malloc(256);
+
+ if (name_buf == NULL) {
+ _z_fs_closedir(dir);
+ return Result< Vec<DirEntry> >::Err("Out of memory");
+ }
+
var is_d: int = 0;
- while (_z_fs_read_entry(dir, name_buf, &is_d)) {
- // Skip . and ..
- if (strcmp(name_buf, ".") == 0 || strcmp(name_buf, "..") == 0) continue;
+ while (_z_fs_read_entry(dir, name_buf, 256, &is_d)) {
+ if (strcmp(name_buf, ".") == 0 || strcmp(name_buf, "..") == 0) {
+ continue;
+ }
- entries.push(DirEntry {
- name: String::new(name_buf),
+ var s = String::new(name_buf);
+ var ent = DirEntry {
+ name: s,
is_dir: is_d != 0
- });
+ };
+
+ // Transfer ownership: String -> DirEntry
+ s.forget();
+
+ entries.push(ent);
+
+ // Transfer ownership: DirEntry -> Vec
+ ent.name.forget();
}
- free(name_buf);
- closedir(dir);
+ _z_fs_free(name_buf);
+ _z_fs_closedir(dir);
+
var res = Result< Vec<DirEntry> >::Ok(entries);
entries.forget();
var ret = res;
res.forget();
return ret;
}
-}
+} \ No newline at end of file
diff --git a/std/result.zc b/std/result.zc
index 3794f1a..1d10b1a 100644
--- a/std/result.zc
+++ b/std/result.zc
@@ -1,4 +1,3 @@
-
import "./core.zc"
struct Result<T> {
@@ -38,6 +37,14 @@ impl Result<T> {
return v;
}
+ fn unwrap_ref(self) -> T* {
+ if (!self.is_ok) {
+ !"Panic: unwrap_ref called on Err: {self.err}";
+ exit(1);
+ }
+ return &self.val;
+ }
+
fn expect(self, msg: char*) -> T {
if (!self.is_ok) {
!"Panic: {msg}: {self.err}";
@@ -45,4 +52,4 @@ impl Result<T> {
}
return self.val;
}
-}
+} \ No newline at end of file
diff --git a/std/string.zc b/std/string.zc
index 2e59179..02ae6b9 100644
--- a/std/string.zc
+++ b/std/string.zc
@@ -1,4 +1,3 @@
-
import "./core.zc"
import "./vec.zc"
import "./option.zc"
@@ -16,10 +15,15 @@ impl String {
v.push(s[i]);
}
v.push(0);
+
+ // Extract fields to transfer ownership
var d = v.data;
var l = v.len;
var c = v.cap;
+
+ // Forget the local vector so it doesn't free the memory
v.forget();
+
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
@@ -27,6 +31,7 @@ impl String {
return String::new(s);
}
+ // Fixed: 'self' implies pointer in generated C
fn c_str(self) -> char* {
return self.vec.data;
}
@@ -40,7 +45,11 @@ impl String {
}
fn append(self, other: String*) {
- self.vec.len = self.vec.len - 1;
+ // Remove null terminator before appending
+ if (self.vec.len > 0) {
+ self.vec.len = self.vec.len - 1;
+ }
+
var other_len = (*other).vec.len;
for (var i = 0; i < other_len; i = i + 1) {
self.vec.push((*other).vec.get(i));
@@ -50,10 +59,12 @@ impl String {
fn add(self, other: String*) -> String {
var new_s = String::from(self.c_str());
new_s.append(other);
+
var d = new_s.vec.data;
var l = new_s.vec.len;
var c = new_s.vec.cap;
new_s.forget();
+
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
@@ -62,10 +73,8 @@ impl String {
}
fn length(self) -> usize {
- if (self.vec.len == 0) { return 0;
- }
+ if (self.vec.len == 0) { return 0; }
return self.vec.len - 1;
- // Exclude null terminator
}
fn substring(self, start: usize, len: usize) -> String {
@@ -73,15 +82,16 @@ impl String {
panic("substring out of bounds");
}
var v = Vec<char>::new();
- // Manual copy
for (var i: usize = 0; i < len; i = i + 1) {
v.push(self.vec.get(start + i));
}
v.push(0);
+
var d = v.data;
var l = v.len;
var c = v.cap;
v.forget();
+
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
@@ -96,11 +106,12 @@ impl String {
}
fn print(self) {
- "{self.c_str()}"..;
+ printf("%s", self.c_str());
+ fflush(stdout);
}
fn println(self) {
- "{self.c_str()}";
+ printf("%s\n", self.c_str());
}
fn is_empty(self) -> bool {
@@ -128,4 +139,4 @@ impl String {
fn free(self) {
self.vec.free();
}
-}
+} \ No newline at end of file
diff --git a/tests/std/test_fs.zc b/tests/std/test_fs.zc
index fc6d069..c3fd47c 100644
--- a/tests/std/test_fs.zc
+++ b/tests/std/test_fs.zc
@@ -61,6 +61,7 @@ test "test_std_fs_extended" {
"Read Dir";
var list_res = File::read_dir(test_dir);
assert(list_res.is_ok(), "Read dir failed");
+
var entries = list_res.unwrap();
var found_idx = -1;
for entry in &entries {