diff --git a/src/analyze.cpp b/src/analyze.cpp index 1f66f41b17..2a10ebcef1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2688,6 +2688,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i return node->data.field_access_expr.type_struct_field->type_entry; } else if (wrapped_in_fn_call) { BlockContext *container_block_context = get_container_block_context(bare_struct_type); + assert(container_block_context); auto entry = container_block_context->decl_table.maybe_get(field_name); AstNode *fn_decl_node = entry ? entry->value : nullptr; if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { diff --git a/src/codegen.cpp b/src/codegen.cpp index d77eda5bf6..9592fafba5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1243,6 +1243,11 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; + *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry; + if (!type_has_bits(*out_type_entry)) { + return nullptr; + } + LLVMValueRef struct_ptr; if (struct_expr_node->type == NodeTypeSymbol) { VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable; @@ -1272,8 +1277,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou int gen_field_index = node->data.field_access_expr.type_struct_field->gen_index; assert(gen_field_index >= 0); - *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry; - set_debug_source_node(g, node); return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, ""); } @@ -1490,7 +1493,14 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, zig_unreachable(); } } else if (node->type == NodeTypeFieldAccessExpr) { - target_ref = gen_field_ptr(g, node, out_type_entry); + AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; + TypeTableEntry *struct_type = get_expr_type(struct_expr_node); + if (struct_type->id == TypeTableEntryIdNamespace) { + target_ref = gen_field_access_expr(g, node, true); + *out_type_entry = get_expr_type(node); + } else { + target_ref = gen_field_ptr(g, node, out_type_entry); + } } else if (node->type == NodeTypePrefixOpExpr) { assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference); AstNode *target_expr = node->data.prefix_op_expr.primary_expr; diff --git a/std/cstr.zig b/std/cstr.zig index af2c564445..76290f5c74 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -1,3 +1,11 @@ +const List = @import("list.zig").List; +const mem = @import("mem.zig"); +const Allocator = mem.Allocator; +const debug = @import("debug.zig"); +const assert = debug.assert; + +const strlen = len; + // TODO fix https://github.com/andrewrk/zig/issues/140 // and then make this able to run at compile time #static_eval_enable(false) @@ -17,10 +25,121 @@ pub fn cmp(a: &const u8, b: &const u8) -> i32 { } pub fn to_slice_const(str: &const u8) -> []const u8 { - return str[0...len(str)]; + return str[0...strlen(str)]; } pub fn to_slice(str: &u8) -> []u8 { - return str[0...len(str)]; + return str[0...strlen(str)]; } + +/// A buffer that allocates memory and maintains a null byte at the end. +pub struct CBuf { + list: List(u8), + + /// Must deinitialize with deinit. + pub fn init(self: &CBuf, allocator: &Allocator) { + self.list.init(allocator); + // This resize is guaranteed to not have an error because we use a list + // with preallocated memory of at least 1 byte. + %%self.resize(0); + } + + /// Must deinitialize with deinit. + pub fn init_from_mem(self: &CBuf, allocator: &Allocator, m: []const u8) -> %void { + self.init(allocator); + %return self.resize(m.len); + mem.copy(u8, self.list.items, m); + } + + /// Must deinitialize with deinit. + pub fn init_from_cstr(self: &CBuf, allocator: &Allocator, s: &const u8) -> %void { + self.init_from_mem(allocator, s[0...strlen(s)]) + } + + /// Must deinitialize with deinit. + pub fn init_from_cbuf(self: &CBuf, cbuf: &const CBuf) -> %void { + self.init_from_mem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()]) + } + + /// Must deinitialize with deinit. + pub fn init_from_slice(self: &CBuf, other: &const CBuf, start: usize, end: usize) -> %void { + self.init_from_mem(other.list.allocator, other.list.items[start...end]) + } + + pub fn deinit(self: &CBuf) { + self.list.deinit(); + } + + pub fn resize(self: &CBuf, new_len: usize) -> %void { + %return self.list.resize(new_len + 1); + self.list.items[self.len()] = 0; + } + + pub fn len(self: &const CBuf) -> usize { + return self.list.len - 1; + } + + pub fn append_mem(self: &CBuf, m: []const u8) -> %void { + const old_len = self.len(); + %return self.resize(old_len + m.len); + mem.copy(u8, self.list.items[old_len...], m); + } + + pub fn append_cstr(self: &CBuf, s: &const u8) -> %void { + self.append_mem(s[0...strlen(s)]) + } + + pub fn append_char(self: &CBuf, c: u8) -> %void { + %return self.resize(self.len() + 1); + self.list.items[self.len() - 1] = c; + } + + pub fn eql_mem(self: &const CBuf, m: []const u8) -> bool { + if (self.len() != m.len) return false; + return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal; + } + + pub fn eql_cstr(self: &const CBuf, s: &const u8) -> bool { + self.eql_mem(s[0...strlen(s)]) + } + + pub fn eql_cbuf(self: &const CBuf, other: &const CBuf) -> bool { + self.eql_mem(other.list.items[0...other.len()]) + } + + pub fn starts_with_mem(self: &const CBuf, m: []const u8) -> bool { + if (self.len() < m.len) return false; + return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal; + } + + pub fn starts_with_cbuf(self: &const CBuf, other: &const CBuf) -> bool { + self.starts_with_mem(other.list.items[0...other.len()]) + } + + pub fn starts_with_cstr(self: &const CBuf, s: &const u8) -> bool { + self.starts_with_mem(s[0...strlen(s)]) + } +} + +#attribute("test") +fn test_simple_cbuf() { + var buf: CBuf = undefined; + buf.init(&debug.global_allocator); + assert(buf.len() == 0); + %%buf.append_cstr(c"hello"); + %%buf.append_char(' '); + %%buf.append_mem("world"); + assert(buf.eql_cstr(c"hello world")); + assert(buf.eql_mem("hello world")); + + var buf2: CBuf = undefined; + %%buf2.init_from_cbuf(&buf); + assert(buf.eql_cbuf(&buf2)); + + assert(buf.starts_with_mem("hell")); + assert(buf.starts_with_cstr(c"hell")); + + %%buf2.resize(4); + assert(buf.starts_with_cbuf(&buf2)); +} diff --git a/std/debug.zig b/std/debug.zig index 1a289b645e..77213b593e 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -1,10 +1,11 @@ +const Allocator = @import("mem.zig").Allocator; const io = @import("io.zig"); pub fn assert(b: bool) { if (!b) unreachable{} } -pub fn print_stack_trace() { +pub fn printStackTrace() { var maybe_fp: ?&const u8 = @frame_address(); while (true) { const fp = maybe_fp ?? break; @@ -14,3 +15,27 @@ pub fn print_stack_trace() { maybe_fp = *(&const ?&const u8)(fp); } } + +pub var global_allocator = Allocator { + .alloc_fn = globalAlloc, + .realloc_fn = globalRealloc, + .free_fn = globalFree, + .context = null, +}; + +var some_mem: [10 * 1024]u8 = undefined; +var some_mem_index: usize = 0; + +fn globalAlloc(self: &Allocator, n: usize) -> %[]u8 { + const result = some_mem[some_mem_index ... some_mem_index + n]; + some_mem_index += n; + return result; +} + +fn globalRealloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { + const result = %return globalAlloc(self, new_size); + @memcpy(result.ptr, old_mem.ptr, old_mem.len); + return result; +} + +fn globalFree(self: &Allocator, old_mem: []u8) { } diff --git a/std/hash_map.zig b/std/hash_map.zig index 7778d0f268..d0dbe1ea15 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -1,4 +1,5 @@ -const assert = @import("debug.zig").assert; +const debug = @import("debug.zig"); +const assert = debug.assert; const math = @import("math.zig"); const mem = @import("mem.zig"); const Allocator = mem.Allocator; @@ -9,7 +10,7 @@ const debug_u32 = if (want_modification_safety) u32 else void; pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool) -> type { - SmallHashMap(K, V, hash, eql, 8) + SmallHashMap(K, V, hash, eql, @sizeof(usize)) } 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 hm.allocator = allocator; hm.size = 0; hm.max_distance_from_start_index = 0; - for (hm.entries) |*entry| { - entry.used = false; - } + hm.prealloc_entries = zeroes; // sets used to false for all entries + hm.modification_count = zeroes; } 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 fn increment_modification_count(hm: &Self) { if (want_modification_safety) { - hm.modification_count += 1; + hm.modification_count +%= 1; } } @@ -231,35 +231,10 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b } } -var global_allocator = Allocator { - .alloc_fn = global_alloc, - .realloc_fn = global_realloc, - .free_fn = global_free, - .context = null, -}; - -var some_mem: [200]u8 = undefined; -var some_mem_index: usize = 0; - -fn global_alloc(self: &Allocator, n: usize) -> %[]u8 { - const result = some_mem[some_mem_index ... some_mem_index + n]; - some_mem_index += n; - return result; -} - -fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { - const result = %return global_alloc(self, new_size); - @memcpy(result.ptr, old_mem.ptr, old_mem.len); - return result; -} - -fn global_free(self: &Allocator, old_mem: []u8) { -} - #attribute("test") fn basic_hash_map_test() { var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; - map.init(&global_allocator); + map.init(&debug.global_allocator); defer map.deinit(); %%map.put(1, 11); diff --git a/std/list.zig b/std/list.zig index fd2aba2b27..891ecc9166 100644 --- a/std/list.zig +++ b/std/list.zig @@ -1,22 +1,25 @@ -const assert = @import("debug.zig").assert; +const debug = @import("debug.zig"); +const assert = debug.assert; const mem = @import("mem.zig"); const Allocator = mem.Allocator; pub fn List(inline T: type) -> type { - SmallList(T, 8) + SmallList(T, @sizeof(usize)) } +// TODO: make sure that setting STATIC_SIZE to 0 codegens to the same code +// as if this were programmed without STATIC_SIZE at all. pub struct SmallList(T: type, STATIC_SIZE: usize) { const Self = SmallList(T, STATIC_SIZE); items: []T, - length: usize, + len: usize, prealloc_items: [STATIC_SIZE]T, allocator: &Allocator, pub fn init(l: &Self, allocator: &Allocator) { l.items = l.prealloc_items[0...]; - l.length = 0; + l.len = 0; l.allocator = allocator; } @@ -27,10 +30,15 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) { } pub fn append(l: &Self, item: T) -> %void { - const new_length = l.length + 1; + const new_length = l.len + 1; %return l.ensure_capacity(new_length); - l.items[l.length] = item; - l.length = new_length; + l.items[l.len] = item; + l.len = new_length; + } + + pub fn resize(l: &Self, new_len: usize) -> %void { + %return l.ensure_capacity(new_len); + l.len = new_len; } pub fn ensure_capacity(l: &Self, new_capacity: usize) -> %void { @@ -50,35 +58,10 @@ pub struct SmallList(T: type, STATIC_SIZE: usize) { } } -var global_allocator = Allocator { - .alloc_fn = global_alloc, - .realloc_fn = global_realloc, - .free_fn = global_free, - .context = null, -}; - -var some_mem: [200]u8 = undefined; -var some_mem_index: usize = 0; - -fn global_alloc(self: &Allocator, n: usize) -> %[]u8 { - const result = some_mem[some_mem_index ... some_mem_index + n]; - some_mem_index += n; - return result; -} - -fn global_realloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { - const result = %return global_alloc(self, new_size); - @memcpy(result.ptr, old_mem.ptr, old_mem.len); - return result; -} - -fn global_free(self: &Allocator, old_mem: []u8) { -} - #attribute("test") fn basic_list_test() { var list: List(i32) = undefined; - list.init(&global_allocator); + list.init(&debug.global_allocator); defer list.deinit(); {var i: usize = 0; while (i < 10; i += 1) { diff --git a/std/math.zig b/std/math.zig index ee208e23df..0c500d527c 100644 --- a/std/math.zig +++ b/std/math.zig @@ -1,3 +1,9 @@ +pub enum Cmp { + Equal, + Greater, + Less, +} + pub fn f64_from_bits(bits: u64) -> f64 { *(&f64)(&bits) } diff --git a/std/mem.zig b/std/mem.zig index 963a4df0ae..af41804885 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -3,6 +3,8 @@ const math = @import("math.zig"); const os = @import("os.zig"); const io = @import("io.zig"); +pub const Cmp = math.Cmp; + pub error NoMem; pub type Context = u8; @@ -40,7 +42,20 @@ pub struct Allocator { /// Copy all of source into dest at position 0. /// dest.len must be >= source.len. -pub fn copy(inline T: type, dest: []T, source: []T) { +pub fn copy(inline T: type, dest: []T, source: []const T) { assert(dest.len >= source.len); @memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len); } + +/// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than, +/// memory b, respectively. +pub fn cmp(inline T: type, a: []const T, b: []const T) -> Cmp { + const n = math.min(usize, a.len, b.len); + var i: usize = 0; + while (i < n; i += 1) { + if (a[i] == b[i]) continue; + return if (a[i] > b[i]) Cmp.Greater else if (a[i] < b[i]) Cmp.Less else Cmp.Equal; + } + + return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal; +}