summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-31 15:31:41 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-31 15:31:41 +0000
commitccc53b11a0e273f46cb40e5f0eb32a74ab6750bf (patch)
tree8a4422c1d74dc4dcd951daf4555511b26b0a2e3b
parent051400c70a4d5384923113cfbcbc69e8e58d27a0 (diff)
Fix for #159
-rw-r--r--docs/std/slice.md15
-rw-r--r--src/codegen/codegen_main.c33
-rw-r--r--src/parser/parser_stmt.c114
-rw-r--r--src/parser/parser_struct.c115
-rw-r--r--std/mem.zc23
-rw-r--r--std/slice.zc5
-rw-r--r--tests/memory/test_memory_safety.zc9
7 files changed, 283 insertions, 31 deletions
diff --git a/docs/std/slice.md b/docs/std/slice.md
index b70c5fe..f029995 100644
--- a/docs/std/slice.md
+++ b/docs/std/slice.md
@@ -10,12 +10,12 @@ import "std/slice.zc"
fn main() {
let arr: int[5] = [1, 2, 3, 4, 5];
- // Direct iteration (Recommended)
+ // Direct iteration (auto-imports std/slice.zc)
for val in arr {
println "{val}";
}
- // Manual slice creation (for partial views or specific needs)
+ // Manual slice creation
let slice = Slice<int>::from_array((int*)(&arr), 5);
for val in slice {
println "{val}";
@@ -39,6 +39,7 @@ struct Slice<T> {
| Method | Signature | Description |
| :--- | :--- | :--- |
| **from_array** | `Slice<T>::from_array(arr: T*, len: usize) -> Slice<T>` | Creates a slice view over an array. |
+| **new** | `Slice<T>::new(data: T*, len: usize) -> Slice<T>` | Alias for `from_array` (backwards compat). |
### Iteration
@@ -62,10 +63,10 @@ struct Slice<T> {
### Iterating over fixed-size arrays
```zc
+// std/slice.zc is auto-imported when using for-in on arrays
let numbers: int[3] = [10, 20, 30];
-let slice = Slice<int>::from_array((int*)(&numbers), 3);
-for n in slice {
+for n in numbers {
println "Number: {n}";
}
```
@@ -73,6 +74,8 @@ for n in slice {
### Safe indexed access
```zc
+import "std/slice.zc"
+
let arr: int[3] = [1, 2, 3];
let slice = Slice<int>::from_array((int*)(&arr), 3);
@@ -84,7 +87,7 @@ if (!opt.is_none()) {
## Notes
-- `Slice<T>` does not own its data - it's just a view
+- `Slice<T>` does not own its data — it's just a view
- No memory management needed (no `free()` method)
-- Must specify the generic type explicitly: `Slice<int>`, `Slice<String>`, etc.
+- **Auto-import**: `std/slice.zc` is automatically imported when using `for val in arr` on a fixed-size array
- The array pointer cast `(T*)(&arr)` is required for fixed-size arrays
diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c
index b298700..82fc3ce 100644
--- a/src/codegen/codegen_main.c
+++ b/src/codegen/codegen_main.c
@@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
emit_type_aliases(kids, out); // Emit local aliases (redundant but safe)
emit_trait_defs(kids, out);
+ // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc)
+ // but only if they weren't already emitted from kids
+ StructRef *trait_ref = ctx->parsed_globals_list;
+ while (trait_ref)
+ {
+ if (trait_ref->node && trait_ref->node->type == NODE_TRAIT)
+ {
+ // Check if this trait was already in kids (explicitly imported)
+ int already_in_kids = 0;
+ ASTNode *k = kids;
+ while (k)
+ {
+ if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name &&
+ strcmp(k->trait.name, trait_ref->node->trait.name) == 0)
+ {
+ already_in_kids = 1;
+ break;
+ }
+ k = k->next;
+ }
+
+ if (!already_in_kids)
+ {
+ // Create a temporary single-node list for emit_trait_defs
+ ASTNode *saved_next = trait_ref->node->next;
+ trait_ref->node->next = NULL;
+ emit_trait_defs(trait_ref->node, out);
+ trait_ref->node->next = saved_next;
+ }
+ }
+ trait_ref = trait_ref->next;
+ }
+
// Track emitted raw statements to prevent duplicates
EmittedContent *emitted_raw = NULL;
diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c
index 7758ae3..0677cf5 100644
--- a/src/parser/parser_stmt.c
+++ b/src/parser/parser_stmt.c
@@ -14,6 +14,118 @@
char *curr_func_ret = NULL;
char *run_comptime_block(ParserContext *ctx, Lexer *l);
+extern char *g_current_filename;
+
+/**
+ * @brief Auto-imports std/slice.zc if not already imported.
+ *
+ * This is called when array iteration is detected in for-in loops,
+ * to ensure the Slice<T>, SliceIter<T>, and Option<T> templates are available.
+ */
+static void auto_import_std_slice(ParserContext *ctx)
+{
+ // Check if already imported via templates
+ GenericTemplate *t = ctx->templates;
+ while (t)
+ {
+ if (strcmp(t->name, "Slice") == 0)
+ {
+ return; // Already have the Slice template
+ }
+ t = t->next;
+ }
+
+ // Try to find and import std/slice.zc
+ static const char *std_paths[] = {"std/slice.zc", "./std/slice.zc", NULL};
+ static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL};
+
+ char resolved_path[1024];
+ int found = 0;
+
+ // First, try relative to current file
+ if (g_current_filename)
+ {
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash)
+ {
+ *last_slash = 0;
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", current_dir);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ free(current_dir);
+ }
+
+ // Try relative paths
+ if (!found)
+ {
+ for (int i = 0; std_paths[i] && !found; i++)
+ {
+ if (access(std_paths[i], R_OK) == 0)
+ {
+ strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ found = 1;
+ }
+ }
+ }
+
+ // Try system paths
+ if (!found)
+ {
+ for (int i = 0; system_paths[i] && !found; i++)
+ {
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/slice.zc", system_paths[i]);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return; // Could not find std/slice.zc, instantiate_generic will error
+ }
+
+ // Canonicalize path
+ char *real_fn = realpath(resolved_path, NULL);
+ if (real_fn)
+ {
+ strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ free(real_fn);
+ }
+
+ // Check if already imported
+ if (is_file_imported(ctx, resolved_path))
+ {
+ return;
+ }
+ mark_file_imported(ctx, resolved_path);
+
+ // Load and parse the file
+ char *src = load_file(resolved_path);
+ if (!src)
+ {
+ return; // Could not load file
+ }
+
+ Lexer i;
+ lexer_init(&i, src);
+
+ // Save and restore filename context
+ char *saved_fn = g_current_filename;
+ g_current_filename = resolved_path;
+
+ // Parse the slice module contents
+ parse_program_nodes(ctx, &i);
+
+ g_current_filename = saved_fn;
+}
static void check_assignment_condition(ASTNode *cond)
{
@@ -1193,6 +1305,8 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l)
// Manually trigger generic instantiation for Slice<T>
// This ensures that Slice_int, Slice_float, etc. structures are generated
+ // First, ensure std/slice.zc is imported (auto-import if needed)
+ auto_import_std_slice(ctx);
Token dummy_tok = {0};
instantiate_generic(ctx, "Slice", elem_type_str, elem_type_str, dummy_tok);
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index 109eeee..e53b56c 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -12,6 +12,114 @@
#include "zprep_plugin.h"
#include "../codegen/codegen.h"
+extern char *g_current_filename;
+
+/**
+ * @brief Auto-imports std/mem.zc if not already imported.
+ *
+ * This is called when the Drop trait is used (impl Drop for X).
+ */
+static void auto_import_std_mem(ParserContext *ctx)
+{
+ // Check if Drop trait is already registered (means mem.zc was imported)
+ if (check_impl(ctx, "Drop", "__trait_marker__"))
+ {
+ // Check_impl returns 0 if not found, but we need a different check
+ // Let's check if we can find any indicator that mem.zc was loaded
+ }
+
+ // Try to find and import std/mem.zc
+ static const char *std_paths[] = {"std/mem.zc", "./std/mem.zc", NULL};
+ static const char *system_paths[] = {"/usr/local/share/zenc", "/usr/share/zenc", NULL};
+
+ char resolved_path[1024];
+ int found = 0;
+
+ // First, try relative to current file
+ if (g_current_filename)
+ {
+ char *current_dir = xstrdup(g_current_filename);
+ char *last_slash = strrchr(current_dir, '/');
+ if (last_slash)
+ {
+ *last_slash = 0;
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", current_dir);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ free(current_dir);
+ }
+
+ // Try relative paths
+ if (!found)
+ {
+ for (int i = 0; std_paths[i] && !found; i++)
+ {
+ if (access(std_paths[i], R_OK) == 0)
+ {
+ strncpy(resolved_path, std_paths[i], sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ found = 1;
+ }
+ }
+ }
+
+ // Try system paths
+ if (!found)
+ {
+ for (int i = 0; system_paths[i] && !found; i++)
+ {
+ snprintf(resolved_path, sizeof(resolved_path), "%s/std/mem.zc", system_paths[i]);
+ if (access(resolved_path, R_OK) == 0)
+ {
+ found = 1;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ return; // Could not find std/mem.zc
+ }
+
+ // Canonicalize path
+ char *real_fn = realpath(resolved_path, NULL);
+ if (real_fn)
+ {
+ strncpy(resolved_path, real_fn, sizeof(resolved_path) - 1);
+ resolved_path[sizeof(resolved_path) - 1] = '\0';
+ free(real_fn);
+ }
+
+ // Check if already imported
+ if (is_file_imported(ctx, resolved_path))
+ {
+ return;
+ }
+ mark_file_imported(ctx, resolved_path);
+
+ // Load and parse the file
+ char *src = load_file(resolved_path);
+ if (!src)
+ {
+ return; // Could not load file
+ }
+
+ Lexer i;
+ lexer_init(&i, src);
+
+ // Save and restore filename context
+ char *saved_fn = g_current_filename;
+ g_current_filename = resolved_path;
+
+ // Parse the mem module contents
+ parse_program_nodes(ctx, &i);
+
+ g_current_filename = saved_fn;
+}
+
// Trait Parsing
ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
{
@@ -149,6 +257,7 @@ ASTNode *parse_trait(ParserContext *ctx, Lexer *l)
}
register_trait(name);
+ add_to_global_list(ctx, n_node); // Track for codegen (VTable emission)
return n_node;
}
@@ -206,6 +315,12 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
register_generic(ctx, target_gen_param);
}
+ // Auto-import std/mem.zc if implementing Drop, Copy, or Clone traits
+ if (strcmp(name1, "Drop") == 0 || strcmp(name1, "Copy") == 0 || strcmp(name1, "Clone") == 0)
+ {
+ auto_import_std_mem(ctx);
+ }
+
register_impl(ctx, name1, name2);
// RAII: Check for "Drop" trait implementation
diff --git a/std/mem.zc b/std/mem.zc
index 6ee96e8..f1a5f5a 100644
--- a/std/mem.zc
+++ b/std/mem.zc
@@ -49,28 +49,7 @@ impl Box<T> {
}
}
-struct Slice<T> {
- data: T*;
- len: usize;
-}
-
-impl Slice<T> {
- fn new(data: T*, len: usize) -> Self {
- return Self { data: data, len: len };
- }
-
- fn get(self, i: usize) -> T {
- return self.data[i];
- }
-
- fn set(self, i: usize, val: T) {
- self.data[i] = val;
- }
-
- fn is_empty(self) -> bool {
- return self.len == 0;
- }
-}
+// Note: Slice<T> is defined in std/slice.zc with iteration support
fn mem_zero<T>(ptr: T*, count: usize) {
memset(ptr, 0, sizeof(T) * count);
diff --git a/std/slice.zc b/std/slice.zc
index 7ace396..3c317ca 100644
--- a/std/slice.zc
+++ b/std/slice.zc
@@ -32,6 +32,11 @@ impl Slice<T> {
return Slice<T> { data: arr, len: len };
}
+ // Alias for backwards compatibility with std/mem.zc
+ fn new(data: T*, len: usize) -> Slice<T> {
+ return Slice<T> { data: data, len: len };
+ }
+
fn iterator(self) -> SliceIter<T> {
return SliceIter<T> {
data: self.data,
diff --git a/tests/memory/test_memory_safety.zc b/tests/memory/test_memory_safety.zc
index a5cc960..b672cc9 100644
--- a/tests/memory/test_memory_safety.zc
+++ b/tests/memory/test_memory_safety.zc
@@ -1,5 +1,6 @@
import "std/mem.zc"
+import "std/slice.zc"
// ** Globals **
let DROP_COUNT = 0;
@@ -127,11 +128,13 @@ test "test_slice" {
let data: int[5] = [1, 2, 3, 4, 5];
let s = Slice<int>::new(&data[0], 5);
f" Slice len: {(int)s.len}";
- let v2 = s.get(2);
+ let opt_v2 = s.get(2);
+ let v2 = opt_v2.unwrap();
f" Slice[2]: {v2}";
assert(v2 == 3, "Slice get failed");
- s.set(0, 99);
- let v0 = s.get(0);
+ s.data[0] = 99;
+ let opt_v0 = s.get(0);
+ let v0 = opt_v0.unwrap();
f" After set: Slice[0] = {v0}";
assert(v0 == 99, "Slice set failed");
" ✓ Slice works!";