| author | |
| committer | |
| log | 0ae9023832584e256aa3b6df0f0829026141d21a |
| tree | 78b989a47e93169227a1d08d7b1da1499a04ee87 |
| parent | 0a482bbbfe1dbabaab6b7a5c26ec4f29f0c618d9 |
and fix ability to take address of variables
from other namespaces8 files changed, 206 insertions(+), 72 deletions(-)
src/analyze.cpp+1| ... | ... | @@ -2688,6 +2688,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 2688 | 2688 | return node->data.field_access_expr.type_struct_field->type_entry; |
| 2689 | 2689 | } else if (wrapped_in_fn_call) { |
| 2690 | 2690 | BlockContext *container_block_context = get_container_block_context(bare_struct_type); |
| 2691 | assert(container_block_context); | |
| 2691 | 2692 | auto entry = container_block_context->decl_table.maybe_get(field_name); |
| 2692 | 2693 | AstNode *fn_decl_node = entry ? entry->value : nullptr; |
| 2693 | 2694 | if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { |
src/codegen.cpp+13-3| ... | ... | @@ -1243,6 +1243,11 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou |
| 1243 | 1243 | |
| 1244 | 1244 | AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; |
| 1245 | 1245 | |
| 1246 | *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry; | |
| 1247 | if (!type_has_bits(*out_type_entry)) { | |
| 1248 | return nullptr; | |
| 1249 | } | |
| 1250 | ||
| 1246 | 1251 | LLVMValueRef struct_ptr; |
| 1247 | 1252 | if (struct_expr_node->type == NodeTypeSymbol) { |
| 1248 | 1253 | VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable; |
| ... | ... | @@ -1272,8 +1277,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou |
| 1272 | 1277 | int gen_field_index = node->data.field_access_expr.type_struct_field->gen_index; |
| 1273 | 1278 | assert(gen_field_index >= 0); |
| 1274 | 1279 | |
| 1275 | *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry; | |
| 1276 | ||
| 1277 | 1280 | set_debug_source_node(g, node); |
| 1278 | 1281 | return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, ""); |
| 1279 | 1282 | } |
| ... | ... | @@ -1490,7 +1493,14 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, |
| 1490 | 1493 | zig_unreachable(); |
| 1491 | 1494 | } |
| 1492 | 1495 | } else if (node->type == NodeTypeFieldAccessExpr) { |
| 1493 | target_ref = gen_field_ptr(g, node, out_type_entry); | |
| 1496 | AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; | |
| 1497 | TypeTableEntry *struct_type = get_expr_type(struct_expr_node); | |
| 1498 | if (struct_type->id == TypeTableEntryIdNamespace) { | |
| 1499 | target_ref = gen_field_access_expr(g, node, true); | |
| 1500 | *out_type_entry = get_expr_type(node); | |
| 1501 | } else { | |
| 1502 | target_ref = gen_field_ptr(g, node, out_type_entry); | |
| 1503 | } | |
| 1494 | 1504 | } else if (node->type == NodeTypePrefixOpExpr) { |
| 1495 | 1505 | assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference); |
| 1496 | 1506 | AstNode *target_expr = node->data.prefix_op_expr.primary_expr; |
std/cstr.zig+121-2| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | const List = @import("list.zig").List; | |
| 2 | const mem = @import("mem.zig"); | |
| 3 | const Allocator = mem.Allocator; | |
| 4 | const debug = @import("debug.zig"); | |
| 5 | const assert = debug.assert; | |
| 6 | ||
| 7 | const strlen = len; | |
| 8 | ||
| 1 | 9 | // TODO fix https://github.com/andrewrk/zig/issues/140 |
| 2 | 10 | // and then make this able to run at compile time |
| 3 | 11 | #static_eval_enable(false) |
| ... | ... | @@ -17,10 +25,121 @@ pub fn cmp(a: &const u8, b: &const u8) -> i32 { |
| 17 | 25 | } |
| 18 | 26 | |
| 19 | 27 | pub fn to_slice_const(str: &const u8) -> []const u8 { |
| 20 | return str[0...len(str)]; | |
| 28 | return str[0...strlen(str)]; | |
| 21 | 29 | } |
| 22 | 30 | |
| 23 | 31 | pub fn to_slice(str: &u8) -> []u8 { |
| 24 | return str[0...len(str)]; | |
| 32 | return str[0...strlen(str)]; | |
| 33 | } | |
| 34 | ||
| 35 | ||
| 36 | /// A buffer that allocates memory and maintains a null byte at the end. | |
| 37 | pub struct CBuf { | |
| 38 | list: List(u8), | |
| 39 | ||
| 40 | /// Must deinitialize with deinit. | |
| 41 | pub fn init(self: &CBuf, allocator: &Allocator) { | |
| 42 | self.list.init(allocator); | |
| 43 | // This resize is guaranteed to not have an error because we use a list | |
| 44 | // with preallocated memory of at least 1 byte. | |
| 45 | %%self.resize(0); | |
| 46 | } | |
| 47 | ||
| 48 | /// Must deinitialize with deinit. | |
| 49 | pub fn init_from_mem(self: &CBuf, allocator: &Allocator, m: []const u8) -> %void { | |
| 50 | self.init(allocator); | |
| 51 | %return self.resize(m.len); | |
| 52 | mem.copy(u8, self.list.items, m); | |
| 53 | } | |
| 54 | ||
| 55 | /// Must deinitialize with deinit. | |
| 56 | pub fn init_from_cstr(self: &CBuf, allocator: &Allocator, s: &const u8) -> %void { | |
| 57 | self.init_from_mem(allocator, s[0...strlen(s)]) | |
| 58 | } | |
| 59 | ||
| 60 | /// Must deinitialize with deinit. | |
| 61 | pub fn init_from_cbuf(self: &CBuf, cbuf: &const CBuf) -> %void { | |
| 62 | self.init_from_mem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()]) | |
| 63 | } | |
| 64 | ||
| 65 | /// Must deinitialize with deinit. | |
| 66 | pub fn init_from_slice(self: &CBuf, other: &const CBuf, start: usize, end: usize) -> %void { | |
| 67 | self.init_from_mem(other.list.allocator, other.list.items[start...end]) | |
| 68 | } | |
| 69 | ||
| 70 | pub fn deinit(self: &CBuf) { | |
| 71 | self.list.deinit(); | |
| 72 | } | |
| 73 | ||
| 74 | pub fn resize(self: &CBuf, new_len: usize) -> %void { | |
| 75 | %return self.list.resize(new_len + 1); | |
| 76 | self.list.items[self.len()] = 0; | |
| 77 | } | |
| 78 | ||
| 79 | pub fn len(self: &const CBuf) -> usize { | |
| 80 | return self.list.len - 1; | |
| 81 | } | |
| 82 | ||
| 83 | pub fn append_mem(self: &CBuf, m: []const u8) -> %void { | |
| 84 | const old_len = self.len(); | |
| 85 | %return self.resize(old_len + m.len); | |
| 86 | mem.copy(u8, self.list.items[old_len...], m); | |
| 87 | } | |
| 88 | ||
| 89 | pub fn append_cstr(self: &CBuf, s: &const u8) -> %void { | |
| 90 | self.append_mem(s[0...strlen(s)]) | |
| 91 | } | |
| 92 | ||
| 93 | pub fn append_char(self: &CBuf, c: u8) -> %void { | |
| 94 | %return self.resize(self.len() + 1); | |
| 95 | self.list.items[self.len() - 1] = c; | |
| 96 | } | |
| 97 | ||
| 98 | pub fn eql_mem(self: &const CBuf, m: []const u8) -> bool { | |
| 99 | if (self.len() != m.len) return false; | |
| 100 | return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal; | |
| 101 | } | |
| 102 | ||
| 103 | pub fn eql_cstr(self: &const CBuf, s: &const u8) -> bool { | |
| 104 | self.eql_mem(s[0...strlen(s)]) | |
| 105 | } | |
| 106 | ||
| 107 | pub fn eql_cbuf(self: &const CBuf, other: &const CBuf) -> bool { | |
| 108 | self.eql_mem(other.list.items[0...other.len()]) | |
| 109 | } | |
| 110 | ||
| 111 | pub fn starts_with_mem(self: &const CBuf, m: []const u8) -> bool { | |
| 112 | if (self.len() < m.len) return false; | |
| 113 | return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal; | |
| 114 | } | |
| 115 | ||
| 116 | pub fn starts_with_cbuf(self: &const CBuf, other: &const CBuf) -> bool { | |
| 117 | self.starts_with_mem(other.list.items[0...other.len()]) | |
| 118 | } | |
| 119 | ||
| 120 | pub fn starts_with_cstr(self: &const CBuf, s: &const u8) -> bool { | |
| 121 | self.starts_with_mem(s[0...strlen(s)]) | |
| 122 | } | |
| 25 | 123 | } |
| 26 | 124 | |
| 125 | #attribute("test") | |
| 126 | fn test_simple_cbuf() { | |
| 127 | var buf: CBuf = undefined; | |
| 128 | buf.init(&debug.global_allocator); | |
| 129 | assert(buf.len() == 0); | |
| 130 | %%buf.append_cstr(c"hello"); | |
| 131 | %%buf.append_char(' '); | |
| 132 | %%buf.append_mem("world"); | |
| 133 | assert(buf.eql_cstr(c"hello world")); | |
| 134 | assert(buf.eql_mem("hello world")); | |
| 135 | ||
| 136 | var buf2: CBuf = undefined; | |
| 137 | %%buf2.init_from_cbuf(&buf); | |
| 138 | assert(buf.eql_cbuf(&buf2)); | |
| 139 | ||
| 140 | assert(buf.starts_with_mem("hell")); | |
| 141 | assert(buf.starts_with_cstr(c"hell")); | |
| 142 | ||
| 143 | %%buf2.resize(4); | |
| 144 | assert(buf.starts_with_cbuf(&buf2)); | |
| 145 | } |
std/debug.zig+26-1| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | const Allocator = @import("mem.zig").Allocator; | |
| 1 | 2 | const io = @import("io.zig"); |
| 2 | 3 | |
| 3 | 4 | pub fn assert(b: bool) { |
| 4 | 5 | if (!b) unreachable{} |
| 5 | 6 | } |
| 6 | 7 | |
| 7 | pub fn print_stack_trace() { | |
| 8 | pub fn printStackTrace() { | |
| 8 | 9 | var maybe_fp: ?&const u8 = @frame_address(); |
| 9 | 10 | while (true) { |
| 10 | 11 | const fp = maybe_fp ?? break; |
| ... | ... | @@ -14,3 +15,27 @@ pub fn print_stack_trace() { |
| 14 | 15 | maybe_fp = *(&const ?&const u8)(fp); |
| 15 | 16 | } |
| 16 | 17 | } |
| 18 | ||
| 19 | pub var global_allocator = Allocator { | |
| 20 | .alloc_fn = globalAlloc, | |
| 21 | .realloc_fn = globalRealloc, | |
| 22 | .free_fn = globalFree, | |
| 23 | .context = null, | |
| 24 | }; | |
| 25 | ||
| 26 | var some_mem: [10 * 1024]u8 = undefined; | |
| 27 | var some_mem_index: usize = 0; | |
| 28 | ||
| 29 | fn globalAlloc(self: &Allocator, n: usize) -> %[]u8 { | |
| 30 | const result = some_mem[some_mem_index ... some_mem_index + n]; | |
| 31 | some_mem_index += n; | |
| 32 | return result; | |
| 33 | } | |
| 34 | ||
| 35 | fn globalRealloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | |
| 36 | const result = %return globalAlloc(self, new_size); | |
| 37 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); | |
| 38 | return result; | |
| 39 | } | |
| 40 | ||
| 41 | fn globalFree(self: &Allocator, old_mem: []u8) { } |
std/hash_map.zig+7-32| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | const assert = @import("debug.zig").assert; | |
| 1 | const debug = @import("debug.zig"); | |
| 2 | const assert = debug.assert; | |
| 2 | 3 | const math = @import("math.zig"); |
| 3 | 4 | const mem = @import("mem.zig"); |
| 4 | 5 | const Allocator = mem.Allocator; |
| ... | ... | @@ -9,7 +10,7 @@ const debug_u32 = if (want_modification_safety) u32 else void; |
| 9 | 10 | pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32, |
| 10 | 11 | inline eql: fn(a: K, b: K)->bool) -> type |
| 11 | 12 | { |
| 12 | SmallHashMap(K, V, hash, eql, 8) | |
| 13 | SmallHashMap(K, V, hash, eql, @sizeof(usize)) | |
| 13 | 14 | } |
| 14 | 15 | |
| 15 | 16 | pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool, STATIC_SIZE: usize) { |
| ... | ... | @@ -63,9 +64,8 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 63 | 64 | hm.allocator = allocator; |
| 64 | 65 | hm.size = 0; |
| 65 | 66 | hm.max_distance_from_start_index = 0; |
| 66 | for (hm.entries) |*entry| { | |
| 67 | entry.used = false; | |
| 68 | } | |
| 67 | hm.prealloc_entries = zeroes; // sets used to false for all entries | |
| 68 | hm.modification_count = zeroes; | |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | 71 | pub fn deinit(hm: &Self) { |
| ... | ... | @@ -162,7 +162,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 162 | 162 | |
| 163 | 163 | fn increment_modification_count(hm: &Self) { |
| 164 | 164 | if (want_modification_safety) { |
| 165 | hm.modification_count += 1; | |
| 165 | hm.modification_count +%= 1; | |
| 166 | 166 | } |
| 167 | 167 | } |
| 168 | 168 | |
| ... | ... | @@ -231,35 +231,10 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 231 | 231 | } |
| 232 | 232 | } |
| 233 | 233 | |
| 234 | var global_allocator = Allocator { | |
| 235 | .alloc_fn = global_alloc, | |
| 236 | .realloc_fn = global_realloc, | |
| 237 | .free_fn = global_free, | |
| 238 | .context = null, | |
| 239 | }; | |
| 240 | ||
| 241 | var some_mem: [200]u8 = undefined; | |
| 242 | var some_mem_index: usize = 0; | |
| 243 | ||
| 244 | fn global_alloc(self: &Allocator, n: usize) -> %[]u8 { | |
| 245 | const result = some_mem[some_mem_index ... some_mem_index + n]; | |
| 246 | some_mem_index += n; | |
| 247 | return result; | |
| 248 | } | |
| 249 | ||
| 250 | fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | |
| 251 | const result = %return global_alloc(self, new_size); | |
| 252 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); | |
| 253 | return result; | |
| 254 | } | |
| 255 | ||
| 256 | fn global_free(self: &Allocator, old_mem: []u8) { | |
| 257 | } | |
| 258 | ||
| 259 | 234 | #attribute("test") |
| 260 | 235 | fn basic_hash_map_test() { |
| 261 | 236 | var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; |
| 262 | map.init(&global_allocator); | |
| 237 | map.init(&debug.global_allocator); | |
| 263 | 238 | defer map.deinit(); |
| 264 | 239 | |
| 265 | 240 | %%map.put(1, 11); |
std/list.zig+16-33| ... | ... | @@ -1,22 +1,25 @@ |
| 1 | const assert = @import("debug.zig").assert; | |
| 1 | const debug = @import("debug.zig"); | |
| 2 | const assert = debug.assert; | |
| 2 | 3 | const mem = @import("mem.zig"); |
| 3 | 4 | const Allocator = mem.Allocator; |
| 4 | 5 | |
| 5 | 6 | pub fn List(inline T: type) -> type { |
| 6 | SmallList(T, 8) | |
| 7 | SmallList(T, @sizeof(usize)) | |
| 7 | 8 | } |
| 8 | 9 | |
| 10 | // TODO: make sure that setting STATIC_SIZE to 0 codegens to the same code | |
| 11 | // as if this were programmed without STATIC_SIZE at all. | |
| 9 | 12 | pub struct SmallList(T: type, STATIC_SIZE: usize) { |
| 10 | 13 | const Self = SmallList(T, STATIC_SIZE); |
| 11 | 14 | |
| 12 | 15 | items: []T, |
| 13 | length: usize, | |
| 16 | len: usize, | |
| 14 | 17 | prealloc_items: [STATIC_SIZE]T, |
| 15 | 18 | allocator: &Allocator, |
| 16 | 19 | |
| 17 | 20 | pub fn init(l: &Self, allocator: &Allocator) { |
| 18 | 21 | l.items = l.prealloc_items[0...]; |
| 19 | l.length = 0; | |
| 22 | l.len = 0; | |
| 20 | 23 | l.allocator = allocator; |
| 21 | 24 | } |
| 22 | 25 | |
| ... | ... | @@ -27,10 +30,15 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) { |
| 27 | 30 | } |
| 28 | 31 | |
| 29 | 32 | pub fn append(l: &Self, item: T) -> %void { |
| 30 | const new_length = l.length + 1; | |
| 33 | const new_length = l.len + 1; | |
| 31 | 34 | %return l.ensure_capacity(new_length); |
| 32 | l.items[l.length] = item; | |
| 33 | l.length = new_length; | |
| 35 | l.items[l.len] = item; | |
| 36 | l.len = new_length; | |
| 37 | } | |
| 38 | ||
| 39 | pub fn resize(l: &Self, new_len: usize) -> %void { | |
| 40 | %return l.ensure_capacity(new_len); | |
| 41 | l.len = new_len; | |
| 34 | 42 | } |
| 35 | 43 | |
| 36 | 44 | pub fn ensure_capacity(l: &Self, new_capacity: usize) -> %void { |
| ... | ... | @@ -50,35 +58,10 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) { |
| 50 | 58 | } |
| 51 | 59 | } |
| 52 | 60 | |
| 53 | var global_allocator = Allocator { | |
| 54 | .alloc_fn = global_alloc, | |
| 55 | .realloc_fn = global_realloc, | |
| 56 | .free_fn = global_free, | |
| 57 | .context = null, | |
| 58 | }; | |
| 59 | ||
| 60 | var some_mem: [200]u8 = undefined; | |
| 61 | var some_mem_index: usize = 0; | |
| 62 | ||
| 63 | fn global_alloc(self: &Allocator, n: usize) -> %[]u8 { | |
| 64 | const result = some_mem[some_mem_index ... some_mem_index + n]; | |
| 65 | some_mem_index += n; | |
| 66 | return result; | |
| 67 | } | |
| 68 | ||
| 69 | fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | |
| 70 | const result = %return global_alloc(self, new_size); | |
| 71 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); | |
| 72 | return result; | |
| 73 | } | |
| 74 | ||
| 75 | fn global_free(self: &Allocator, old_mem: []u8) { | |
| 76 | } | |
| 77 | ||
| 78 | 61 | #attribute("test") |
| 79 | 62 | fn basic_list_test() { |
| 80 | 63 | var list: List(i32) = undefined; |
| 81 | list.init(&global_allocator); | |
| 64 | list.init(&debug.global_allocator); | |
| 82 | 65 | defer list.deinit(); |
| 83 | 66 | |
| 84 | 67 | {var i: usize = 0; while (i < 10; i += 1) { |
std/math.zig+6| ... | ... | @@ -1,3 +1,9 @@ |
| 1 | pub enum Cmp { | |
| 2 | Equal, | |
| 3 | Greater, | |
| 4 | Less, | |
| 5 | } | |
| 6 | ||
| 1 | 7 | pub fn f64_from_bits(bits: u64) -> f64 { |
| 2 | 8 | *(&f64)(&bits) |
| 3 | 9 | } |
std/mem.zig+16-1| ... | ... | @@ -3,6 +3,8 @@ const math = @import("math.zig"); |
| 3 | 3 | const os = @import("os.zig"); |
| 4 | 4 | const io = @import("io.zig"); |
| 5 | 5 | |
| 6 | pub const Cmp = math.Cmp; | |
| 7 | ||
| 6 | 8 | pub error NoMem; |
| 7 | 9 | |
| 8 | 10 | pub type Context = u8; |
| ... | ... | @@ -40,7 +42,20 @@ pub struct Allocator { |
| 40 | 42 | |
| 41 | 43 | /// Copy all of source into dest at position 0. |
| 42 | 44 | /// dest.len must be >= source.len. |
| 43 | pub fn copy(inline T: type, dest: []T, source: []T) { | |
| 45 | pub fn copy(inline T: type, dest: []T, source: []const T) { | |
| 44 | 46 | assert(dest.len >= source.len); |
| 45 | 47 | @memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len); |
| 46 | 48 | } |
| 49 | ||
| 50 | /// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than, | |
| 51 | /// memory b, respectively. | |
| 52 | pub fn cmp(inline T: type, a: []const T, b: []const T) -> Cmp { | |
| 53 | const n = math.min(usize, a.len, b.len); | |
| 54 | var i: usize = 0; | |
| 55 | while (i < n; i += 1) { | |
| 56 | if (a[i] == b[i]) continue; | |
| 57 | return if (a[i] > b[i]) Cmp.Greater else if (a[i] < b[i]) Cmp.Less else Cmp.Equal; | |
| 58 | } | |
| 59 | ||
| 60 | return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal; | |
| 61 | } |