Commit a71fbe49cb

Andrew Kelley <superjoe30@gmail.com>
2016-12-19 01:40:26
IR: add FnProto instruction
1 parent f12fbce
src/all_types.hpp
@@ -1448,6 +1448,7 @@ enum IrInstructionId {
     IrInstructionIdUnwrapErrPayload,
     IrInstructionIdErrWrapCode,
     IrInstructionIdErrWrapPayload,
+    IrInstructionIdFnProto,
 };
 
 struct IrInstruction {
@@ -2060,6 +2061,13 @@ struct IrInstructionErrWrapCode {
     LLVMValueRef tmp_ptr;
 };
 
+struct IrInstructionFnProto {
+    IrInstruction base;
+
+    IrInstruction **param_types;
+    IrInstruction *return_type;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/codegen.cpp
@@ -2196,6 +2196,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdIntType:
         case IrInstructionIdMemberCount:
         case IrInstructionIdAlignOf:
+        case IrInstructionIdFnProto:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
src/ir.cpp
@@ -439,6 +439,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapCode *) {
     return IrInstructionIdErrWrapCode;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFnProto *) {
+    return IrInstructionIdFnProto;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1826,6 +1830,22 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
     return new_instruction;
 }
 
+static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
+    IrInstruction **param_types, IrInstruction *return_type)
+{
+    IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
+    instruction->param_types = param_types;
+    instruction->return_type = return_type;
+
+    assert(source_node->type == NodeTypeFnProto);
+    for (size_t i = 0; i < source_node->data.fn_proto.params.length; i += 1) {
+        ir_ref_instruction(param_types[i]);
+    }
+    ir_ref_instruction(return_type);
+
+    return &instruction->base;
+}
+
 static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
     results[ReturnKindUnconditional] = 0;
     results[ReturnKindError] = 0;
@@ -3894,6 +3914,28 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
     return ir_build_const_type(irb, parent_scope, node, container_type);
 }
 
+static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeFnProto);
+
+    size_t param_count = node->data.fn_proto.params.length;
+    IrInstruction **param_types = allocate<IrInstruction*>(param_count);
+
+    for (size_t i = 0; i < param_count; i += 1) {
+        AstNode *param_node = node->data.fn_proto.params.at(i);
+        AstNode *type_node = param_node->data.param_decl.type;
+        IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope);
+        if (type_value == irb->codegen->invalid_instruction)
+            return irb->codegen->invalid_instruction;
+        param_types[i] = type_value;
+    }
+
+    IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
+    if (return_type == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
+    return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type);
+}
+
 static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
         LValPurpose lval)
 {
@@ -3978,11 +4020,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
         case NodeTypeContainerDecl:
             return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
         case NodeTypeFnProto:
+            return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
         case NodeTypeFnDef:
+            zig_panic("TODO IR gen NodeTypeFnDef");
         case NodeTypeFnDecl:
+            zig_panic("TODO IR gen NodeTypeFnDecl");
         case NodeTypeErrorValueDecl:
+            zig_panic("TODO IR gen NodeTypeErrorValueDecl");
         case NodeTypeTypeDecl:
-            zig_panic("TODO more IR gen for node types");
+            zig_panic("TODO IR gen NodeTypeTypeDecl");
         case NodeTypeZeroesLiteral:
             zig_panic("TODO zeroes is deprecated");
     }
@@ -9377,6 +9423,41 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
 
 }
 
+static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) {
+    AstNode *proto_node = instruction->base.source_node;
+    assert(proto_node->type == NodeTypeFnProto);
+
+    FnTypeId fn_type_id = {0};
+    init_fn_type_id(&fn_type_id, proto_node);
+
+    bool depends_on_compile_var = false;
+
+    for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
+        AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
+        assert(param_node->type == NodeTypeParamDecl);
+
+        IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other;
+
+        FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
+        param_info->is_noalias = param_node->data.param_decl.is_noalias;
+        param_info->type = ir_resolve_type(ira, param_type_value);
+        if (param_info->type->id == TypeTableEntryIdInvalid)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        depends_on_compile_var = depends_on_compile_var || param_type_value->static_value.depends_on_compile_var;
+    }
+
+    IrInstruction *return_type_value = instruction->return_type->other;
+    fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
+    if (fn_type_id.return_type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+    depends_on_compile_var = depends_on_compile_var || return_type_value->static_value.depends_on_compile_var;
+
+    ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+    out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id);
+    return ira->codegen->builtin_types.entry_type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -9517,6 +9598,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction);
         case IrInstructionIdUnwrapErrPayload:
             return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction);
+        case IrInstructionIdFnProto:
+            return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction);
         case IrInstructionIdMaybeWrap:
         case IrInstructionIdErrWrapCode:
         case IrInstructionIdErrWrapPayload:
@@ -9677,6 +9760,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdMaybeWrap:
         case IrInstructionIdErrWrapCode:
         case IrInstructionIdErrWrapPayload:
+        case IrInstructionIdFnProto:
             return false;
         case IrInstructionIdAsm:
             {
src/ir_print.cpp
@@ -882,6 +882,17 @@ static void ir_print_err_wrap_payload(IrPrint *irp, IrInstructionErrWrapPayload
     fprintf(irp->f, ")");
 }
 
+static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) {
+    fprintf(irp->f, "fn(");
+    for (size_t i = 0; i < instruction->base.source_node->data.fn_proto.params.length; i += 1) {
+        if (i != 0)
+            fprintf(irp->f, ",");
+        ir_print_other_instruction(irp, instruction->param_types[i]);
+    }
+    fprintf(irp->f, ")->");
+    ir_print_other_instruction(irp, instruction->return_type);
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -1112,6 +1123,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdErrWrapPayload:
             ir_print_err_wrap_payload(irp, (IrInstructionErrWrapPayload *)instruction);
             break;
+        case IrInstructionIdFnProto:
+            ir_print_fn_proto(irp, (IrInstructionFnProto *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
std/debug.zig
@@ -69,7 +69,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
     }
 }
 
-struct ElfStackTrace {
+const ElfStackTrace = struct {
     self_exe_stream: io.InStream,
     elf: elf.Elf,
     debug_info: &elf.SectionHeader,
@@ -77,36 +77,36 @@ struct ElfStackTrace {
     debug_str: &elf.SectionHeader,
     abbrev_table_list: List(AbbrevTableHeader),
     compile_unit_list: List(CompileUnit),
-}
+};
 
-struct CompileUnit {
+const CompileUnit = struct {
     is_64: bool,
     die: &Die,
     pc_start: u64,
     pc_end: u64,
-}
+};
 
 const AbbrevTable = List(AbbrevTableEntry);
 
-struct AbbrevTableHeader {
+const AbbrevTableHeader = struct {
     // offset from .debug_abbrev
     offset: u64,
     table: AbbrevTable,
-}
+};
 
-struct AbbrevTableEntry {
+const AbbrevTableEntry = struct {
     has_children: bool,
     abbrev_code: u64,
     tag_id: u64,
     attrs: List(AbbrevAttr),
-}
+};
 
-struct AbbrevAttr {
+const AbbrevAttr = struct {
     attr_id: u64,
     form_id: u64,
-}
+};
 
-enum FormValue {
+const FormValue = enum {
     Address: u64,
     Block: []u8,
     Const: Constant,
@@ -118,9 +118,9 @@ enum FormValue {
     RefSig8: u64,
     String: []u8,
     StrPtr: u64,
-}
+};
 
-struct Constant {
+const Constant = struct {
     payload: []u8,
     signed: bool,
 
@@ -131,17 +131,17 @@ struct Constant {
             return error.InvalidDebugInfo;
         return mem.sliceAsInt(self.payload, false, u64);
     }
-}
+};
 
-struct Die {
+const Die = struct {
     tag_id: u64,
     has_children: bool,
     attrs: List(Attr),
 
-    struct Attr {
+    const Attr = struct {
         id: u64,
         value: FormValue,
-    }
+    };
 
     fn getAttr(self: &const Die, id: u64) -> ?&const FormValue {
         for (self.attrs.toSlice()) |*attr| {
@@ -175,7 +175,7 @@ struct Die {
             else => error.InvalidDebugInfo,
         }
     }
-}
+};
 
 fn readString(in_stream: &io.InStream) -> %[]u8 {
     var buf = List(u8).init(&global_allocator);
std/elf.zig
@@ -30,14 +30,14 @@ pub const SHT_HIPROC = 0x7fffffff;
 pub const SHT_LOUSER = 0x80000000;
 pub const SHT_HIUSER = 0xffffffff;
 
-pub enum FileType {
+pub const FileType = enum {
     Relocatable,
     Executable,
     Shared,
     Core,
-}
+};
 
-pub enum Arch {
+pub const Arch = enum {
     Sparc,
     x86,
     Mips,
@@ -47,9 +47,9 @@ pub enum Arch {
     IA_64,
     x86_64,
     AArch64,
-}
+};
 
-pub struct SectionHeader {
+pub const SectionHeader = struct {
     name: u32,
     sh_type: u32,
     flags: u64,
@@ -60,9 +60,9 @@ pub struct SectionHeader {
     info: u32,
     addr_align: u64,
     ent_size: u64,
-}
+};
 
-pub struct Elf {
+pub const Elf = struct {
     in_stream: &io.InStream,
     auto_close_stream: bool,
     is_64: bool,
@@ -258,4 +258,4 @@ pub struct Elf {
     pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
         %return elf.in_stream.seekTo(section.offset);
     }
-}
+};
std/hash_map.zig
@@ -13,220 +13,225 @@ pub fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32,
     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) {
-    entries: []Entry,
-    size: usize,
-    max_distance_from_start_index: usize,
-    allocator: &Allocator,
-    // if the hash map is small enough, we use linear search through these
-    // entries instead of allocating memory
-    prealloc_entries: [static_size]Entry,
-    // this is used to detect bugs where a hashtable is edited while an iterator is running.
-    modification_count: debug_u32,
-
-    const Self = this;
-
-    pub struct Entry {
-        used: bool,
-        distance_from_start_index: usize,
-        key: K,
-        value: V,
-    }
-
-    pub struct Iterator {
-        hm: &Self,
-        // how many items have we returned
-        count: usize,
-        // iterator through the entry array
-        index: usize,
-        // used to detect concurrent modification
-        initial_modification_count: debug_u32,
+pub fn SmallHashMap(inline K: type, inline V: type,
+    inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool,
+    inline static_size: usize) -> type
+{
+    struct {
+        entries: []Entry,
+        size: usize,
+        max_distance_from_start_index: usize,
+        allocator: &Allocator,
+        // if the hash map is small enough, we use linear search through these
+        // entries instead of allocating memory
+        prealloc_entries: [static_size]Entry,
+        // this is used to detect bugs where a hashtable is edited while an iterator is running.
+        modification_count: debug_u32,
+
+        const Self = this;
+
+        pub const Entry = struct {
+            used: bool,
+            distance_from_start_index: usize,
+            key: K,
+            value: V,
+        };
 
-        pub fn next(it: &Iterator) -> ?&Entry {
-            if (want_modification_safety) {
-                assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
-            }
-            if (it.count >= it.hm.size) return null;
-            while (it.index < it.hm.entries.len; it.index += 1) {
-                const entry = &it.hm.entries[it.index];
-                if (entry.used) {
-                    it.index += 1;
-                    it.count += 1;
-                    return entry;
+        pub const Iterator = struct {
+            hm: &Self,
+            // how many items have we returned
+            count: usize,
+            // iterator through the entry array
+            index: usize,
+            // used to detect concurrent modification
+            initial_modification_count: debug_u32,
+
+            pub fn next(it: &Iterator) -> ?&Entry {
+                if (want_modification_safety) {
+                    assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
                 }
+                if (it.count >= it.hm.size) return null;
+                while (it.index < it.hm.entries.len; it.index += 1) {
+                    const entry = &it.hm.entries[it.index];
+                    if (entry.used) {
+                        it.index += 1;
+                        it.count += 1;
+                        return entry;
+                    }
+                }
+                @unreachable() // no next item
             }
-            @unreachable() // no next item
-        }
-    }
-    
-    pub fn init(hm: &Self, allocator: &Allocator) {
-        hm.entries = hm.prealloc_entries[0...];
-        hm.allocator = allocator;
-        hm.size = 0;
-        hm.max_distance_from_start_index = 0;
-        hm.prealloc_entries = zeroes; // sets used to false for all entries
-        hm.modification_count = zeroes;
-    }
+        };
 
-    pub fn deinit(hm: &Self) {
-        if (hm.entries.ptr != &hm.prealloc_entries[0]) {
-            hm.allocator.free(Entry, hm.entries);
+        pub fn init(hm: &Self, allocator: &Allocator) {
+            hm.entries = hm.prealloc_entries[0...];
+            hm.allocator = allocator;
+            hm.size = 0;
+            hm.max_distance_from_start_index = 0;
+            hm.prealloc_entries = zeroes; // sets used to false for all entries
+            hm.modification_count = zeroes;
         }
-    }
 
-    pub fn clear(hm: &Self) {
-        for (hm.entries) |*entry| {
-            entry.used = false;
+        pub fn deinit(hm: &Self) {
+            if (hm.entries.ptr != &hm.prealloc_entries[0]) {
+                hm.allocator.free(Entry, hm.entries);
+            }
         }
-        hm.size = 0;
-        hm.max_distance_from_start_index = 0;
-        hm.incrementModificationCount();
-    }
-
-    pub fn put(hm: &Self, key: K, value: V) -> %void {
-        hm.incrementModificationCount();
 
-        const resize = if (hm.entries.ptr == &hm.prealloc_entries[0]) {
-            // preallocated entries table is full
-            hm.size == hm.entries.len
-        } else {
-            // if we get too full (60%), double the capacity
-            hm.size * 5 >= hm.entries.len * 3
-        };
-        if (resize) {
-            const old_entries = hm.entries;
-            %return hm.initCapacity(hm.entries.len * 2);
-            // dump all of the old elements into the new table
-            for (old_entries) |*old_entry| {
-                if (old_entry.used) {
-                    hm.internalPut(old_entry.key, old_entry.value);
-                }
-            }
-            if (old_entries.ptr != &hm.prealloc_entries[0]) {
-                hm.allocator.free(Entry, old_entries);
+        pub fn clear(hm: &Self) {
+            for (hm.entries) |*entry| {
+                entry.used = false;
             }
+            hm.size = 0;
+            hm.max_distance_from_start_index = 0;
+            hm.incrementModificationCount();
         }
 
-        hm.internalPut(key, value);
-    }
-
-    pub fn get(hm: &Self, key: K) -> ?&Entry {
-        return hm.internalGet(key);
-    }
+        pub fn put(hm: &Self, key: K, value: V) -> %void {
+            hm.incrementModificationCount();
 
-    pub fn remove(hm: &Self, key: K) {
-        hm.incrementModificationCount();
-        const start_index = hm.keyToIndex(key);
-        {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
-            const index = (start_index + roll_over) % hm.entries.len;
-            var entry = &hm.entries[index];
+            const resize = if (hm.entries.ptr == &hm.prealloc_entries[0]) {
+                // preallocated entries table is full
+                hm.size == hm.entries.len
+            } else {
+                // if we get too full (60%), double the capacity
+                hm.size * 5 >= hm.entries.len * 3
+            };
+            if (resize) {
+                const old_entries = hm.entries;
+                %return hm.initCapacity(hm.entries.len * 2);
+                // dump all of the old elements into the new table
+                for (old_entries) |*old_entry| {
+                    if (old_entry.used) {
+                        hm.internalPut(old_entry.key, old_entry.value);
+                    }
+                }
+                if (old_entries.ptr != &hm.prealloc_entries[0]) {
+                    hm.allocator.free(Entry, old_entries);
+                }
+            }
 
-            assert(entry.used); // key not found
+            hm.internalPut(key, value);
+        }
 
-            if (!eql(entry.key, key)) continue;
+        pub fn get(hm: &Self, key: K) -> ?&Entry {
+            return hm.internalGet(key);
+        }
 
-            while (roll_over < hm.entries.len; roll_over += 1) {
-                const next_index = (start_index + roll_over + 1) % hm.entries.len;
-                const next_entry = &hm.entries[next_index];
-                if (!next_entry.used || next_entry.distance_from_start_index == 0) {
-                    entry.used = false;
-                    hm.size -= 1;
-                    return;
+        pub fn remove(hm: &Self, key: K) {
+            hm.incrementModificationCount();
+            const start_index = hm.keyToIndex(key);
+            {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
+                const index = (start_index + roll_over) % hm.entries.len;
+                var entry = &hm.entries[index];
+
+                assert(entry.used); // key not found
+
+                if (!eql(entry.key, key)) continue;
+
+                while (roll_over < hm.entries.len; roll_over += 1) {
+                    const next_index = (start_index + roll_over + 1) % hm.entries.len;
+                    const next_entry = &hm.entries[next_index];
+                    if (!next_entry.used || next_entry.distance_from_start_index == 0) {
+                        entry.used = false;
+                        hm.size -= 1;
+                        return;
+                    }
+                    *entry = *next_entry;
+                    entry.distance_from_start_index -= 1;
+                    entry = next_entry;
                 }
-                *entry = *next_entry;
-                entry.distance_from_start_index -= 1;
-                entry = next_entry;
-            }
-            @unreachable() // shifting everything in the table
-        }}
-        @unreachable() // key not found
-    }
+                @unreachable() // shifting everything in the table
+            }}
+            @unreachable() // key not found
+        }
 
-    pub fn entryIterator(hm: &Self) -> Iterator {
-        return Iterator {
-            .hm = hm,
-            .count = 0,
-            .index = 0,
-            .initial_modification_count = hm.modification_count,
-        };
-    }
+        pub fn entryIterator(hm: &Self) -> Iterator {
+            return Iterator {
+                .hm = hm,
+                .count = 0,
+                .index = 0,
+                .initial_modification_count = hm.modification_count,
+            };
+        }
 
-    fn initCapacity(hm: &Self, capacity: usize) -> %void {
-        hm.entries = %return hm.allocator.alloc(Entry, capacity);
-        hm.size = 0;
-        hm.max_distance_from_start_index = 0;
-        for (hm.entries) |*entry| {
-            entry.used = false;
+        fn initCapacity(hm: &Self, capacity: usize) -> %void {
+            hm.entries = %return hm.allocator.alloc(Entry, capacity);
+            hm.size = 0;
+            hm.max_distance_from_start_index = 0;
+            for (hm.entries) |*entry| {
+                entry.used = false;
+            }
         }
-    }
 
-    fn incrementModificationCount(hm: &Self) {
-        if (want_modification_safety) {
-            hm.modification_count +%= 1;
+        fn incrementModificationCount(hm: &Self) {
+            if (want_modification_safety) {
+                hm.modification_count +%= 1;
+            }
         }
-    }
 
-    fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
-        var key = orig_key;
-        var value = orig_value;
-        const start_index = hm.keyToIndex(key);
-        var roll_over: usize = 0;
-        var distance_from_start_index: usize = 0;
-        while (roll_over < hm.entries.len; {roll_over += 1; distance_from_start_index += 1}) {
-            const index = (start_index + roll_over) % hm.entries.len;
-            const entry = &hm.entries[index];
-
-            if (entry.used && !eql(entry.key, key)) {
-                if (entry.distance_from_start_index < distance_from_start_index) {
-                    // robin hood to the rescue
-                    const tmp = *entry;
-                    hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index,
-                        distance_from_start_index);
-                    *entry = Entry {
-                        .used = true,
-                        .distance_from_start_index = distance_from_start_index,
-                        .key = key,
-                        .value = value,
-                    };
-                    key = tmp.key;
-                    value = tmp.value;
-                    distance_from_start_index = tmp.distance_from_start_index;
+        fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
+            var key = orig_key;
+            var value = orig_value;
+            const start_index = hm.keyToIndex(key);
+            var roll_over: usize = 0;
+            var distance_from_start_index: usize = 0;
+            while (roll_over < hm.entries.len; {roll_over += 1; distance_from_start_index += 1}) {
+                const index = (start_index + roll_over) % hm.entries.len;
+                const entry = &hm.entries[index];
+
+                if (entry.used && !eql(entry.key, key)) {
+                    if (entry.distance_from_start_index < distance_from_start_index) {
+                        // robin hood to the rescue
+                        const tmp = *entry;
+                        hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index,
+                            distance_from_start_index);
+                        *entry = Entry {
+                            .used = true,
+                            .distance_from_start_index = distance_from_start_index,
+                            .key = key,
+                            .value = value,
+                        };
+                        key = tmp.key;
+                        value = tmp.value;
+                        distance_from_start_index = tmp.distance_from_start_index;
+                    }
+                    continue;
                 }
-                continue;
-            }
 
-            if (!entry.used) {
-                // adding an entry. otherwise overwriting old value with
-                // same key
-                hm.size += 1;
-            }
+                if (!entry.used) {
+                    // adding an entry. otherwise overwriting old value with
+                    // same key
+                    hm.size += 1;
+                }
 
-            hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
-            *entry = Entry {
-                .used = true,
-                .distance_from_start_index = distance_from_start_index,
-                .key = key,
-                .value = value,
-            };
-            return;
+                hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
+                *entry = Entry {
+                    .used = true,
+                    .distance_from_start_index = distance_from_start_index,
+                    .key = key,
+                    .value = value,
+                };
+                return;
+            }
+            @unreachable() // put into a full map
         }
-        @unreachable() // put into a full map
-    }
 
-    fn internalGet(hm: &Self, key: K) -> ?&Entry {
-        const start_index = hm.keyToIndex(key);
-        {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
-            const index = (start_index + roll_over) % hm.entries.len;
-            const entry = &hm.entries[index];
+        fn internalGet(hm: &Self, key: K) -> ?&Entry {
+            const start_index = hm.keyToIndex(key);
+            {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
+                const index = (start_index + roll_over) % hm.entries.len;
+                const entry = &hm.entries[index];
 
-            if (!entry.used) return null;
-            if (eql(entry.key, key)) return entry;
-        }}
-        return null;
-    }
+                if (!entry.used) return null;
+                if (eql(entry.key, key)) return entry;
+            }}
+            return null;
+        }
 
-    fn keyToIndex(hm: &Self, key: K) -> usize {
-        return usize(hash(key)) % hm.entries.len;
+        fn keyToIndex(hm: &Self, key: K) -> usize {
+            return usize(hash(key)) % hm.entries.len;
+        }
     }
 }
 
std/list.zig
@@ -3,49 +3,51 @@ const assert = debug.assert;
 const mem = @import("mem.zig");
 const Allocator = mem.Allocator;
 
-pub struct List(T: type) {
-    const Self = this;
+pub fn List(inline T: type) -> type{
+    struct {
+        const Self = this;
 
-    items: []T,
-    len: usize,
-    allocator: &Allocator,
+        items: []T,
+        len: usize,
+        allocator: &Allocator,
 
-    pub fn init(allocator: &Allocator) -> Self {
-        Self {
-            .items = zeroes,
-            .len = 0,
-            .allocator = allocator,
+        pub fn init(allocator: &Allocator) -> Self {
+            Self {
+                .items = zeroes,
+                .len = 0,
+                .allocator = allocator,
+            }
         }
-    }
 
-    pub fn deinit(l: &Self) {
-        l.allocator.free(T, l.items);
-    }
+        pub fn deinit(l: &Self) {
+            l.allocator.free(T, l.items);
+        }
 
-    pub fn toSlice(l: &Self) -> []T {
-        return l.items[0...l.len];
-    }
+        pub fn toSlice(l: &Self) -> []T {
+            return l.items[0...l.len];
+        }
 
-    pub fn append(l: &Self, item: T) -> %void {
-        const new_length = l.len + 1;
-        %return l.ensureCapacity(new_length);
-        l.items[l.len] = item;
-        l.len = new_length;
-    }
+        pub fn append(l: &Self, item: T) -> %void {
+            const new_length = l.len + 1;
+            %return l.ensureCapacity(new_length);
+            l.items[l.len] = item;
+            l.len = new_length;
+        }
 
-    pub fn resize(l: &Self, new_len: usize) -> %void {
-        %return l.ensureCapacity(new_len);
-        l.len = new_len;
-    }
+        pub fn resize(l: &Self, new_len: usize) -> %void {
+            %return l.ensureCapacity(new_len);
+            l.len = new_len;
+        }
 
-    pub fn ensureCapacity(l: &Self, new_capacity: usize) -> %void {
-        var better_capacity = l.items.len;
-        if (better_capacity >= new_capacity) return;
-        while (true) {
-            better_capacity += better_capacity / 2 + 8;
-            if (better_capacity >= new_capacity) break;
+        pub fn ensureCapacity(l: &Self, new_capacity: usize) -> %void {
+            var better_capacity = l.items.len;
+            if (better_capacity >= new_capacity) return;
+            while (true) {
+                better_capacity += better_capacity / 2 + 8;
+                if (better_capacity >= new_capacity) break;
+            }
+            l.items = %return l.allocator.realloc(T, l.items, better_capacity);
         }
-        l.items = %return l.allocator.realloc(T, l.items, better_capacity);
     }
 }
 
std/mem.zig
@@ -8,7 +8,7 @@ pub const Cmp = math.Cmp;
 pub error NoMem;
 
 pub type Context = u8;
-pub struct Allocator {
+pub const Allocator = struct {
     allocFn: fn (self: &Allocator, n: usize) -> %[]u8,
     reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8,
     freeFn: fn (self: &Allocator, mem: []u8),
@@ -39,7 +39,7 @@ pub struct Allocator {
     fn free(self: &Allocator, inline T: type, mem: []T) {
         self.freeFn(self, ([]u8)(mem));
     }
-}
+};
 
 /// Copy all of source into dest at position 0.
 /// dest.len must be >= source.len.
std/net.zig
@@ -13,7 +13,7 @@ pub error NoMem;
 pub error NotSocket;
 pub error BadFd;
 
-struct Connection {
+const Connection = struct {
     socket_fd: i32,
 
     pub fn send(c: Connection, buf: []const u8) -> %usize {
@@ -56,14 +56,14 @@ struct Connection {
             else => return error.Unexpected,
         }
     }
-}
+};
 
-struct Address {
+const Address = struct {
     family: u16,
     scope_id: u32,
     addr: [16]u8,
     sort_key: i32,
-}
+};
 
 pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
     if (hostname.len == 0) {
std/rand.zig
@@ -18,7 +18,7 @@ pub const MT19937_64 = MersenneTwister(
     43, 6364136223846793005);
 
 /// Use `init` to initialize this state.
-pub struct Rand {
+pub const Rand = struct {
     const Rng = if (@sizeOf(usize) >= 8) MT19937_64 else MT19937_32;
 
     rng: Rng,
@@ -91,65 +91,67 @@ pub struct Rand {
         };
         return T(r.rangeUnsigned(int_type, 0, precision)) / T(precision);
     }
-}
-
-struct MersenneTwister(
-    int: type, n: usize, m: usize, r: int,
-    a: int,
-    u: int, d: int,
-    s: int, b: int,
-    t: int, c: int,
-    l: int, f: int)
+};
+
+fn MersenneTwister(
+    inline int: type, inline n: usize, inline m: usize, inline r: int,
+    inline a: int,
+    inline u: int, inline d: int,
+    inline s: int, inline b: int,
+    inline t: int, inline c: int,
+    inline l: int, inline f: int) -> type
 {
-    const Self = this;
+    struct {
+        const Self = this;
 
-    array: [n]int,
-    index: usize,
+        array: [n]int,
+        index: usize,
 
-    pub fn init(mt: &Self, seed: int) {
-        mt.index = n;
+        pub fn init(mt: &Self, seed: int) {
+            mt.index = n;
 
-        var prev_value = seed;
-        mt.array[0] = prev_value;
-        {var i: usize = 1; while (i < n; i += 1) {
-            prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
-            mt.array[i] = prev_value;
-        }};
-    }
+            var prev_value = seed;
+            mt.array[0] = prev_value;
+            {var i: usize = 1; while (i < n; i += 1) {
+                prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
+                mt.array[i] = prev_value;
+            }};
+        }
 
-    pub fn get(mt: &Self) -> int {
-        const mag01 = []int{0, a};
-        const LM: int = (1 << r) - 1;
-        const UM = ~LM;
+        pub fn get(mt: &Self) -> int {
+            const mag01 = []int{0, a};
+            const LM: int = (1 << r) - 1;
+            const UM = ~LM;
 
-        if (mt.index >= n) {
-            var i: usize = 0;
+            if (mt.index >= n) {
+                var i: usize = 0;
 
-            while (i < n - m; i += 1) {
-                const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
-                mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[x & 0x1];
-            }
+                while (i < n - m; i += 1) {
+                    const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
+                    mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[x & 0x1];
+                }
 
-            while (i < n - 1; i += 1) {
-                const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
-                mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[x & 0x1];
+                while (i < n - 1; i += 1) {
+                    const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
+                    mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[x & 0x1];
 
-            }
-            const x = (mt.array[i] & UM) | (mt.array[0] & LM);
-            mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[x & 0x1];
+                }
+                const x = (mt.array[i] & UM) | (mt.array[0] & LM);
+                mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[x & 0x1];
 
-            mt.index = 0;
-        }
+                mt.index = 0;
+            }
 
-        var x = mt.array[mt.index];
-        mt.index += 1;
+            var x = mt.array[mt.index];
+            mt.index += 1;
 
-        x ^= ((x >> u) & d);
-        x ^= ((x <<% s) & b);
-        x ^= ((x <<% t) & c);
-        x ^= (x >> l);
+            x ^= ((x >> u) & d);
+            x ^= ((x <<% s) & b);
+            x ^= ((x <<% t) & c);
+            x ^= (x >> l);
 
-        return x;
+            return x;
+        }
     }
 }
 
CMakeLists.txt
@@ -52,11 +52,11 @@ set(ZIG_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link.cpp"
     "${CMAKE_SOURCE_DIR}/src/main.cpp"
     "${CMAKE_SOURCE_DIR}/src/os.cpp"
-    "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
     "${CMAKE_SOURCE_DIR}/src/parser.cpp"
     "${CMAKE_SOURCE_DIR}/src/target.cpp"
     "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
     "${CMAKE_SOURCE_DIR}/src/util.cpp"
+    "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
     "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
 )