diff options
| -rw-r--r-- | src/codegen/codegen.c | 50 | ||||
| -rw-r--r-- | std/fs.zc | 217 | ||||
| -rw-r--r-- | std/result.zc | 11 | ||||
| -rw-r--r-- | std/string.zc | 29 | ||||
| -rw-r--r-- | tests/std/test_fs.zc | 1 |
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); @@ -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 { |
